blob: 82b37dec5a4768fda297e6d04a39ede0b9a382da [file] [log] [blame]
nxpandroid64fd68c2015-09-23 16:45:15 +05301/*
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
17package com.android.nfc.ndefpush;
18
19import com.android.nfc.DeviceHost.LlcpServerSocket;
20import com.android.nfc.DeviceHost.LlcpSocket;
21import com.android.nfc.LlcpException;
22import com.android.nfc.NfcService;
23
24import android.nfc.FormatException;
25import android.nfc.NdefMessage;
26import android.nfc.NfcAdapter;
27import android.util.Log;
28
29import java.io.ByteArrayOutputStream;
30import 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 */
36public 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) {
Suhas Sureshf1a5edb2018-04-27 11:18:14 +0530168 if (DBG) Log.d(TAG, "IO error");
nxpandroid64fd68c2015-09-23 16:45:15 +0530169 } 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}