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 */ 16package com.android.nfc.sneptest; 17 18import java.io.IOException; 19 20import android.content.Context; 21import android.nfc.NdefMessage; 22import android.util.Log; 23 24import com.android.nfc.DtaServiceConnector; 25import com.android.nfc.DeviceHost.LlcpServerSocket; 26import com.android.nfc.DeviceHost.LlcpSocket; 27import com.android.nfc.LlcpException; 28import com.android.nfc.NfcService; 29import com.android.nfc.snep.SnepException; 30import com.android.nfc.snep.SnepMessage; 31import com.android.nfc.snep.SnepMessenger; 32 33public 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 { 55 public SnepMessage doPut(NdefMessage msg); 56 public SnepMessage doGet(int acceptableLength, NdefMessage msg); 57 } 58 59 // for NFC Forum SNEP DTA 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 78 ConnectionThread(LlcpSocket socket, int fragmentLength) { 79 super(TAG); 80 mSock = socket; 81 mMessager = new SnepMessenger(false, socket, fragmentLength); 82 } 83 84 @Override 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 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 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 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 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 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