Home | History | Annotate | Download | only in handover
      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 com.android.nfc.DeviceHost.LlcpServerSocket;
     19 import com.android.nfc.DeviceHost.LlcpSocket;
     20 import com.android.nfc.LlcpException;
     21 import com.android.nfc.NfcService;
     22 import com.android.nfc.beam.BeamManager;
     23 import com.android.nfc.beam.BeamReceiveService;
     24 import com.android.nfc.beam.BeamTransferRecord;
     25 
     26 import android.bluetooth.BluetoothDevice;
     27 import android.content.Context;
     28 import android.content.Intent;
     29 import android.nfc.FormatException;
     30 import android.nfc.NdefMessage;
     31 import android.os.UserHandle;
     32 import android.util.Log;
     33 
     34 import java.io.ByteArrayOutputStream;
     35 import java.io.IOException;
     36 import java.util.Arrays;
     37 
     38 public 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) {
    132                     if (DBG) Log.d(TAG, "IO error");
    133                 } 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 }
    263 
    264