1 /* 2 * Copyright (C) 2017 NXP Semiconductors 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 package com.android.nfc.sneptest; 17 18 import java.io.IOException; 19 20 import android.content.Context; 21 import android.nfc.NdefMessage; 22 import android.util.Log; 23 24 import com.android.nfc.DtaServiceConnector; 25 import com.android.nfc.DeviceHost.LlcpServerSocket; 26 import com.android.nfc.DeviceHost.LlcpSocket; 27 import com.android.nfc.LlcpException; 28 import com.android.nfc.NfcService; 29 import com.android.nfc.snep.SnepException; 30 import com.android.nfc.snep.SnepMessage; 31 import com.android.nfc.snep.SnepMessenger; 32 33 public final class ExtDtaSnepServer { 34 private static final String TAG = "ExtDtaSnepServer"; 35 private static final boolean DBG = true; 36 public static final int DEFAULT_PORT = 5; 37 public static final String EXTENDED_SNEP_DTA_SERVICE_NAME = "urn:nfc:sn:sneptest"; 38 public static final String DEFAULT_SERVICE_NAME = EXTENDED_SNEP_DTA_SERVICE_NAME; 39 40 final Callback mExtDtaSnepServerCallback; 41 final String mDtaServiceName; 42 final int mDtaServiceSap; 43 final int mDtaFragmentLength; 44 final int mDtaMiu; 45 final int mDtaRwSize; 46 public static Context mContext; 47 public static int mTestCaseId; 48 49 /** Protected by 'this', null when stopped, non-null when running */ 50 ServerThread mServerThread = null; 51 boolean mServerRunning = false; 52 static DtaServiceConnector dtaServiceConnector; 53 54 public interface Callback { doPut(NdefMessage msg)55 public SnepMessage doPut(NdefMessage msg); doGet(int acceptableLength, NdefMessage msg)56 public SnepMessage doGet(int acceptableLength, NdefMessage msg); 57 } 58 59 // for NFC Forum SNEP DTA ExtDtaSnepServer(String serviceName, int serviceSap, int miu, int rwSize, Callback callback,Context mContext,int testCaseId)60 public ExtDtaSnepServer(String serviceName, int serviceSap, int miu, int rwSize, 61 Callback callback,Context mContext,int testCaseId) { 62 mExtDtaSnepServerCallback = callback; 63 mDtaServiceName = serviceName; 64 mDtaServiceSap = serviceSap; 65 mDtaFragmentLength = -1; // to get remote MIU 66 mDtaMiu = miu; 67 mDtaRwSize = rwSize; 68 mTestCaseId = testCaseId; 69 dtaServiceConnector=new DtaServiceConnector(mContext); 70 dtaServiceConnector.bindService(); 71 } 72 73 /** Connection class, used to handle incoming connections */ 74 private class ConnectionThread extends Thread { 75 private final LlcpSocket mSock; 76 private final SnepMessenger mMessager; 77 ConnectionThread(LlcpSocket socket, int fragmentLength)78 ConnectionThread(LlcpSocket socket, int fragmentLength) { 79 super(TAG); 80 mSock = socket; 81 mMessager = new SnepMessenger(false, socket, fragmentLength); 82 } 83 84 @Override run()85 public void run() { 86 if (DBG) Log.d(TAG, "starting connection thread"); 87 try { 88 boolean running; 89 synchronized (ExtDtaSnepServer.this) { 90 running = mServerRunning; 91 } 92 93 while (running) { 94 if (!handleRequest(mMessager, mExtDtaSnepServerCallback)) 95 break; 96 97 synchronized (ExtDtaSnepServer.this) { 98 running = mServerRunning; 99 } 100 } 101 } catch (IOException e) { 102 if (DBG) Log.e(TAG, "Closing from IOException"); 103 } finally { 104 try { 105 if (DBG) Log.d(TAG, "about to close"); 106 mSock.close(); 107 } catch (IOException e) {} 108 } 109 if (DBG) Log.d(TAG, "finished connection thread"); 110 } 111 } 112 handleRequest(SnepMessenger messenger, Callback callback)113 static boolean handleRequest(SnepMessenger messenger, Callback callback) throws IOException { 114 SnepMessage request; 115 try { 116 request = messenger.getMessage(); 117 } catch (SnepException e) { 118 if (DBG) Log.w(TAG, "Bad snep message", e); 119 try { 120 messenger.sendMessage(SnepMessage.getMessage( 121 SnepMessage.RESPONSE_BAD_REQUEST)); 122 } catch (IOException e2) {} 123 return false; 124 } 125 126 if (((request.getVersion() & 0xF0) >> 4) != SnepMessage.VERSION_MAJOR) { 127 messenger.sendMessage(SnepMessage.getMessage( 128 SnepMessage.RESPONSE_UNSUPPORTED_VERSION)); 129 } else if ((request.getLength() > SnepMessage.MAL_IUT) || request.getLength() == SnepMessage.MAL) { 130 if (DBG) Log.d(TAG, "Bad requested length"); 131 messenger.sendMessage(SnepMessage.getMessage(SnepMessage.RESPONSE_REJECT)); 132 } else if (request.getField() == SnepMessage.REQUEST_GET) { 133 if (DBG) Log.d(TAG, "getting message " + request.toString()); 134 messenger.sendMessage(callback.doGet(request.getAcceptableLength(), request.getNdefMessage())); 135 if (request.getNdefMessage() != null) 136 dtaServiceConnector.sendMessage(request.getNdefMessage().toString()); 137 } else if (request.getField() == SnepMessage.REQUEST_PUT) { 138 if (DBG) Log.d(TAG, "putting message " + request.toString()); 139 messenger.sendMessage(callback.doPut(request.getNdefMessage())); 140 if (request.getNdefMessage() != null) 141 dtaServiceConnector.sendMessage(request.getNdefMessage().toString()); 142 } else { 143 if (DBG) Log.d(TAG, "Unknown request (" + request.getField() +")"); 144 messenger.sendMessage(SnepMessage.getMessage(SnepMessage.RESPONSE_BAD_REQUEST)); 145 } 146 return true; 147 } 148 149 /** Server class, used to listen for incoming connection request */ 150 class ServerThread extends Thread { 151 private boolean mThreadRunning = true; 152 LlcpServerSocket mServerSocket; 153 154 @Override run()155 public void run() { 156 boolean threadRunning; 157 synchronized (ExtDtaSnepServer.this) { 158 threadRunning = mThreadRunning; 159 } 160 161 while (threadRunning) { 162 if (DBG) Log.d(TAG, "about create LLCP service socket"); 163 try { 164 synchronized (ExtDtaSnepServer.this) { 165 mServerSocket = NfcService.getInstance().createLlcpServerSocket(mDtaServiceSap, 166 mDtaServiceName, mDtaMiu, mDtaRwSize, 1024); 167 } 168 if (mServerSocket == null) { 169 if (DBG) Log.d(TAG, "failed to create LLCP service socket"); 170 return; 171 } 172 if (DBG) Log.d(TAG, "created LLCP service socket"); 173 synchronized (ExtDtaSnepServer.this) { 174 threadRunning = mThreadRunning; 175 } 176 177 while (threadRunning) { 178 LlcpServerSocket serverSocket; 179 synchronized (ExtDtaSnepServer.this) { 180 serverSocket = mServerSocket; 181 } 182 183 if (serverSocket == null) { 184 if (DBG) Log.d(TAG, "Server socket shut down."); 185 return; 186 } 187 if (DBG) Log.d(TAG, "about to accept"); 188 LlcpSocket communicationSocket = serverSocket.accept(); 189 if (DBG) Log.d(TAG, "accept returned " + communicationSocket); 190 if (communicationSocket != null) { 191 int miu = communicationSocket.getRemoteMiu(); 192 int fragmentLength = (mDtaFragmentLength == -1) ? miu : Math.min(miu, mDtaFragmentLength); 193 new ConnectionThread(communicationSocket, fragmentLength).start(); 194 } 195 196 synchronized (ExtDtaSnepServer.this) { 197 threadRunning = mThreadRunning; 198 } 199 } 200 if (DBG) Log.d(TAG, "stop running"); 201 } catch (LlcpException e) { 202 Log.e(TAG, "llcp error", e); 203 } catch (IOException e) { 204 Log.e(TAG, "IO error", e); 205 } finally { 206 synchronized (ExtDtaSnepServer.this) { 207 if (mServerSocket != null) { 208 if (DBG) Log.d(TAG, "about to close"); 209 try { 210 mServerSocket.close(); 211 } catch (IOException e) {} 212 mServerSocket = null; 213 } 214 } 215 } 216 217 synchronized (ExtDtaSnepServer.this) { 218 threadRunning = mThreadRunning; 219 } 220 } 221 } 222 shutdown()223 public void shutdown() { 224 synchronized (ExtDtaSnepServer.this) { 225 mThreadRunning = false; 226 if (mServerSocket != null) { 227 try { 228 mServerSocket.close(); 229 } catch (IOException e) {} 230 mServerSocket = null; 231 } 232 } 233 } 234 } 235 start()236 public void start() { 237 synchronized (ExtDtaSnepServer.this) { 238 if (DBG) Log.d(TAG, "start, thread = " + mServerThread); 239 if (mServerThread == null) { 240 if (DBG) Log.d(TAG, "starting new server thread"); 241 mServerThread = new ServerThread(); 242 mServerThread.start(); 243 mServerRunning = true; 244 } 245 } 246 } 247 stop()248 public void stop() { 249 synchronized (ExtDtaSnepServer.this) { 250 if (DBG) Log.d(TAG, "stop, thread = " + mServerThread); 251 if (mServerThread != null) { 252 if (DBG) Log.d(TAG, "shuting down server thread"); 253 mServerThread.shutdown(); 254 mServerThread = null; 255 mServerRunning = false; 256 } 257 } 258 } 259 } 260