Home | History | Annotate | Download | only in ndefpush
      1 /*
      2  * Copyright (C) 2010 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 
     17 package com.android.nfc.ndefpush;
     18 
     19 import com.android.nfc.DeviceHost.LlcpServerSocket;
     20 import com.android.nfc.DeviceHost.LlcpSocket;
     21 import com.android.nfc.LlcpException;
     22 import com.android.nfc.NfcService;
     23 
     24 import android.nfc.FormatException;
     25 import android.nfc.NdefMessage;
     26 import android.nfc.NfcAdapter;
     27 import android.util.Log;
     28 
     29 import java.io.ByteArrayOutputStream;
     30 import java.io.IOException;
     31 
     32 /**
     33  * A simple server that accepts NDEF messages pushed to it over an LLCP connection. Those messages
     34  * are typically set on the client side by using {@link NfcAdapter#enableForegroundNdefPush}.
     35  */
     36 public class NdefPushServer {
     37     private static final String TAG = "NdefPushServer";
     38     private static final boolean DBG = true;
     39 
     40     private static final int MIU = 248;
     41 
     42     int mSap;
     43 
     44     static final String SERVICE_NAME = "com.android.npp";
     45 
     46     NfcService mService = NfcService.getInstance();
     47 
     48     final Callback mCallback;
     49 
     50     /** Protected by 'this', null when stopped, non-null when running */
     51     ServerThread mServerThread = null;
     52 
     53     public interface Callback {
     54         void onMessageReceived(NdefMessage msg);
     55     }
     56 
     57     public NdefPushServer(final int sap, Callback callback) {
     58         mSap = sap;
     59         mCallback = callback;
     60     }
     61 
     62     /** Connection class, used to handle incoming connections */
     63     private class ConnectionThread extends Thread {
     64         private LlcpSocket mSock;
     65 
     66         ConnectionThread(LlcpSocket sock) {
     67             super(TAG);
     68             mSock = sock;
     69         }
     70 
     71         @Override
     72         public void run() {
     73             if (DBG) Log.d(TAG, "starting connection thread");
     74             try {
     75                 ByteArrayOutputStream buffer = new ByteArrayOutputStream(1024);
     76                 byte[] partial = new byte[1024];
     77                 int size;
     78                 boolean connectionBroken = false;
     79 
     80                 // Get raw data from remote server
     81                 while(!connectionBroken) {
     82                     try {
     83                         size = mSock.receive(partial);
     84                         if (DBG) Log.d(TAG, "read " + size + " bytes");
     85                         if (size < 0) {
     86                             connectionBroken = true;
     87                             break;
     88                         } else {
     89                             buffer.write(partial, 0, size);
     90                         }
     91                     } catch (IOException e) {
     92                         // Connection broken
     93                         connectionBroken = true;
     94                         if (DBG) Log.d(TAG, "connection broken by IOException", e);
     95                     }
     96                 }
     97 
     98                 // Build NDEF message set from the stream
     99                 NdefPushProtocol msg = new NdefPushProtocol(buffer.toByteArray());
    100                 if (DBG) Log.d(TAG, "got message " + msg.toString());
    101 
    102                 // Send the intent for the fake tag
    103                 mCallback.onMessageReceived(msg.getImmediate());
    104             } catch (FormatException e) {
    105                 Log.e(TAG, "badly formatted NDEF message, ignoring", e);
    106             } finally {
    107                 try {
    108                     if (DBG) Log.d(TAG, "about to close");
    109                     mSock.close();
    110                 } catch (IOException e) {
    111                     // ignore
    112                 }
    113             }
    114             if (DBG) Log.d(TAG, "finished connection thread");
    115         }
    116     }
    117 
    118     /** Server class, used to listen for incoming connection request */
    119     class ServerThread extends Thread {
    120         // Variables below synchronized on NdefPushServer.this
    121         boolean mRunning = true;
    122         LlcpServerSocket mServerSocket;
    123 
    124         @Override
    125         public void run() {
    126             boolean threadRunning;
    127             synchronized (NdefPushServer.this) {
    128                 threadRunning = mRunning;
    129             }
    130             while (threadRunning) {
    131                 if (DBG) Log.d(TAG, "about create LLCP service socket");
    132                 try {
    133                     synchronized (NdefPushServer.this) {
    134                         mServerSocket = mService.createLlcpServerSocket(mSap, SERVICE_NAME,
    135                                 MIU, 1, 1024);
    136                     }
    137                     if (mServerSocket == null) {
    138                         if (DBG) Log.d(TAG, "failed to create LLCP service socket");
    139                         return;
    140                     }
    141                     if (DBG) Log.d(TAG, "created LLCP service socket");
    142                     synchronized (NdefPushServer.this) {
    143                         threadRunning = mRunning;
    144                     }
    145 
    146                     while (threadRunning) {
    147                         LlcpServerSocket serverSocket;
    148                         synchronized (NdefPushServer.this) {
    149                             serverSocket = mServerSocket;
    150                         }
    151                         if (serverSocket == null) return;
    152 
    153                         if (DBG) Log.d(TAG, "about to accept");
    154                         LlcpSocket communicationSocket = serverSocket.accept();
    155                         if (DBG) Log.d(TAG, "accept returned " + communicationSocket);
    156                         if (communicationSocket != null) {
    157                             new ConnectionThread(communicationSocket).start();
    158                         }
    159 
    160                         synchronized (NdefPushServer.this) {
    161                             threadRunning = mRunning;
    162                         }
    163                     }
    164                     if (DBG) Log.d(TAG, "stop running");
    165                 } catch (LlcpException e) {
    166                     Log.e(TAG, "llcp error", e);
    167                 } catch (IOException e) {
    168                     Log.e(TAG, "IO error", e);
    169                 } finally {
    170                     synchronized (NdefPushServer.this) {
    171                         if (mServerSocket != null) {
    172                             if (DBG) Log.d(TAG, "about to close");
    173                             try {
    174                                 mServerSocket.close();
    175                             } catch (IOException e) {
    176                                 // ignore
    177                             }
    178                             mServerSocket = null;
    179                         }
    180                     }
    181                 }
    182 
    183                 synchronized (NdefPushServer.this) {
    184                     threadRunning = mRunning;
    185                 }
    186             }
    187         }
    188 
    189         public void shutdown() {
    190             synchronized (NdefPushServer.this) {
    191                 mRunning = false;
    192                 if (mServerSocket != null) {
    193                     try {
    194                         mServerSocket.close();
    195                     } catch (IOException e) {
    196                         // ignore
    197                     }
    198                     mServerSocket = null;
    199                 }
    200             }
    201         }
    202     }
    203 
    204     public void start() {
    205         synchronized (this) {
    206             if (DBG) Log.d(TAG, "start, thread = " + mServerThread);
    207             if (mServerThread == null) {
    208                 if (DBG) Log.d(TAG, "starting new server thread");
    209                 mServerThread = new ServerThread();
    210                 mServerThread.start();
    211             }
    212         }
    213     }
    214 
    215     public void stop() {
    216         synchronized (this) {
    217             if (DBG) Log.d(TAG, "stop, thread = " + mServerThread);
    218             if (mServerThread != null) {
    219                 if (DBG) Log.d(TAG, "shuting down server thread");
    220                 mServerThread.shutdown();
    221                 mServerThread = null;
    222             }
    223         }
    224     }
    225 }
    226