157d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton/* 257d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton * Copyright (C) 2010 The Android Open Source Project 357d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton * 457d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton * Licensed under the Apache License, Version 2.0 (the "License"); 557d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton * you may not use this file except in compliance with the License. 657d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton * You may obtain a copy of the License at 757d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton * 857d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton * http://www.apache.org/licenses/LICENSE-2.0 957d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton * 1057d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton * Unless required by applicable law or agreed to in writing, software 1157d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton * distributed under the License is distributed on an "AS IS" BASIS, 1257d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1357d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton * See the License for the specific language governing permissions and 1457d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton * limitations under the License. 1557d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton */ 1657d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton 17ca1a86ecb8edce740a232c3439355e8d5b706e7aJeff Hamiltonpackage com.android.nfc.ndefpush; 1857d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton 194a61d3b45e81c0070538f94747a70a49c78f12faJeff Hamiltonimport com.android.nfc.DeviceHost.LlcpServerSocket; 204a61d3b45e81c0070538f94747a70a49c78f12faJeff Hamiltonimport com.android.nfc.DeviceHost.LlcpSocket; 214a61d3b45e81c0070538f94747a70a49c78f12faJeff Hamiltonimport com.android.nfc.LlcpException; 2257d376f1ee1a3939977b95759525585abb9601fbJeff Hamiltonimport com.android.nfc.NfcService; 2357d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton 2457d376f1ee1a3939977b95759525585abb9601fbJeff Hamiltonimport android.nfc.FormatException; 252429c20351e7a660b1fcb0b6c3d2558c05f6f5deMartijn Coenenimport android.nfc.NdefMessage; 2657d376f1ee1a3939977b95759525585abb9601fbJeff Hamiltonimport android.nfc.NfcAdapter; 2757d376f1ee1a3939977b95759525585abb9601fbJeff Hamiltonimport android.util.Log; 2857d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton 2957d376f1ee1a3939977b95759525585abb9601fbJeff Hamiltonimport java.io.ByteArrayOutputStream; 3057d376f1ee1a3939977b95759525585abb9601fbJeff Hamiltonimport java.io.IOException; 3157d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton 3257d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton/** 3357d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton * A simple server that accepts NDEF messages pushed to it over an LLCP connection. Those messages 344a61d3b45e81c0070538f94747a70a49c78f12faJeff Hamilton * are typically set on the client side by using {@link NfcAdapter#enableForegroundNdefPush}. 3557d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton */ 36ca1a86ecb8edce740a232c3439355e8d5b706e7aJeff Hamiltonpublic class NdefPushServer { 37ca1a86ecb8edce740a232c3439355e8d5b706e7aJeff Hamilton private static final String TAG = "NdefPushServer"; 3857d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton private static final boolean DBG = true; 393fb30ae5bf51d9ffe6271a345d55905dade8040dJeff Hamilton 4073a6ef05557f78d20e291ef34aec013770644da0Sylvain Fonteneau private static final int MIU = 248; 414a61d3b45e81c0070538f94747a70a49c78f12faJeff Hamilton 424a61d3b45e81c0070538f94747a70a49c78f12faJeff Hamilton int mSap; 4357d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton 44ca1a86ecb8edce740a232c3439355e8d5b706e7aJeff Hamilton static final String SERVICE_NAME = "com.android.npp"; 4557d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton 4657d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton NfcService mService = NfcService.getInstance(); 473fb30ae5bf51d9ffe6271a345d55905dade8040dJeff Hamilton 482429c20351e7a660b1fcb0b6c3d2558c05f6f5deMartijn Coenen final Callback mCallback; 492429c20351e7a660b1fcb0b6c3d2558c05f6f5deMartijn Coenen 5057d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton /** Protected by 'this', null when stopped, non-null when running */ 5157d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton ServerThread mServerThread = null; 5257d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton 532429c20351e7a660b1fcb0b6c3d2558c05f6f5deMartijn Coenen public interface Callback { 542429c20351e7a660b1fcb0b6c3d2558c05f6f5deMartijn Coenen void onMessageReceived(NdefMessage msg); 552429c20351e7a660b1fcb0b6c3d2558c05f6f5deMartijn Coenen } 562429c20351e7a660b1fcb0b6c3d2558c05f6f5deMartijn Coenen 572429c20351e7a660b1fcb0b6c3d2558c05f6f5deMartijn Coenen public NdefPushServer(final int sap, Callback callback) { 5832cdff503e206b6753c6cf020c545163a43bcaa1Martijn Coenen mSap = sap; 592429c20351e7a660b1fcb0b6c3d2558c05f6f5deMartijn Coenen mCallback = callback; 6032cdff503e206b6753c6cf020c545163a43bcaa1Martijn Coenen } 6132cdff503e206b6753c6cf020c545163a43bcaa1Martijn Coenen 6257d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton /** Connection class, used to handle incoming connections */ 6357d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton private class ConnectionThread extends Thread { 6457d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton private LlcpSocket mSock; 6557d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton 6657d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton ConnectionThread(LlcpSocket sock) { 67ca1a86ecb8edce740a232c3439355e8d5b706e7aJeff Hamilton super(TAG); 6857d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton mSock = sock; 6957d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton } 7057d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton 7157d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton @Override 7257d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton public void run() { 733fb30ae5bf51d9ffe6271a345d55905dade8040dJeff Hamilton if (DBG) Log.d(TAG, "starting connection thread"); 7457d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton try { 7557d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton ByteArrayOutputStream buffer = new ByteArrayOutputStream(1024); 7657d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton byte[] partial = new byte[1024]; 7757d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton int size; 7857d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton boolean connectionBroken = false; 7957d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton 8057d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton // Get raw data from remote server 8157d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton while(!connectionBroken) { 8257d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton try { 8357d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton size = mSock.receive(partial); 843fb30ae5bf51d9ffe6271a345d55905dade8040dJeff Hamilton if (DBG) Log.d(TAG, "read " + size + " bytes"); 8557d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton if (size < 0) { 8657d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton connectionBroken = true; 8757d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton break; 8857d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton } else { 8957d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton buffer.write(partial, 0, size); 9057d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton } 9157d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton } catch (IOException e) { 9257d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton // Connection broken 9357d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton connectionBroken = true; 943fb30ae5bf51d9ffe6271a345d55905dade8040dJeff Hamilton if (DBG) Log.d(TAG, "connection broken by IOException", e); 9557d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton } 9657d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton } 9757d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton 98ca1a86ecb8edce740a232c3439355e8d5b706e7aJeff Hamilton // Build NDEF message set from the stream 99ca1a86ecb8edce740a232c3439355e8d5b706e7aJeff Hamilton NdefPushProtocol msg = new NdefPushProtocol(buffer.toByteArray()); 1003fb30ae5bf51d9ffe6271a345d55905dade8040dJeff Hamilton if (DBG) Log.d(TAG, "got message " + msg.toString()); 10157d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton 10257d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton // Send the intent for the fake tag 1032429c20351e7a660b1fcb0b6c3d2558c05f6f5deMartijn Coenen mCallback.onMessageReceived(msg.getImmediate()); 10457d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton } catch (FormatException e) { 1053fb30ae5bf51d9ffe6271a345d55905dade8040dJeff Hamilton Log.e(TAG, "badly formatted NDEF message, ignoring", e); 106b40b1d6ff2b05f8629d0cf62e987658f6c1e4731Jeff Hamilton } finally { 107b40b1d6ff2b05f8629d0cf62e987658f6c1e4731Jeff Hamilton try { 1083fb30ae5bf51d9ffe6271a345d55905dade8040dJeff Hamilton if (DBG) Log.d(TAG, "about to close"); 109b40b1d6ff2b05f8629d0cf62e987658f6c1e4731Jeff Hamilton mSock.close(); 110b40b1d6ff2b05f8629d0cf62e987658f6c1e4731Jeff Hamilton } catch (IOException e) { 111b40b1d6ff2b05f8629d0cf62e987658f6c1e4731Jeff Hamilton // ignore 112b40b1d6ff2b05f8629d0cf62e987658f6c1e4731Jeff Hamilton } 11357d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton } 1143fb30ae5bf51d9ffe6271a345d55905dade8040dJeff Hamilton if (DBG) Log.d(TAG, "finished connection thread"); 11557d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton } 116c9342fef947c49e247495b83f94f16d43cd3562cmike wakerly } 11757d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton 11857d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton /** Server class, used to listen for incoming connection request */ 11957d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton class ServerThread extends Thread { 120525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project // Variables below synchronized on NdefPushServer.this 12157d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton boolean mRunning = true; 1224a61d3b45e81c0070538f94747a70a49c78f12faJeff Hamilton LlcpServerSocket mServerSocket; 12357d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton 12457d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton @Override 12557d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton public void run() { 126525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project boolean threadRunning; 127525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project synchronized (NdefPushServer.this) { 128525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project threadRunning = mRunning; 129525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project } 130525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project while (threadRunning) { 1313fb30ae5bf51d9ffe6271a345d55905dade8040dJeff Hamilton if (DBG) Log.d(TAG, "about create LLCP service socket"); 13289c67765e6df5925a7799176a18b17171cbf9e03Sylvain Fonteneau try { 133525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project synchronized (NdefPushServer.this) { 134525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project mServerSocket = mService.createLlcpServerSocket(mSap, SERVICE_NAME, 135525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project MIU, 1, 1024); 136525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project } 1374a61d3b45e81c0070538f94747a70a49c78f12faJeff Hamilton if (mServerSocket == null) { 1384a61d3b45e81c0070538f94747a70a49c78f12faJeff Hamilton if (DBG) Log.d(TAG, "failed to create LLCP service socket"); 1394a61d3b45e81c0070538f94747a70a49c78f12faJeff Hamilton return; 1404a61d3b45e81c0070538f94747a70a49c78f12faJeff Hamilton } 1414a61d3b45e81c0070538f94747a70a49c78f12faJeff Hamilton if (DBG) Log.d(TAG, "created LLCP service socket"); 142525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project synchronized (NdefPushServer.this) { 143525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project threadRunning = mRunning; 144525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project } 145525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project 146525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project while (threadRunning) { 147525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project LlcpServerSocket serverSocket; 148525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project synchronized (NdefPushServer.this) { 149525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project serverSocket = mServerSocket; 150525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project } 151525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project if (serverSocket == null) return; 152525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project 1533fb30ae5bf51d9ffe6271a345d55905dade8040dJeff Hamilton if (DBG) Log.d(TAG, "about to accept"); 154525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project LlcpSocket communicationSocket = serverSocket.accept(); 1553fb30ae5bf51d9ffe6271a345d55905dade8040dJeff Hamilton if (DBG) Log.d(TAG, "accept returned " + communicationSocket); 15689c67765e6df5925a7799176a18b17171cbf9e03Sylvain Fonteneau if (communicationSocket != null) { 15789c67765e6df5925a7799176a18b17171cbf9e03Sylvain Fonteneau new ConnectionThread(communicationSocket).start(); 15889c67765e6df5925a7799176a18b17171cbf9e03Sylvain Fonteneau } 159525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project 160525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project synchronized (NdefPushServer.this) { 161525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project threadRunning = mRunning; 162525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project } 16389c67765e6df5925a7799176a18b17171cbf9e03Sylvain Fonteneau } 1643fb30ae5bf51d9ffe6271a345d55905dade8040dJeff Hamilton if (DBG) Log.d(TAG, "stop running"); 16589c67765e6df5925a7799176a18b17171cbf9e03Sylvain Fonteneau } catch (LlcpException e) { 1663fb30ae5bf51d9ffe6271a345d55905dade8040dJeff Hamilton Log.e(TAG, "llcp error", e); 16789c67765e6df5925a7799176a18b17171cbf9e03Sylvain Fonteneau } catch (IOException e) { 1683fb30ae5bf51d9ffe6271a345d55905dade8040dJeff Hamilton Log.e(TAG, "IO error", e); 16989c67765e6df5925a7799176a18b17171cbf9e03Sylvain Fonteneau } finally { 170525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project synchronized (NdefPushServer.this) { 171525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project if (mServerSocket != null) { 172525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project if (DBG) Log.d(TAG, "about to close"); 173525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project try { 174525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project mServerSocket.close(); 175525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project } catch (IOException e) { 176525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project // ignore 177525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project } 178525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project mServerSocket = null; 1794a61d3b45e81c0070538f94747a70a49c78f12faJeff Hamilton } 18089c67765e6df5925a7799176a18b17171cbf9e03Sylvain Fonteneau } 181e9848c7a0ab1162e7355683f0cc12e2455d7c939Sylvain Fonteneau } 182525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project 183525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project synchronized (NdefPushServer.this) { 184525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project threadRunning = mRunning; 185525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project } 18657d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton } 18757d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton } 18857d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton 18957d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton public void shutdown() { 190525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project synchronized (NdefPushServer.this) { 191525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project mRunning = false; 192525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project if (mServerSocket != null) { 193525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project try { 194525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project mServerSocket.close(); 195525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project } catch (IOException e) { 196525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project // ignore 197525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project } 198525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project mServerSocket = null; 1994a61d3b45e81c0070538f94747a70a49c78f12faJeff Hamilton } 20057d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton } 20157d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton } 202c9342fef947c49e247495b83f94f16d43cd3562cmike wakerly } 20357d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton 20457d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton public void start() { 20557d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton synchronized (this) { 20657d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton if (DBG) Log.d(TAG, "start, thread = " + mServerThread); 20757d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton if (mServerThread == null) { 20857d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton if (DBG) Log.d(TAG, "starting new server thread"); 20957d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton mServerThread = new ServerThread(); 21057d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton mServerThread.start(); 21157d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton } 21257d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton } 21357d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton } 21457d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton 21557d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton public void stop() { 21657d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton synchronized (this) { 21757d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton if (DBG) Log.d(TAG, "stop, thread = " + mServerThread); 21857d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton if (mServerThread != null) { 21957d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton if (DBG) Log.d(TAG, "shuting down server thread"); 22057d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton mServerThread.shutdown(); 22157d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton mServerThread = null; 22257d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton } 22357d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton } 22457d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton } 22557d376f1ee1a3939977b95759525585abb9601fbJeff Hamilton} 226