blob: 33b640630aa9bfbc323f5dffd61438d92469a7d5 [file] [log] [blame]
nxpandroid64fd68c2015-09-23 16:45:15 +05301/*
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 */
Bhupendra Pawar9f0c8382018-01-11 17:05:27 +053016
nxpandroid64fd68c2015-09-23 16:45:15 +053017package com.android.nfc.snep;
18
19import com.android.nfc.DeviceHost.LlcpServerSocket;
20import com.android.nfc.DeviceHost.LlcpSocket;
21import com.android.nfc.LlcpException;
22import com.android.nfc.NfcService;
23
24import android.nfc.NdefMessage;
25import android.nfc.NfcAdapter;
26import android.util.Log;
27
28import 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 */
34public 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 {
56 public SnepMessage doPut(NdefMessage msg);
57 public SnepMessage doGet(int acceptableLength, NdefMessage msg);
58 }
59
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
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
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
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
101 ConnectionThread(LlcpSocket socket, int fragmentLength) {
102 super(TAG);
103 mSock = socket;
104 mMessager = new SnepMessenger(false, socket, fragmentLength);
105 }
106
107 @Override
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
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));
Bhupendra Pawar9f0c8382018-01-11 17:05:27 +0530158 } else if (NfcService.sIsDtaMode && ((request.getLength() > SnepMessage.MAL_IUT) ||
159 request.getLength() == SnepMessage.MAL)) {
nxpandroid64fd68c2015-09-23 16:45:15 +0530160 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
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) {
Suhas Sureshf1a5edb2018-04-27 11:18:14 +0530231 if (DBG) Log.d(TAG, "IO error");
nxpandroid64fd68c2015-09-23 16:45:15 +0530232 } 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
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
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
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}