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         boolean mRunning = true;
    121         LlcpServerSocket mServerSocket;
    122 
    123         @Override
    124         public void run() {
    125             while (mRunning) {
    126                 if (DBG) Log.d(TAG, "about create LLCP service socket");
    127                 try {
    128                     mServerSocket = mService.createLlcpServerSocket(mSap, SERVICE_NAME,
    129                             MIU, 1, 1024);
    130                     if (mServerSocket == null) {
    131                         if (DBG) Log.d(TAG, "failed to create LLCP service socket");
    132                         return;
    133                     }
    134                     if (DBG) Log.d(TAG, "created LLCP service socket");
    135                     while (mRunning) {
    136                         if (DBG) Log.d(TAG, "about to accept");
    137                         LlcpSocket communicationSocket = mServerSocket.accept();
    138                         if (DBG) Log.d(TAG, "accept returned " + communicationSocket);
    139                         if (communicationSocket != null) {
    140                             new ConnectionThread(communicationSocket).start();
    141                         }
    142                     }
    143                     if (DBG) Log.d(TAG, "stop running");
    144                 } catch (LlcpException e) {
    145                     Log.e(TAG, "llcp error", e);
    146                 } catch (IOException e) {
    147                     Log.e(TAG, "IO error", e);
    148                 } finally {
    149                     if (mServerSocket != null) {
    150                         if (DBG) Log.d(TAG, "about to close");
    151                         try {
    152                             mServerSocket.close();
    153                         } catch (IOException e) {
    154                             // ignore
    155                         }
    156                         mServerSocket = null;
    157                     }
    158                 }
    159             }
    160         }
    161 
    162         public void shutdown() {
    163             mRunning = false;
    164             if (mServerSocket != null) {
    165                 try {
    166                     mServerSocket.close();
    167                 } catch (IOException e) {
    168                     // ignore
    169                 }
    170                 mServerSocket = null;
    171             }
    172         }
    173     }
    174 
    175     public void start() {
    176         synchronized (this) {
    177             if (DBG) Log.d(TAG, "start, thread = " + mServerThread);
    178             if (mServerThread == null) {
    179                 if (DBG) Log.d(TAG, "starting new server thread");
    180                 mServerThread = new ServerThread();
    181                 mServerThread.start();
    182             }
    183         }
    184     }
    185 
    186     public void stop() {
    187         synchronized (this) {
    188             if (DBG) Log.d(TAG, "stop, thread = " + mServerThread);
    189             if (mServerThread != null) {
    190                 if (DBG) Log.d(TAG, "shuting down server thread");
    191                 mServerThread.shutdown();
    192                 mServerThread = null;
    193             }
    194         }
    195     }
    196 }
    197