1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.nfc.snep; 18 19 import com.android.nfc.DeviceHost.LlcpServerSocket; 20 import com.android.nfc.DeviceHost.LlcpSocket; 21 import com.android.nfc.LlcpException; 22 import com.android.nfc.NfcService; 23 24 import android.nfc.NdefMessage; 25 import android.nfc.NfcAdapter; 26 import android.util.Log; 27 28 import java.io.IOException; 29 30 /** 31 * A simple server that accepts NDEF messages pushed to it over an LLCP connection. Those messages 32 * are typically set on the client side by using {@link NfcAdapter#enableForegroundNdefPush}. 33 */ 34 public final class SnepServer { 35 private static final String TAG = "SnepServer"; 36 private static final boolean DBG = false; 37 private static final int DEFAULT_MIU = 248; 38 private static final int DEFAULT_RW_SIZE = 1; 39 40 public static final int DEFAULT_PORT = 4; 41 42 public static final String DEFAULT_SERVICE_NAME = "urn:nfc:sn:snep"; 43 44 final Callback mCallback; 45 final String mServiceName; 46 final int mServiceSap; 47 final int mFragmentLength; 48 final int mMiu; 49 final int mRwSize; 50 51 /** Protected by 'this', null when stopped, non-null when running */ 52 ServerThread mServerThread = null; 53 boolean mServerRunning = false; 54 55 public interface Callback { doPut(NdefMessage msg)56 public SnepMessage doPut(NdefMessage msg); doGet(int acceptableLength, NdefMessage msg)57 public SnepMessage doGet(int acceptableLength, NdefMessage msg); 58 } 59 SnepServer(Callback callback)60 public SnepServer(Callback callback) { 61 mCallback = callback; 62 mServiceName = DEFAULT_SERVICE_NAME; 63 mServiceSap = DEFAULT_PORT; 64 mFragmentLength = -1; 65 mMiu = DEFAULT_MIU; 66 mRwSize = DEFAULT_RW_SIZE; 67 } 68 SnepServer(String serviceName, int serviceSap, Callback callback)69 public SnepServer(String serviceName, int serviceSap, Callback callback) { 70 mCallback = callback; 71 mServiceName = serviceName; 72 mServiceSap = serviceSap; 73 mFragmentLength = -1; 74 mMiu = DEFAULT_MIU; 75 mRwSize = DEFAULT_RW_SIZE; 76 } 77 SnepServer(Callback callback, int miu, int rwSize)78 public SnepServer(Callback callback, int miu, int rwSize) { 79 mCallback = callback; 80 mServiceName = DEFAULT_SERVICE_NAME; 81 mServiceSap = DEFAULT_PORT; 82 mFragmentLength = -1; 83 mMiu = miu; 84 mRwSize = rwSize; 85 } 86 SnepServer(String serviceName, int serviceSap, int fragmentLength, Callback callback)87 SnepServer(String serviceName, int serviceSap, int fragmentLength, Callback callback) { 88 mCallback = callback; 89 mServiceName = serviceName; 90 mServiceSap = serviceSap; 91 mFragmentLength = fragmentLength; 92 mMiu = DEFAULT_MIU; 93 mRwSize = DEFAULT_RW_SIZE; 94 } 95 96 /** Connection class, used to handle incoming connections */ 97 private class ConnectionThread extends Thread { 98 private final LlcpSocket mSock; 99 private final SnepMessenger mMessager; 100 ConnectionThread(LlcpSocket socket, int fragmentLength)101 ConnectionThread(LlcpSocket socket, int fragmentLength) { 102 super(TAG); 103 mSock = socket; 104 mMessager = new SnepMessenger(false, socket, fragmentLength); 105 } 106 107 @Override run()108 public void run() { 109 if (DBG) Log.d(TAG, "starting connection thread"); 110 try { 111 boolean running; 112 synchronized (SnepServer.this) { 113 running = mServerRunning; 114 } 115 116 while (running) { 117 if (!handleRequest(mMessager, mCallback)) { 118 break; 119 } 120 121 synchronized (SnepServer.this) { 122 running = mServerRunning; 123 } 124 } 125 } catch (IOException e) { 126 if (DBG) Log.e(TAG, "Closing from IOException"); 127 } finally { 128 try { 129 if (DBG) Log.d(TAG, "about to close"); 130 mSock.close(); 131 } catch (IOException e) { 132 // ignore 133 } 134 } 135 136 if (DBG) Log.d(TAG, "finished connection thread"); 137 } 138 } 139 handleRequest(SnepMessenger messenger, Callback callback)140 static boolean handleRequest(SnepMessenger messenger, Callback callback) throws IOException { 141 SnepMessage request; 142 try { 143 request = messenger.getMessage(); 144 } catch (SnepException e) { 145 if (DBG) Log.w(TAG, "Bad snep message", e); 146 try { 147 messenger.sendMessage(SnepMessage.getMessage( 148 SnepMessage.RESPONSE_BAD_REQUEST)); 149 } catch (IOException e2) { 150 // Ignore 151 } 152 return false; 153 } 154 155 if (((request.getVersion() & 0xF0) >> 4) != SnepMessage.VERSION_MAJOR) { 156 messenger.sendMessage(SnepMessage.getMessage( 157 SnepMessage.RESPONSE_UNSUPPORTED_VERSION)); 158 } else if (NfcService.sIsDtaMode && ((request.getLength() > SnepMessage.MAL_IUT) || 159 request.getLength() == SnepMessage.MAL)) { 160 if (DBG) Log.d(TAG, "Bad requested length"); 161 messenger.sendMessage(SnepMessage.getMessage(SnepMessage.RESPONSE_REJECT)); 162 } else if (request.getField() == SnepMessage.REQUEST_GET) { 163 messenger.sendMessage(callback.doGet(request.getAcceptableLength(), 164 request.getNdefMessage())); 165 } else if (request.getField() == SnepMessage.REQUEST_PUT) { 166 if (DBG) Log.d(TAG, "putting message " + request.toString()); 167 messenger.sendMessage(callback.doPut(request.getNdefMessage())); 168 } else { 169 if (DBG) Log.d(TAG, "Unknown request (" + request.getField() +")"); 170 messenger.sendMessage(SnepMessage.getMessage( 171 SnepMessage.RESPONSE_BAD_REQUEST)); 172 } 173 return true; 174 } 175 176 /** Server class, used to listen for incoming connection request */ 177 class ServerThread extends Thread { 178 private boolean mThreadRunning = true; 179 LlcpServerSocket mServerSocket; 180 181 @Override run()182 public void run() { 183 boolean threadRunning; 184 synchronized (SnepServer.this) { 185 threadRunning = mThreadRunning; 186 } 187 188 while (threadRunning) { 189 if (DBG) Log.d(TAG, "about create LLCP service socket"); 190 try { 191 synchronized (SnepServer.this) { 192 mServerSocket = NfcService.getInstance().createLlcpServerSocket(mServiceSap, 193 mServiceName, mMiu, mRwSize, 1024); 194 } 195 if (mServerSocket == null) { 196 if (DBG) Log.d(TAG, "failed to create LLCP service socket"); 197 return; 198 } 199 if (DBG) Log.d(TAG, "created LLCP service socket"); 200 synchronized (SnepServer.this) { 201 threadRunning = mThreadRunning; 202 } 203 204 while (threadRunning) { 205 LlcpServerSocket serverSocket; 206 synchronized (SnepServer.this) { 207 serverSocket = mServerSocket; 208 } 209 210 if (serverSocket == null) { 211 if (DBG) Log.d(TAG, "Server socket shut down."); 212 return; 213 } 214 if (DBG) Log.d(TAG, "about to accept"); 215 LlcpSocket communicationSocket = serverSocket.accept(); 216 if (DBG) Log.d(TAG, "accept returned " + communicationSocket); 217 if (communicationSocket != null) { 218 int fragmentLength = (mFragmentLength == -1) ? 219 mMiu : Math.min(mMiu, mFragmentLength); 220 new ConnectionThread(communicationSocket, fragmentLength).start(); 221 } 222 223 synchronized (SnepServer.this) { 224 threadRunning = mThreadRunning; 225 } 226 } 227 if (DBG) Log.d(TAG, "stop running"); 228 } catch (LlcpException e) { 229 Log.e(TAG, "llcp error", e); 230 } catch (IOException e) { 231 if (DBG) Log.d(TAG, "IO error"); 232 } finally { 233 synchronized (SnepServer.this) { 234 if (mServerSocket != null) { 235 if (DBG) Log.d(TAG, "about to close"); 236 try { 237 mServerSocket.close(); 238 } catch (IOException e) { 239 // ignore 240 } 241 mServerSocket = null; 242 } 243 } 244 } 245 246 synchronized (SnepServer.this) { 247 threadRunning = mThreadRunning; 248 } 249 } 250 } 251 shutdown()252 public void shutdown() { 253 synchronized (SnepServer.this) { 254 mThreadRunning = false; 255 if (mServerSocket != null) { 256 try { 257 mServerSocket.close(); 258 } catch (IOException e) { 259 // ignore 260 } 261 mServerSocket = null; 262 } 263 } 264 } 265 } 266 start()267 public void start() { 268 synchronized (SnepServer.this) { 269 if (DBG) Log.d(TAG, "start, thread = " + mServerThread); 270 if (mServerThread == null) { 271 if (DBG) Log.d(TAG, "starting new server thread"); 272 mServerThread = new ServerThread(); 273 mServerThread.start(); 274 mServerRunning = true; 275 } 276 } 277 } 278 stop()279 public void stop() { 280 synchronized (SnepServer.this) { 281 if (DBG) Log.d(TAG, "stop, thread = " + mServerThread); 282 if (mServerThread != null) { 283 if (DBG) Log.d(TAG, "shuting down server thread"); 284 mServerThread.shutdown(); 285 mServerThread = null; 286 mServerRunning = false; 287 } 288 } 289 } 290 } 291