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 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