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