blob: 4a731622956516fc5a71367f71931ea6ffd80e89 [file] [log] [blame]
nxpandroid64fd68c2015-09-23 16:45:15 +05301/*
2 * Copyright (C) 2012 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 */
16package com.android.nfc.handover;
17
18import com.android.nfc.DeviceHost.LlcpServerSocket;
19import com.android.nfc.DeviceHost.LlcpSocket;
20import com.android.nfc.LlcpException;
21import com.android.nfc.NfcService;
22import com.android.nfc.beam.BeamManager;
23import com.android.nfc.beam.BeamReceiveService;
24import com.android.nfc.beam.BeamTransferRecord;
25
26import android.bluetooth.BluetoothDevice;
27import android.content.Context;
28import android.content.Intent;
29import android.nfc.FormatException;
30import android.nfc.NdefMessage;
31import android.os.UserHandle;
32import android.util.Log;
33
34import java.io.ByteArrayOutputStream;
35import java.io.IOException;
36import java.util.Arrays;
37
38public final class HandoverServer {
39 static final String HANDOVER_SERVICE_NAME = "urn:nfc:sn:handover";
40 static final String TAG = "HandoverServer";
41 static final Boolean DBG = false;
42
43 static final int MIU = 128;
44
45 final HandoverDataParser mHandoverDataParser;
46 final int mSap;
47 final Callback mCallback;
48 private final Context mContext;
49
50 ServerThread mServerThread = null;
51 boolean mServerRunning = false;
52
53 public interface Callback {
54 void onHandoverRequestReceived();
55 void onHandoverBusy();
56 }
57
58 public HandoverServer(Context context, int sap, HandoverDataParser manager, Callback callback) {
59 mContext = context;
60 mSap = sap;
61 mHandoverDataParser = manager;
62 mCallback = callback;
63 }
64
65 public synchronized void start() {
66 if (mServerThread == null) {
67 mServerThread = new ServerThread();
68 mServerThread.start();
69 mServerRunning = true;
70 }
71 }
72
73 public synchronized void stop() {
74 if (mServerThread != null) {
75 mServerThread.shutdown();
76 mServerThread = null;
77 mServerRunning = false;
78 }
79 }
80
81 private class ServerThread extends Thread {
82 private boolean mThreadRunning = true;
83 LlcpServerSocket mServerSocket;
84
85 @Override
86 public void run() {
87 boolean threadRunning;
88 synchronized (HandoverServer.this) {
89 threadRunning = mThreadRunning;
90 }
91
92 while (threadRunning) {
93 try {
94 synchronized (HandoverServer.this) {
95 mServerSocket = NfcService.getInstance().createLlcpServerSocket(mSap,
96 HANDOVER_SERVICE_NAME, MIU, 1, 1024);
97 }
98 if (mServerSocket == null) {
99 if (DBG) Log.d(TAG, "failed to create LLCP service socket");
100 return;
101 }
102 if (DBG) Log.d(TAG, "created LLCP service socket");
103 synchronized (HandoverServer.this) {
104 threadRunning = mThreadRunning;
105 }
106
107 while (threadRunning) {
108 LlcpServerSocket serverSocket;
109 synchronized (HandoverServer.this) {
110 serverSocket = mServerSocket;
111 }
112
113 if (serverSocket == null) {
114 if (DBG) Log.d(TAG, "Server socket shut down.");
115 return;
116 }
117 if (DBG) Log.d(TAG, "about to accept");
118 LlcpSocket communicationSocket = serverSocket.accept();
119 if (DBG) Log.d(TAG, "accept returned " + communicationSocket);
120 if (communicationSocket != null) {
121 new ConnectionThread(communicationSocket).start();
122 }
123
124 synchronized (HandoverServer.this) {
125 threadRunning = mThreadRunning;
126 }
127 }
128 if (DBG) Log.d(TAG, "stop running");
129 } catch (LlcpException e) {
130 Log.e(TAG, "llcp error", e);
131 } catch (IOException e) {
Suhas Sureshf1a5edb2018-04-27 11:18:14 +0530132 if (DBG) Log.d(TAG, "IO error");
nxpandroid64fd68c2015-09-23 16:45:15 +0530133 } finally {
134 synchronized (HandoverServer.this) {
135 if (mServerSocket != null) {
136 if (DBG) Log.d(TAG, "about to close");
137 try {
138 mServerSocket.close();
139 } catch (IOException e) {
140 // ignore
141 }
142 mServerSocket = null;
143 }
144 }
145 }
146
147 synchronized (HandoverServer.this) {
148 threadRunning = mThreadRunning;
149 }
150 }
151 }
152
153 public void shutdown() {
154 synchronized (HandoverServer.this) {
155 mThreadRunning = false;
156 if (mServerSocket != null) {
157 try {
158 mServerSocket.close();
159 } catch (IOException e) {
160 // ignore
161 }
162 mServerSocket = null;
163 }
164 }
165 }
166 }
167
168 private class ConnectionThread extends Thread {
169 private final LlcpSocket mSock;
170
171 ConnectionThread(LlcpSocket socket) {
172 super(TAG);
173 mSock = socket;
174 }
175
176 @Override
177 public void run() {
178 if (DBG) Log.d(TAG, "starting connection thread");
179 ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
180
181 try {
182 boolean running;
183 synchronized (HandoverServer.this) {
184 running = mServerRunning;
185 }
186
187 byte[] partial = new byte[mSock.getLocalMiu()];
188
189 NdefMessage handoverRequestMsg = null;
190 while (running) {
191 int size = mSock.receive(partial);
192 if (size < 0) {
193 break;
194 }
195 byteStream.write(partial, 0, size);
196 // 1) Try to parse a handover request message from bytes received so far
197 try {
198 handoverRequestMsg = new NdefMessage(byteStream.toByteArray());
199 } catch (FormatException e) {
200 // Ignore, and try to fetch more bytes
201 }
202
203 if (handoverRequestMsg != null) {
204 BeamManager beamManager = BeamManager.getInstance();
205
206 if (beamManager.isBeamInProgress()) {
207 mCallback.onHandoverBusy();
208 break;
209 }
210
211 // 2) convert to handover response
212 HandoverDataParser.IncomingHandoverData handoverData
213 = mHandoverDataParser.getIncomingHandoverData(handoverRequestMsg);
214 if (handoverData == null) {
215 Log.e(TAG, "Failed to create handover response");
216 break;
217 }
218
219 // 3) send handover response
220 int offset = 0;
221 byte[] buffer = handoverData.handoverSelect.toByteArray();
222 int remoteMiu = mSock.getRemoteMiu();
223 while (offset < buffer.length) {
224 int length = Math.min(buffer.length - offset, remoteMiu);
225 byte[] tmpBuffer = Arrays.copyOfRange(buffer, offset, offset+length);
226 mSock.send(tmpBuffer);
227 offset += length;
228 }
229 // We're done
230 mCallback.onHandoverRequestReceived();
231 if (!beamManager.startBeamReceive(mContext, handoverData.handoverData)) {
232 mCallback.onHandoverBusy();
233 break;
234 }
235 // We can process another handover transfer
236 byteStream = new ByteArrayOutputStream();
237 }
238
239 synchronized (HandoverServer.this) {
240 running = mServerRunning;
241 }
242 }
243
244 } catch (IOException e) {
245 if (DBG) Log.d(TAG, "IOException");
246 } finally {
247 try {
248 if (DBG) Log.d(TAG, "about to close");
249 mSock.close();
250 } catch (IOException e) {
251 // ignore
252 }
253 try {
254 byteStream.close();
255 } catch (IOException e) {
256 // ignore
257 }
258 }
259 if (DBG) Log.d(TAG, "finished connection thread");
260 }
261 }
262}