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