Revert "Remove My Tag support."
This reverts commit 825f01522a1d68cadb634c88101e96f842478926.
Change-Id: I75e027c066ba7bddaab87f910083707d8eb380b0
diff --git a/src/com/android/nfc/NfcService.java b/src/com/android/nfc/NfcService.java
index db6d758..2aab0cc 100755
--- a/src/com/android/nfc/NfcService.java
+++ b/src/com/android/nfc/NfcService.java
@@ -18,8 +18,11 @@
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 android.app.Application;
+import android.app.StatusBarManager;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -48,6 +51,11 @@
import android.os.ServiceManager;
import android.util.Log;
+import java.io.ByteArrayOutputStream;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.ListIterator;
@@ -55,6 +63,8 @@
public class NfcService extends Application {
static final boolean DBG = false;
+ private static final String MY_TAG_FILE_NAME = "mytag";
+
static {
System.loadLibrary("nfc_jni");
}
@@ -148,6 +158,9 @@
static final int MSG_LLCP_LINK_ACTIVATION = 2;
static final int MSG_LLCP_LINK_DEACTIVATED = 3;
static final int MSG_TARGET_DESELECTED = 4;
+ static final int MSG_SHOW_MY_TAG_ICON = 5;
+ static final int MSG_HIDE_MY_TAG_ICON = 6;
+ static final int MSG_MOCK_NDEF_TAG = 7;
// TODO: none of these appear to be synchronized but are
// read/written from different threads (notably Binder threads)...
@@ -170,6 +183,8 @@
private SharedPreferences mPrefs;
private SharedPreferences.Editor mPrefsEditor;
private PowerManager.WakeLock mWakeLock;
+ private MyTagServer mMyTagServer;
+ private MyTagClient mMyTagClient;
private static NfcService sService;
@@ -189,6 +204,9 @@
mManager = new NativeNfcManager(mContext, this);
mManager.initializeNativeStructure();
+ mMyTagServer = new MyTagServer();
+ mMyTagClient = new MyTagClient(this);
+
mPrefs = mContext.getSharedPreferences(PREF, Context.MODE_PRIVATE);
mPrefsEditor = mPrefs.edit();
@@ -250,6 +268,7 @@
if (previouslyEnabled) {
/* tear down the my tag server */
+ mMyTagServer.stop();
isSuccess = mManager.deinitialize();
if (DBG) Log.d(TAG, "NFC success of deinitialize = " + isSuccess);
if (isSuccess) {
@@ -798,12 +817,56 @@
@Override
public NdefMessage localGet() throws RemoteException {
- throw new UnsupportedOperationException("local tag not supported");
+ mContext.enforceCallingOrSelfPermission(NFC_PERM, NFC_PERM_ERROR);
+
+ synchronized (this) {
+ return mLocalMessage;
+ }
}
@Override
public void localSet(NdefMessage message) throws RemoteException {
- throw new UnsupportedOperationException("local tag not supported");
+ mContext.enforceCallingOrSelfPermission(ADMIN_PERM, ADMIN_PERM_ERROR);
+
+ synchronized (this) {
+ mLocalMessage = message;
+ Context context = NfcService.this.getApplicationContext();
+
+ // Send a message to the UI thread to show or hide the icon so the requests are
+ // serialized and the icon can't get out of sync with reality.
+ if (message != null) {
+ FileOutputStream out = null;
+
+ try {
+ out = context.openFileOutput(MY_TAG_FILE_NAME, Context.MODE_PRIVATE);
+ byte[] bytes = message.toByteArray();
+ if (bytes.length == 0) {
+ Log.w(TAG, "Setting a empty mytag");
+ }
+
+ out.write(bytes);
+ } catch (IOException e) {
+ Log.e(TAG, "Could not write mytag file", e);
+ } finally {
+ try {
+ if (out != null) {
+ out.flush();
+ out.close();
+ }
+ } catch (IOException e) {
+ // Ignore
+ }
+ }
+
+ // Only show the icon if NFC is enabled.
+ if (mIsNfcEnabled) {
+ sendMessage(MSG_SHOW_MY_TAG_ICON, null);
+ }
+ } else {
+ context.deleteFile(MY_TAG_FILE_NAME);
+ sendMessage(MSG_HIDE_MY_TAG_ICON, null);
+ }
+ }
}
};
@@ -1713,6 +1776,10 @@
/* Start polling loop */
maybeEnableDiscovery();
+
+ /* bring up the my tag server */
+ mMyTagServer.start();
+
} else {
mIsNfcEnabled = false;
}
@@ -1766,6 +1833,55 @@
intent.putExtra(NfcAdapter.EXTRA_NEW_BOOLEAN_STATE, mIsNfcEnabled);
mContext.sendBroadcast(intent);
}
+
+ if (mIsNfcEnabled) {
+
+ Context context = getApplicationContext();
+
+ // Set this to null by default. If there isn't a tag on disk
+ // or if there was an error reading the tag then this will cause
+ // the status bar icon to be removed.
+ NdefMessage myTag = null;
+
+ FileInputStream input = null;
+
+ try {
+ input = context.openFileInput(MY_TAG_FILE_NAME);
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+
+ byte[] buffer = new byte[4096];
+ int read = 0;
+ while ((read = input.read(buffer)) > 0) {
+ bytes.write(buffer, 0, read);
+ }
+
+ myTag = new NdefMessage(bytes.toByteArray());
+ } catch (FileNotFoundException e) {
+ // Ignore.
+ } catch (IOException e) {
+ Log.e(TAG, "Could not read mytag file: ", e);
+ context.deleteFile(MY_TAG_FILE_NAME);
+ } catch (FormatException e) {
+ Log.e(TAG, "Invalid NdefMessage for mytag", e);
+ context.deleteFile(MY_TAG_FILE_NAME);
+ } finally {
+ try {
+ if (input != null) {
+ input.close();
+ }
+ } catch (IOException e) {
+ // Ignore
+ }
+ }
+
+ try {
+ mNfcAdapter.localSet(myTag);
+ } catch (RemoteException e) {
+ // Ignore
+ }
+ } else {
+ sendMessage(MSG_HIDE_MY_TAG_ICON, null);
+ }
}
}
@@ -2044,6 +2160,14 @@
mContext.sendOrderedBroadcast(LlcpLinkIntent, NFC_PERM);
}
+ public void sendMockNdefTag(NdefMessage msg) {
+ NdefTag tag = NdefTag.createMockNdefTag(new byte[] { 0x00 },
+ new String[] { Tag.TARGET_OTHER },
+ null, null, new String[] { NdefTag.TARGET_OTHER },
+ new NdefMessage[][] { new NdefMessage[] { msg } });
+ sendMessage(MSG_MOCK_NDEF_TAG, tag);
+ }
+
void sendMessage(int what, Object obj) {
Message msg = mHandler.obtainMessage();
msg.what = what;
@@ -2055,6 +2179,19 @@
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
+ case MSG_MOCK_NDEF_TAG: {
+ NdefTag tag = (NdefTag) msg.obj;
+ Intent intent = buildNdefTagIntent(tag);
+ Log.d(TAG, "mock NDEF tag, starting corresponding activity");
+ Log.d(TAG, tag.toString());
+ try {
+ mContext.startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ Log.w(TAG, "No activity found for mock tag");
+ }
+ break;
+ }
+
case MSG_NDEF_TAG:
if (DBG) Log.d(TAG, "Tag detected, notifying applications");
NativeNfcTag nativeTag = (NativeNfcTag) msg.obj;
@@ -2221,6 +2358,20 @@
mContext.sendOrderedBroadcast(TargetDeselectedIntent, NFC_PERM);
break;
+ case MSG_SHOW_MY_TAG_ICON: {
+ StatusBarManager sb = (StatusBarManager) getSystemService(
+ Context.STATUS_BAR_SERVICE);
+ sb.setIcon("nfc", R.drawable.stat_sys_nfc, 0);
+ break;
+ }
+
+ case MSG_HIDE_MY_TAG_ICON: {
+ StatusBarManager sb = (StatusBarManager) getSystemService(
+ Context.STATUS_BAR_SERVICE);
+ sb.removeIcon("nfc");
+ break;
+ }
+
default:
Log.e(TAG, "Unknown message received");
break;
diff --git a/src/com/android/nfc/mytag/MyTagClient.java b/src/com/android/nfc/mytag/MyTagClient.java
new file mode 100755
index 0000000..241d9e2
--- /dev/null
+++ b/src/com/android/nfc/mytag/MyTagClient.java
@@ -0,0 +1,97 @@
+/*
+ * 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.mytag;
+
+import com.android.internal.nfc.LlcpException;
+import com.android.internal.nfc.LlcpSocket;
+import com.android.nfc.NfcService;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.nfc.NdefMessage;
+import android.nfc.NfcAdapter;
+import android.os.AsyncTask;
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * 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";
+ private static final boolean DBG = true;
+
+ public MyTagClient(Context context) {
+ context.registerReceiver(this, new IntentFilter(NfcAdapter.ACTION_LLCP_LINK_STATE_CHANGED));
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (DBG) Log.d(TAG, "LLCP connection up and running");
+ NfcAdapter adapter = NfcAdapter.getDefaultAdapter();
+ NdefMessage msg = adapter.getLocalNdefMessage();
+
+ if (msg == null) {
+ if (DBG) Log.d(TAG, "No MyTag set, exiting");
+ // Nothing to send to the server
+ return;
+ }
+
+ int linkState = intent.getIntExtra(NfcAdapter.EXTRA_LLCP_LINK_STATE_CHANGED,
+ NfcAdapter.LLCP_LINK_STATE_DEACTIVATED);
+
+ if (linkState != NfcAdapter.LLCP_LINK_STATE_ACTIVATED) {
+ if (DBG) Log.d(TAG, "LLCP connection not activated, exiting");
+ return;
+ }
+
+ new SendAsync().execute(msg);
+ }
+
+ final class SendAsync extends AsyncTask<NdefMessage, Void, Void> {
+ @Override
+ public Void doInBackground(NdefMessage... msgs) {
+ NfcService service = NfcService.getInstance();
+ NdefMessage msg = msgs[0];
+ try {
+ if (DBG) Log.d(TAG, "about to create socket");
+ // Connect to the my tag server on the remote side
+ LlcpSocket sock = service.createLlcpSocket(0, 128, 1, 1024);
+ if (DBG) Log.d(TAG, "about to connect");
+// sock.connect(MyTagServer.SERVICE_NAME);
+ sock.connect(0x20);
+
+ // Push the local NDEF message to the server
+ if (DBG) Log.d(TAG, "about to send");
+ sock.send(msg.toByteArray());
+ if (DBG) Log.d(TAG, "about to close");
+ sock.close();
+
+ } catch (IOException e) {
+ Log.e(TAG, "couldn't send tag", e);
+ } catch (LlcpException e) {
+ // Most likely the other side doesn't support the my tag protocol
+ Log.e(TAG, "couldn't send tag", e);
+ }
+ return null;
+ }
+ }
+}
diff --git a/src/com/android/nfc/mytag/MyTagServer.java b/src/com/android/nfc/mytag/MyTagServer.java
new file mode 100755
index 0000000..cb2059f
--- /dev/null
+++ b/src/com/android/nfc/mytag/MyTagServer.java
@@ -0,0 +1,156 @@
+/*
+ * 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.mytag;
+
+import com.android.internal.nfc.LlcpException;
+import com.android.internal.nfc.LlcpServiceSocket;
+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;
+
+import java.io.ByteArrayOutputStream;
+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 = "~~~~~~~~~~~~~~~";
+ private static final boolean DBG = true;
+ private static final int SERVICE_SAP = 0x20;
+
+ static final String SERVICE_NAME = "com.android.mytag";
+
+ NfcService mService = NfcService.getInstance();
+ /** Protected by 'this', null when stopped, non-null when running */
+ ServerThread mServerThread = null;
+
+ /** Connection class, used to handle incoming connections */
+ private class ConnectionThread extends Thread {
+ private LlcpSocket mSock;
+
+ ConnectionThread(LlcpSocket sock) {
+ mSock = sock;
+ }
+
+ @Override
+ public void run() {
+ if (DBG) Log.d(TAG, "starting connection thread");
+ try {
+ ByteArrayOutputStream buffer = new ByteArrayOutputStream(1024);
+ byte[] partial = new byte[1024];
+ int size;
+ boolean connectionBroken = false;
+
+ // Get raw data from remote server
+ while(!connectionBroken) {
+ try {
+ size = mSock.receive(partial);
+ if (DBG) Log.d(TAG, "read " + size + " bytes");
+ if (size < 0) {
+ connectionBroken = true;
+ break;
+ } else {
+ buffer.write(partial, 0, size);
+ }
+ } catch (IOException e) {
+ // Connection broken
+ connectionBroken = true;
+ if (DBG) Log.d(TAG, "connection broken");
+ }
+ }
+
+ // Build NDEF message from the stream
+ NdefMessage msg = new NdefMessage(buffer.toByteArray());
+ if (DBG) Log.d(TAG, "got message " + msg.toString());
+
+ // Send the intent for the fake tag
+ mService.sendMockNdefTag(msg);
+ } catch (FormatException e) {
+ Log.e(TAG, "badly formatted NDEF message, ignoring", e);
+ }
+ }
+ };
+
+ /** Server class, used to listen for incoming connection request */
+ class ServerThread extends Thread {
+ boolean mRunning = true;
+ LlcpServiceSocket mServerSocket;
+
+ @Override
+ public void run() {
+ if (DBG) Log.d(TAG, "about create LLCP service socket");
+ mServerSocket = mService.createLlcpServiceSocket(SERVICE_SAP, null,
+ 128, 1, 1024);
+ if (mServerSocket == null) {
+ Log.d(TAG, "failed to create LLCP service socket");
+ return;
+ }
+ if (DBG) Log.d(TAG, "created LLCP service socket");
+ try {
+ while (mRunning) {
+ if (DBG) Log.d(TAG, "about to accept");
+ LlcpSocket communicationSocket = mServerSocket.accept();
+ if (DBG) Log.d(TAG, "accept returned " + communicationSocket);
+ if (communicationSocket != null) {
+ new ConnectionThread(communicationSocket).start();
+ }
+ }
+ } catch (LlcpException e) {
+ Log.e(TAG, "llcp error", e);
+ } catch (IOException e) {
+ Log.e(TAG, "IO error", e);
+ } finally {
+ if (mServerSocket != null) mServerSocket.close();
+ }
+ }
+
+ public void shutdown() {
+ mRunning = false;
+ if (mServerSocket != null) {
+ mServerSocket.close();
+ }
+ }
+ };
+
+ public void start() {
+ synchronized (this) {
+ if (DBG) Log.d(TAG, "start, thread = " + mServerThread);
+ if (mServerThread == null) {
+ if (DBG) Log.d(TAG, "starting new server thread");
+ mServerThread = new ServerThread();
+ mServerThread.start();
+ }
+ }
+ }
+
+ public void stop() {
+ synchronized (this) {
+ if (DBG) Log.d(TAG, "stop, thread = " + mServerThread);
+ if (mServerThread != null) {
+ if (DBG) Log.d(TAG, "shuting down server thread");
+ mServerThread.shutdown();
+ mServerThread = null;
+ }
+ }
+ }
+}