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