summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeff Hamilton <jham@android.com>2010-12-10 16:11:35 -0800
committerJeff Hamilton <jham@android.com>2011-01-18 09:02:14 -0800
commitca1a86ecb8edce740a232c3439355e8d5b706e7a (patch)
treec240a32ac92119a738c7747fbe4c01eebce786a7
parenteb488e0b323ec2d4fe03528e2b9f3a73e66ae17d (diff)
downloadpackages_apps_nfc-ca1a86ecb8edce740a232c3439355e8d5b706e7a.zip
packages_apps_nfc-ca1a86ecb8edce740a232c3439355e8d5b706e7a.tar.gz
packages_apps_nfc-ca1a86ecb8edce740a232c3439355e8d5b706e7a.tar.bz2
NDEF Push Protocol implementation.
The protocol allows pushing NDEF messages over LLCP in order to simulate a card read wihtout having to use card emulation. This also allows for 2 way transfer of meesages. The protocol allows for a single immediate message, to be dispatched upon arrival as if it were read from a tag, and any number of deferred dispatch messages. The handling of deferred dispatch messages is up to the receiver and not implemented in this patch. Change-Id: Ib99e4fc01532cc741debab370a417f94669b62ac
-rwxr-xr-xsrc/com/android/nfc/NfcService.java43
-rwxr-xr-xsrc/com/android/nfc/ndefpush/NdefPushClient.java (renamed from src/com/android/nfc/mytag/MyTagClient.java)83
-rw-r--r--src/com/android/nfc/ndefpush/NdefPushProtocol.java167
-rwxr-xr-xsrc/com/android/nfc/ndefpush/NdefPushServer.java (renamed from src/com/android/nfc/mytag/MyTagServer.java)21
4 files changed, 258 insertions, 56 deletions
diff --git a/src/com/android/nfc/NfcService.java b/src/com/android/nfc/NfcService.java
index ff888ea..098a0aa 100755
--- a/src/com/android/nfc/NfcService.java
+++ b/src/com/android/nfc/NfcService.java
@@ -18,14 +18,14 @@ package com.android.nfc;
import com.android.internal.nfc.LlcpServiceSocket;
import com.android.internal.nfc.LlcpSocket;
-import com.android.nfc.mytag.MyTagClient;
-import com.android.nfc.mytag.MyTagServer;
+import com.android.nfc.ndefpush.NdefPushClient;
+import com.android.nfc.ndefpush.NdefPushServer;
import android.app.Activity;
import android.app.Application;
import android.app.PendingIntent;
-import android.app.PendingIntent.CanceledException;
import android.app.StatusBarManager;
+import android.app.PendingIntent.CanceledException;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -252,8 +252,8 @@ public class NfcService extends Application {
private SharedPreferences mPrefs;
private SharedPreferences.Editor mPrefsEditor;
private PowerManager.WakeLock mWakeLock;
- private MyTagServer mMyTagServer;
- private MyTagClient mMyTagClient;
+ NdefPushClient mNdefPushClient;
+ NdefPushServer mNdefPushServer;
private static NfcService sService;
@@ -273,8 +273,8 @@ public class NfcService extends Application {
mManager = new NativeNfcManager(mContext, this);
mManager.initializeNativeStructure();
- mMyTagServer = new MyTagServer();
- mMyTagClient = new MyTagClient(this);
+ mNdefPushClient = new NdefPushClient(this);
+ mNdefPushServer = new NdefPushServer();
mSecureElement = new NativeNfcSecureElement();
@@ -339,7 +339,7 @@ public class NfcService extends Application {
if (previouslyEnabled) {
/* tear down the my tag server */
- mMyTagServer.stop();
+ mNdefPushServer.stop();
isSuccess = mManager.deinitialize();
if (DBG) Log.d(TAG, "NFC success of deinitialize = " + isSuccess);
if (isSuccess) {
@@ -361,10 +361,10 @@ public class NfcService extends Application {
public void enableForegroundDispatch(ComponentName activity, PendingIntent intent,
IntentFilter[] filters) {
mContext.enforceCallingOrSelfPermission(NFC_PERM, NFC_PERM_ERROR);
+ if (activity == null || filters == null || filters.length == 0 || intent == null) {
+ throw new IllegalArgumentException();
+ }
synchronized (this) {
- if (activity == null || filters == null || filters.length == 0 || intent == null) {
- throw new IllegalArgumentException();
- }
if (mDispatchOverrideFilters != null) {
Log.e(TAG, "Replacing active dispatch overrides");
}
@@ -386,6 +386,25 @@ public class NfcService extends Application {
}
@Override
+ public void enableForegroundNdefPush(ComponentName activity, NdefMessage msg) {
+ mContext.enforceCallingOrSelfPermission(NFC_PERM, NFC_PERM_ERROR);
+ if (activity == null || msg == null) {
+ throw new IllegalArgumentException();
+ }
+ if (mNdefPushClient.setForegroundMessage(msg)) {
+ Log.e(TAG, "Replacing active NDEF push message");
+ }
+ }
+
+ @Override
+ public void disableForegroundNdefPush(ComponentName activity) {
+ mContext.enforceCallingOrSelfPermission(NFC_PERM, NFC_PERM_ERROR);
+ if (!mNdefPushClient.setForegroundMessage(null)) {
+ Log.e(TAG, "No active foreground NDEF push message");
+ }
+ }
+
+ @Override
public int createLlcpConnectionlessSocket(int sap) throws RemoteException {
mContext.enforceCallingOrSelfPermission(NFC_PERM, NFC_PERM_ERROR);
@@ -2142,7 +2161,7 @@ public class NfcService extends Application {
maybeEnableDiscovery();
/* bring up the my tag server */
- mMyTagServer.start();
+ mNdefPushServer.start();
} else {
mIsNfcEnabled = false;
diff --git a/src/com/android/nfc/mytag/MyTagClient.java b/src/com/android/nfc/ndefpush/NdefPushClient.java
index 613fad4..8d7c9a8 100755
--- a/src/com/android/nfc/mytag/MyTagClient.java
+++ b/src/com/android/nfc/ndefpush/NdefPushClient.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.nfc.mytag;
+package com.android.nfc.ndefpush;
import com.android.internal.nfc.LlcpException;
import com.android.internal.nfc.LlcpSocket;
@@ -30,20 +30,32 @@ import android.os.AsyncTask;
import android.util.Log;
import java.io.IOException;
+import java.util.Arrays;
/**
* Simple client to push the local NDEF message to a server on the remote side of an
* LLCP connection. The message is set via {@link NfcAdapter#setLocalNdefMessage}.
*/
-public class MyTagClient extends BroadcastReceiver {
- private static final String TAG = "MyTagClient";
+public class NdefPushClient extends BroadcastReceiver {
+ private static final String TAG = "NdefPushClient";
private static final int MIU = 128;
private static final boolean DBG = true;
- public MyTagClient(Context context) {
+ /** Locked on MyTagClient.class */
+ NdefMessage mForegroundMsg;
+
+ public NdefPushClient(Context context) {
context.registerReceiver(this, new IntentFilter(NfcAdapter.ACTION_LLCP_LINK_STATE_CHANGED));
}
+ public boolean setForegroundMessage(NdefMessage msg) {
+ synchronized (this) {
+ boolean set = mForegroundMsg != null;
+ mForegroundMsg = msg;
+ return set;
+ }
+ }
+
@Override
public void onReceive(Context context, Intent intent) {
int linkState = intent.getIntExtra(NfcAdapter.EXTRA_LLCP_LINK_STATE_CHANGED,
@@ -54,63 +66,68 @@ public class MyTagClient extends BroadcastReceiver {
}
if (DBG) Log.d(TAG, "LLCP connection up and running");
+
+ NdefMessage foregroundMsg;
+ synchronized (this) {
+ foregroundMsg = mForegroundMsg;
+ }
+
NfcAdapter adapter = NfcAdapter.getDefaultAdapter(context);
- NdefMessage msg = adapter.getLocalNdefMessage();
- if (msg == null) {
- if (DBG) Log.d(TAG, "No MyTag set, exiting");
- // Nothing to send to the server
+ NdefMessage myTag = adapter.getLocalNdefMessage();
+
+ if (foregroundMsg != null && myTag != null) {
+ if (DBG) Log.d(TAG, "sending foreground and my tag");
+ new SendAsync().execute(foregroundMsg, myTag);
+ } else if (myTag != null) {
+ if (DBG) Log.d(TAG, "sending my tag");
+ new SendAsync().execute(myTag);
+ } else if (foregroundMsg != null) {
+ if (DBG) Log.d(TAG, "sending foreground");
+ new SendAsync().execute(foregroundMsg);
+ } else {
+ if (DBG) Log.d(TAG, "no tags set, bailing");
return;
}
-
- new SendAsync().execute(msg);
}
final class SendAsync extends AsyncTask<NdefMessage, Void, Void> {
- private void trace(String msg) {
- if (DBG) Log.d(TAG, Thread.currentThread().getId() + ": " + msg);
- }
- private void error(String msg, Throwable e) {
- if (DBG) Log.e(TAG, Thread.currentThread().getId() + ": " + msg, e);
- }
-
@Override
public Void doInBackground(NdefMessage... msgs) {
NfcService service = NfcService.getInstance();
- NdefMessage msg = msgs[0];
+
+ // We only handle a single immediate action for now
+ NdefPushProtocol msg = new NdefPushProtocol(msgs[0], NdefPushProtocol.ACTION_IMMEDIATE);
byte[] buffer = msg.toByteArray();
int offset = 0;
int remoteMiu;
LlcpSocket sock = null;
try {
- trace("about to create socket");
+ if (DBG) Log.d(TAG, "about to create socket");
// Connect to the my tag server on the remote side
sock = service.createLlcpSocket(0, MIU, 1, 1024);
- trace("about to connect");
-// sock.connect(MyTagServer.SERVICE_NAME);
- sock.connect(0x20);
+ if (DBG) Log.d(TAG, "about to connect to service " + NdefPushServer.SERVICE_NAME);
+ sock.connect(NdefPushServer.SERVICE_NAME);
remoteMiu = sock.getRemoteSocketMiu();
- trace("about to send a " + buffer.length + "-bytes message");
+ if (DBG) Log.d(TAG, "about to send a " + buffer.length + " byte message");
while (offset < buffer.length) {
- int length = buffer.length - offset;
- if (length > remoteMiu) {
- length = remoteMiu;
- }
- byte[] tmpBuffer = new byte[length];
- System.arraycopy(buffer, offset, tmpBuffer, 0, length);
- trace("about to send a " + length + "-bytes packet");
+ int length = Math.min(buffer.length - offset, remoteMiu);
+ byte[] tmpBuffer = Arrays.copyOfRange(buffer, offset, length);
+ if (DBG) Log.d(TAG, "about to send a " + length + " byte packet");
sock.send(tmpBuffer);
offset += length;
}
} catch (IOException e) {
- error("couldn't send tag", e);
+ Log.e(TAG, "couldn't send tag");
+ if (DBG) Log.d(TAG, "exception:", e);
} catch (LlcpException e) {
// Most likely the other side doesn't support the my tag protocol
- error("couldn't send tag", e);
+ Log.e(TAG, "couldn't send tag");
+ if (DBG) Log.d(TAG, "exception:", e);
} finally {
if (sock != null) {
try {
- trace("about to close");
+ if (DBG) Log.d(TAG, "about to close");
sock.close();
} catch (IOException e) {
// Ignore
diff --git a/src/com/android/nfc/ndefpush/NdefPushProtocol.java b/src/com/android/nfc/ndefpush/NdefPushProtocol.java
new file mode 100644
index 0000000..5520615
--- /dev/null
+++ b/src/com/android/nfc/ndefpush/NdefPushProtocol.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.nfc.ndefpush;
+
+import android.util.Log;
+
+import android.nfc.NdefMessage;
+import android.nfc.FormatException;
+
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+
+/**
+ * Implementation of the NDEF Push Protocol.
+ */
+public class NdefPushProtocol {
+ public static final byte ACTION_IMMEDIATE = (byte) 0x01;
+ public static final byte ACTION_BACKGROUND = (byte) 0x02;
+
+ private static final String TAG = "NdefMessageSet";
+ private static final byte VERSION = 1;
+
+ private int mNumMessages;
+ private byte[] mActions;
+ private NdefMessage[] mMessages;
+
+ public NdefPushProtocol(NdefMessage msg, byte action) {
+ mNumMessages = 1;
+ mActions = new byte[1];
+ mActions[0] = action;
+ mMessages = new NdefMessage[1];
+ mMessages[0] = msg;
+ }
+
+ public NdefPushProtocol(byte[] actions, NdefMessage[] messages) {
+ if (actions.length != messages.length || actions.length == 0) {
+ throw new IllegalArgumentException(
+ "actions and messages must be the same size and non-empty");
+ }
+
+ // Keep a copy of these arrays
+ int numMessages = actions.length;
+ mActions = new byte[numMessages];
+ System.arraycopy(actions, 0, mActions, 0, numMessages);
+ mMessages = new NdefMessage[numMessages];
+ System.arraycopy(messages, 0, mMessages, 0, numMessages);
+ mNumMessages = numMessages;
+ }
+
+ public NdefPushProtocol(byte[] data) throws FormatException {
+ ByteArrayInputStream buffer = new ByteArrayInputStream(data);
+ DataInputStream input = new DataInputStream(buffer);
+
+ // Check version of protocol
+ byte version;
+ try {
+ version = input.readByte();
+ } catch (java.io.IOException e) {
+ Log.w(TAG, "Unable to read version");
+ throw new FormatException("Unable to read version");
+ }
+
+ if(version != VERSION) {
+ Log.w(TAG, "Got version " + version + ", expected " + VERSION);
+ throw new FormatException("Got version " + version + ", expected " + VERSION);
+ }
+
+ // Read numMessages
+ try {
+ mNumMessages = input.readInt();
+ } catch(java.io.IOException e) {
+ Log.w(TAG, "Unable to read numMessages");
+ throw new FormatException("Error while parsing NdefMessageSet");
+ }
+ if(mNumMessages == 0) {
+ Log.w(TAG, "No NdefMessage inside NdefMessageSet packet");
+ throw new FormatException("Error while parsing NdefMessageSet");
+ }
+
+ // Read actions and messages
+ mActions = new byte[mNumMessages];
+ mMessages = new NdefMessage[mNumMessages];
+ for(int i = 0; i < mNumMessages; i++) {
+ // Read action
+ try {
+ mActions[i] = input.readByte();
+ } catch(java.io.IOException e) {
+ Log.w(TAG, "Unable to read action for message " + i);
+ throw new FormatException("Error while parsing NdefMessageSet");
+ }
+ // Read message length
+ int length;
+ try {
+ length = input.readInt();
+ } catch(java.io.IOException e) {
+ Log.w(TAG, "Unable to read length for message " + i);
+ throw new FormatException("Error while parsing NdefMessageSet");
+ }
+ byte[] bytes = new byte[length];
+ // Read message
+ int lengthRead;
+ try {
+ lengthRead = input.read(bytes);
+ } catch(java.io.IOException e) {
+ Log.w(TAG, "Unable to read bytes for message " + i);
+ throw new FormatException("Error while parsing NdefMessageSet");
+ }
+ if(length != lengthRead) {
+ Log.w(TAG, "Read " + lengthRead + " bytes but expected " +
+ length);
+ throw new FormatException("Error while parsing NdefMessageSet");
+ }
+ // Create and store NdefMessage
+ try {
+ mMessages[i] = new NdefMessage(bytes);
+ } catch(FormatException e) {
+ throw e;
+ }
+ }
+ }
+
+ public NdefMessage getImmediate() {
+ // Find and return the first immediate message
+ for(int i = 0; i < mNumMessages; i++) {
+ if(mActions[i] == ACTION_IMMEDIATE) {
+ return mMessages[i];
+ }
+ }
+ return null;
+ }
+
+ public byte[] toByteArray() {
+ ByteArrayOutputStream buffer = new ByteArrayOutputStream(1024);
+ DataOutputStream output = new DataOutputStream(buffer);
+
+ try {
+ output.writeByte(VERSION);
+ output.writeInt(mNumMessages);
+ for(int i = 0; i < mNumMessages; i++) {
+ output.writeByte(mActions[i]);
+ byte[] bytes = mMessages[i].toByteArray();
+ output.writeInt(bytes.length);
+ output.write(bytes);
+ }
+ } catch(java.io.IOException e) {
+ return null;
+ }
+
+ return buffer.toByteArray();
+ }
+}
diff --git a/src/com/android/nfc/mytag/MyTagServer.java b/src/com/android/nfc/ndefpush/NdefPushServer.java
index 0b7c426..4073567 100755
--- a/src/com/android/nfc/mytag/MyTagServer.java
+++ b/src/com/android/nfc/ndefpush/NdefPushServer.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.nfc.mytag;
+package com.android.nfc.ndefpush;
import com.android.internal.nfc.LlcpException;
import com.android.internal.nfc.LlcpServiceSocket;
@@ -22,7 +22,6 @@ import com.android.internal.nfc.LlcpSocket;
import com.android.nfc.NfcService;
import android.nfc.FormatException;
-import android.nfc.NdefMessage;
import android.nfc.NfcAdapter;
import android.util.Log;
@@ -33,14 +32,14 @@ import java.io.IOException;
* A simple server that accepts NDEF messages pushed to it over an LLCP connection. Those messages
* are typically set on the client side by using {@link NfcAdapter#setLocalNdefMessage}.
*/
-public class MyTagServer {
- private static final String TAG = "MyTagServer";
+public class NdefPushServer {
+ private static final String TAG = "NdefPushServer";
private static final boolean DBG = true;
- private static final int SERVICE_SAP = 0x20;
+ private static final int SERVICE_SAP = 0x10;
private static final int MIU = 256;
- static final String SERVICE_NAME = "com.android.mytag";
+ static final String SERVICE_NAME = "com.android.npp";
NfcService mService = NfcService.getInstance();
@@ -52,7 +51,7 @@ public class MyTagServer {
private LlcpSocket mSock;
ConnectionThread(LlcpSocket sock) {
- super("MyTagServer");
+ super(TAG);
mSock = sock;
}
@@ -83,12 +82,12 @@ public class MyTagServer {
}
}
- // Build NDEF message from the stream
- NdefMessage msg = new NdefMessage(buffer.toByteArray());
+ // Build NDEF message set from the stream
+ NdefPushProtocol msg = new NdefPushProtocol(buffer.toByteArray());
if (DBG) Log.d(TAG, "got message " + msg.toString());
// Send the intent for the fake tag
- mService.sendMockNdefTag(msg);
+ mService.sendMockNdefTag(msg.getImmediate());
} catch (FormatException e) {
Log.e(TAG, "badly formatted NDEF message, ignoring", e);
} finally {
@@ -112,7 +111,7 @@ public class MyTagServer {
public void run() {
while (mRunning) {
if (DBG) Log.d(TAG, "about create LLCP service socket");
- mServerSocket = mService.createLlcpServiceSocket(SERVICE_SAP, null,
+ mServerSocket = mService.createLlcpServiceSocket(SERVICE_SAP, SERVICE_NAME,
MIU, 1, 1024);
if (mServerSocket == null) {
if (DBG) Log.d(TAG, "failed to create LLCP service socket");