resolved conflicts for merge of fe817189 to master
Change-Id: I42b37768e9f66e959b5487e4e5409e517202d63a
diff --git a/Android.mk b/Android.mk
index e5b93b5..b5ae045 100755
--- a/Android.mk
+++ b/Android.mk
@@ -12,8 +12,6 @@
LOCAL_PROGUARD_ENABLED := disabled
-# dont build in master
-# temporary
-#include $(BUILD_PACKAGE)
+include $(BUILD_PACKAGE)
-#include $(call all-makefiles-under,$(LOCAL_PATH))
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index eaf432d..f7ce543 100755
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -22,7 +22,10 @@
android:icon="@drawable/icon"
android:label="@string/app_name"
android:persistent="true"
+ android:backupAgent="com.android.nfc.NfcBackupAgent"
>
+ <meta-data android:name="com.google.android.backup.api_key"
+ android:value="AEdPqrEAAAAIEXGvR1H2lq6SEOC7O7rGHoAmAf-GHCR_EhLqzg" />
<activity android:name="com.android.nfc.TechListChooserActivity"
android:theme="@*android:style/Theme.Dialog.Alert"
diff --git a/jni/Android.mk b/jni/Android.mk
index f68feb9..600a54a 100644
--- a/jni/Android.mk
+++ b/jni/Android.mk
@@ -2,7 +2,7 @@
include $(CLEAR_VARS)
-LOCAL_PRELINK_MODULE := false
+
LOCAL_SRC_FILES:= \
com_android_nfc_NativeLlcpConnectionlessSocket.cpp \
diff --git a/jni/com_android_nfc.cpp b/jni/com_android_nfc.cpp
index c9b9da1..0facbd2 100644
--- a/jni/com_android_nfc.cpp
+++ b/jni/com_android_nfc.cpp
@@ -487,8 +487,6 @@
{
index = addTechIfNeeded(technologies, handles, libnfctypes, index,
MAX_NUM_TECHNOLOGIES, TARGET_TYPE_ISO14443_4, handle, type);
- index = addTechIfNeeded(technologies, handles, libnfctypes, index,
- MAX_NUM_TECHNOLOGIES, TARGET_TYPE_ISO14443_3A, handle, type);
break;
}
case phNfc_eISO14443_4B_PICC:
@@ -531,14 +529,10 @@
// could be UL or UL-C
index = addTechIfNeeded(technologies, handles, libnfctypes,
index, MAX_NUM_TECHNOLOGIES, TARGET_TYPE_MIFARE_UL, handle, type);
- index = addTechIfNeeded(technologies, handles, libnfctypes,
- index, MAX_NUM_TECHNOLOGIES, TARGET_TYPE_ISO14443_3A, handle, type);
break;
default:
index = addTechIfNeeded(technologies, handles, libnfctypes,
index, MAX_NUM_TECHNOLOGIES, TARGET_TYPE_MIFARE_CLASSIC, handle, type);
- index = addTechIfNeeded(technologies, handles, libnfctypes,
- index, MAX_NUM_TECHNOLOGIES, TARGET_TYPE_ISO14443_3A, handle, type);
break;
}
}break;
diff --git a/jni/com_android_nfc_NativeNfcManager.cpp b/jni/com_android_nfc_NativeNfcManager.cpp
index d0bd3b7..a1dd891 100644
--- a/jni/com_android_nfc_NativeNfcManager.cpp
+++ b/jni/com_android_nfc_NativeNfcManager.cpp
@@ -596,6 +596,7 @@
REENTRANCE_LOCK();
phLibNfc_SetIsoXchgTimeout(NXP_ISO_XCHG_TIMEOUT);
phLibNfc_SetHciTimeout(NXP_NFC_HCI_TIMEOUT);
+ phLibNfc_SetFelicaTimeout(NXP_FELICA_XCHG_TIMEOUT);
REENTRANCE_UNLOCK();
}
@@ -1492,12 +1493,32 @@
CONCURRENCY_UNLOCK();
}
-static void com_android_nfc_NfcManager_doResetIsoDepTimeout( JNIEnv *e, jobject o) {
+static void com_android_nfc_NfcManager_doResetTimeouts( JNIEnv *e, jobject o) {
CONCURRENCY_LOCK();
nfc_jni_reset_timeout_values();
CONCURRENCY_UNLOCK();
}
+
+static void com_android_nfc_NfcManager_doSetFelicaTimeout( JNIEnv *e, jobject o,
+ jint timeout) {
+ CONCURRENCY_LOCK();
+ // The Felica timeout is configurable in the PN544 upto a maximum of 255 ms.
+ // It can be set to 0 to disable the timeout altogether, in which case we
+ // use the sw watchdog as a fallback.
+ if (timeout == 0) {
+ // Disable timeout altogether, not allowed
+ LOGE("It's not allowed to set the NFC Felica timeout to 0!");
+ } else if (timeout <= 255) {
+ phLibNfc_SetFelicaTimeout(timeout);
+ } else {
+ // Disable hw timeout, use sw watchdog for timeout
+ phLibNfc_SetFelicaTimeout(0);
+ phLibNfc_SetHciTimeout(timeout);
+ }
+
+ CONCURRENCY_UNLOCK();
+}
// Calculates ceiling log2 of value
static unsigned int log2(int value) {
unsigned int ret = 0;
@@ -2394,8 +2415,11 @@
{"doSetIsoDepTimeout", "(I)V",
(void *)com_android_nfc_NfcManager_doSetIsoDepTimeout},
- {"doResetIsoDepTimeout", "()V",
- (void *)com_android_nfc_NfcManager_doResetIsoDepTimeout},
+ {"doResetTimeouts", "()V",
+ (void *)com_android_nfc_NfcManager_doResetTimeouts},
+
+ {"doSetFelicaTimeout", "(I)V",
+ (void *)com_android_nfc_NfcManager_doSetFelicaTimeout},
};
diff --git a/res/drawable-hdpi/stat_sys_nfc.png b/res/drawable-hdpi/stat_sys_nfc.png
new file mode 100644
index 0000000..87f22b0
--- /dev/null
+++ b/res/drawable-hdpi/stat_sys_nfc.png
Binary files differ
diff --git a/res/drawable-mdpi/stat_sys_nfc.png b/res/drawable-mdpi/stat_sys_nfc.png
new file mode 100644
index 0000000..ce89ee7
--- /dev/null
+++ b/res/drawable-mdpi/stat_sys_nfc.png
Binary files differ
diff --git a/src/com/android/nfc/ErrorCodes.java b/src/com/android/nfc/ErrorCodes.java
deleted file mode 100755
index e738c1c..0000000
--- a/src/com/android/nfc/ErrorCodes.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * 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;
-
-/**
- * This class defines all the error codes that can be returned by the service
- * and producing an exception on the application level. These are needed since
- * binders does not support exceptions.
- */
-public class ErrorCodes {
-
- public static boolean isError(int code) {
- if (code < 0) {
- return true;
- } else {
- return false;
- }
- }
-
- public static final int SUCCESS = 0;
-
- public static final int ERROR_IO = -1;
-
- public static final int ERROR_CANCELLED = -2;
-
- public static final int ERROR_TIMEOUT = -3;
-
- public static final int ERROR_BUSY = -4;
-
- public static final int ERROR_CONNECT = -5;
-
- public static final int ERROR_DISCONNECT = -5;
-
- public static final int ERROR_READ = -6;
-
- public static final int ERROR_WRITE = -7;
-
- public static final int ERROR_INVALID_PARAM = -8;
-
- public static final int ERROR_INSUFFICIENT_RESOURCES = -9;
-
- public static final int ERROR_SOCKET_CREATION = -10;
-
- public static final int ERROR_SOCKET_NOT_CONNECTED = -11;
-
- public static final int ERROR_BUFFER_TO_SMALL = -12;
-
- public static final int ERROR_SAP_USED = -13;
-
- public static final int ERROR_SERVICE_NAME_USED = -14;
-
- public static final int ERROR_SOCKET_OPTIONS = -15;
-
- public static final int ERROR_NFC_ON = -16;
-
- public static final int ERROR_NOT_INITIALIZED = -17;
-
- public static final int ERROR_SE_ALREADY_SELECTED = -18;
-
- public static final int ERROR_SE_CONNECTED = -19;
-
- public static final int ERROR_NO_SE_CONNECTED = -20;
-
-
-
-
-
-
-}
diff --git a/src/com/android/nfc/NativeNfcManager.java b/src/com/android/nfc/NativeNfcManager.java
index 7e4db32..3eec71c 100755
--- a/src/com/android/nfc/NativeNfcManager.java
+++ b/src/com/android/nfc/NativeNfcManager.java
@@ -93,9 +93,9 @@
public native boolean doActivateLlcp();
- public native void doResetIsoDepTimeout();
- public void resetIsoDepTimeout() {
- doResetIsoDepTimeout();
+ public native void doResetTimeouts();
+ public void resetTimeouts() {
+ doResetTimeouts();
}
public native void doSetIsoDepTimeout(int timeout);
@@ -103,6 +103,12 @@
doSetIsoDepTimeout(timeout);
}
+ public native void doSetFelicaTimeout(int timeout);
+ public void setFelicaTimeout(int timeout) {
+ doSetFelicaTimeout(timeout);
+ }
+
+
/**
* Notifies Ndef Message (TODO: rename into notifyTargetDiscovered)
*/
diff --git a/src/com/android/nfc/NativeNfcTag.java b/src/com/android/nfc/NativeNfcTag.java
index 94df1d2..1489b23 100755
--- a/src/com/android/nfc/NativeNfcTag.java
+++ b/src/com/android/nfc/NativeNfcTag.java
@@ -22,6 +22,7 @@
import android.nfc.tech.NfcB;
import android.nfc.tech.NfcF;
import android.nfc.tech.NfcV;
+import android.nfc.tech.MifareUltralight;
import android.nfc.tech.TagTechnology;
import android.nfc.NdefMessage;
import android.os.Bundle;
@@ -490,6 +491,59 @@
}
+ private boolean isUltralightC() {
+ /* Make a best-effort attempt at classifying ULTRALIGHT
+ * vs ULTRALIGHT-C (based on NXP's public AN1303).
+ * The memory layout is as follows:
+ * Page # BYTE1 BYTE2 BYTE3 BYTE4
+ * 2 INT1 INT2 LOCK LOCK
+ * 3 OTP OTP OTP OTP (NDEF CC if NDEF-formatted)
+ * 4 DATA DATA DATA DATA (version info if factory-state)
+ *
+ * Read four blocks from page 2, which will get us both
+ * the lock page, the OTP page and the version info.
+ */
+ boolean isUltralightC = false;
+ byte[] readCmd = { 0x30, 0x02 };
+ int[] retCode = new int[2];
+ byte[] respData = transceive(readCmd, false, retCode);
+ if (respData != null && respData.length == 16) {
+ // Check the lock bits (last 2 bytes in page2)
+ // and the OTP bytes (entire page 3)
+ if (respData[2] == 0 && respData[3] == 0 && respData[4] == 0 &&
+ respData[5] == 0 && respData[6] == 0 && respData[7] == 0) {
+ // Very likely to be a blank card, look at version info
+ // in page 4.
+ if ((respData[8] == (byte)0x02) && respData[9] == (byte)0x00) {
+ // This is Ultralight-C
+ isUltralightC = true;
+ } else {
+ // 0xFF 0xFF would indicate Ultralight, but we also use Ultralight
+ // as a fallback if it's anything else
+ isUltralightC = false;
+ }
+ } else {
+ // See if we can find the NDEF CC in the OTP page and if it's
+ // smaller than major version two
+ if (respData[4] == (byte)0xE1 && ((respData[5] & 0xff) < 0x20)) {
+ // OK, got NDEF. Technically we'd have to search for the
+ // NDEF TLV as well. However, this would add too much
+ // time for discovery and we can make already make a good guess
+ // with the data we have here. Byte 2 of the OTP page
+ // indicates the size of the tag - 0x06 is UL, anything
+ // above indicates UL-C.
+ if ((respData[6] & 0xff) > 0x06) {
+ isUltralightC = true;
+ }
+ } else {
+ // Fall back to ultralight
+ isUltralightC = false;
+ }
+ }
+ }
+ return isUltralightC;
+ }
+
public Bundle[] getTechExtras() {
synchronized (this) {
if (mTechExtras != null) return mTechExtras;
@@ -556,6 +610,11 @@
}
break;
}
+ case TagTechnology.MIFARE_ULTRALIGHT: {
+ boolean isUlc = isUltralightC();
+ extras.putBoolean(MifareUltralight.EXTRA_IS_UL_C, isUlc);
+ break;
+ }
default: {
// Leave the entry in the array null
diff --git a/src/com/android/nfc/NfcBackupAgent.java b/src/com/android/nfc/NfcBackupAgent.java
new file mode 100644
index 0000000..27fdf76
--- /dev/null
+++ b/src/com/android/nfc/NfcBackupAgent.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2011 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;
+
+import android.app.backup.BackupAgentHelper;
+import android.app.backup.SharedPreferencesBackupHelper;
+
+public class NfcBackupAgent extends BackupAgentHelper {
+ // Backup identifier
+ static final String SHARED_PREFS_BACKUP_KEY = "shared_prefs";
+
+ @Override
+ public void onCreate() {
+ SharedPreferencesBackupHelper helper =
+ new SharedPreferencesBackupHelper(this, NfcService.PREF);
+ addHelper(SHARED_PREFS_BACKUP_KEY, helper);
+ }
+}
+
diff --git a/src/com/android/nfc/NfcDispatcher.java b/src/com/android/nfc/NfcDispatcher.java
new file mode 100644
index 0000000..1fece76
--- /dev/null
+++ b/src/com/android/nfc/NfcDispatcher.java
@@ -0,0 +1,394 @@
+/*
+ * Copyright (C) 2011 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;
+
+import android.app.Activity;
+import android.app.ActivityManagerNative;
+import android.app.IActivityManager;
+import android.app.PendingIntent;
+import android.app.PendingIntent.CanceledException;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
+import android.nfc.FormatException;
+import android.nfc.NdefMessage;
+import android.nfc.NdefRecord;
+import android.nfc.NfcAdapter;
+import android.nfc.Tag;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.nfc.ndefpush.NdefPushClient;
+import com.android.nfc.RegisteredComponentCache.ComponentInfo;
+
+import java.nio.charset.Charsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * Dispatch of NFC events to start activities
+ */
+public class NfcDispatcher {
+ private static final boolean DBG = NfcService.DBG;
+ private static final String TAG = NfcService.TAG;
+
+ private final Context mContext;
+ private final NdefPushClient mNdefPushClient;
+ private final IActivityManager mIActivityManager;
+ private final RegisteredComponentCache mTechListFilters;
+
+ // Locked on this
+ private PendingIntent mOverrideIntent;
+ private IntentFilter[] mOverrideFilters;
+ private String[][] mOverrideTechLists;
+
+ public NfcDispatcher(Context context, NdefPushClient ndefPushClient) {
+ mContext = context;
+ mNdefPushClient = ndefPushClient;
+ mIActivityManager = ActivityManagerNative.getDefault();
+ mTechListFilters = new RegisteredComponentCache(mContext,
+ NfcAdapter.ACTION_TECH_DISCOVERED, NfcAdapter.ACTION_TECH_DISCOVERED);
+ }
+
+ public synchronized void disableForegroundDispatch() {
+ if (DBG) Log.d(TAG, "Disable Foreground Dispatch");
+ mOverrideIntent = null;
+ mOverrideFilters = null;
+ mOverrideTechLists = null;
+ }
+
+ public synchronized void enableForegroundDispatch(PendingIntent intent,
+ IntentFilter[] filters, String[][] techLists) {
+ if (DBG) Log.d(TAG, "Enable Foreground Dispatch");
+ mOverrideIntent = intent;
+ mOverrideFilters = filters;
+ mOverrideTechLists = techLists;
+ }
+
+ /** Returns false if no activities were found to dispatch to */
+ public boolean dispatchTag(Tag tag, NdefMessage[] msgs) {
+ if (DBG) {
+ Log.d(TAG, "Dispatching tag");
+ Log.d(TAG, tag.toString());
+ }
+
+ IntentFilter[] overrideFilters;
+ PendingIntent overrideIntent;
+ String[][] overrideTechLists;
+ boolean foregroundNdefPush = mNdefPushClient.getForegroundMessage() != null;
+ synchronized (this) {
+ overrideFilters = mOverrideFilters;
+ overrideIntent = mOverrideIntent;
+ overrideTechLists = mOverrideTechLists;
+ }
+
+ // First look for dispatch overrides
+ if (overrideIntent != null) {
+ if (DBG) Log.d(TAG, "Attempting to dispatch tag with override");
+ try {
+ if (dispatchTagInternal(tag, msgs, overrideIntent, overrideFilters,
+ overrideTechLists)) {
+ if (DBG) Log.d(TAG, "Dispatched to override");
+ return true;
+ }
+ Log.w(TAG, "Dispatch override registered, but no filters matched");
+ } catch (CanceledException e) {
+ Log.w(TAG, "Dispatch overrides pending intent was canceled");
+ synchronized (this) {
+ mOverrideFilters = null;
+ mOverrideIntent = null;
+ mOverrideTechLists = null;
+ }
+ }
+ }
+
+ // If there is not foreground NDEF push setup try a normal dispatch.
+ //
+ // This is avoided when disabled in the NDEF push case to avoid the situation where each
+ // user has a different app in the foreground, causing each to launch itself on the
+ // remote device and the apps swapping which is in the foreground on each phone.
+ if (!foregroundNdefPush) {
+ try {
+ return dispatchTagInternal(tag, msgs, null, null, null);
+ } catch (CanceledException e) {
+ Log.e(TAG, "CanceledException unexpected here", e);
+ return false;
+ }
+ }
+
+ return false;
+ }
+
+ private Intent buildTagIntent(Tag tag, NdefMessage[] msgs, String action) {
+ Intent intent = new Intent(action);
+ intent.putExtra(NfcAdapter.EXTRA_TAG, tag);
+ intent.putExtra(NfcAdapter.EXTRA_ID, tag.getId());
+ intent.putExtra(NfcAdapter.EXTRA_NDEF_MESSAGES, msgs);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ return intent;
+ }
+
+ // Dispatch to either an override pending intent or a standard startActivity()
+ private boolean dispatchTagInternal(Tag tag, NdefMessage[] msgs,
+ PendingIntent overrideIntent, IntentFilter[] overrideFilters,
+ String[][] overrideTechLists)
+ throws CanceledException{
+ Intent intent;
+
+ //
+ // Try the NDEF content specific dispatch
+ //
+ if (msgs != null && msgs.length > 0) {
+ NdefMessage msg = msgs[0];
+ NdefRecord[] records = msg.getRecords();
+ if (records.length > 0) {
+ // Found valid NDEF data, try to dispatch that first
+ NdefRecord record = records[0];
+
+ intent = buildTagIntent(tag, msgs, NfcAdapter.ACTION_NDEF_DISCOVERED);
+ if (setTypeOrDataFromNdef(intent, record)) {
+ // The record contains filterable data, try to start a matching activity
+ if (startDispatchActivity(intent, overrideIntent, overrideFilters,
+ overrideTechLists)) {
+ // If an activity is found then skip further dispatching
+ return true;
+ } else {
+ if (DBG) Log.d(TAG, "No activities for NDEF handling of " + intent);
+ }
+ }
+ }
+ }
+
+ //
+ // Try the technology specific dispatch
+ //
+ String[] tagTechs = tag.getTechList();
+ Arrays.sort(tagTechs);
+
+ if (overrideIntent != null) {
+ // There are dispatch overrides in place
+ if (overrideTechLists != null) {
+ for (String[] filterTechs : overrideTechLists) {
+ if (filterMatch(tagTechs, filterTechs)) {
+ // An override matched, send it to the foreground activity.
+ intent = buildTagIntent(tag, msgs,
+ NfcAdapter.ACTION_TECH_DISCOVERED);
+ overrideIntent.send(mContext, Activity.RESULT_OK, intent);
+ return true;
+ }
+ }
+ }
+ } else {
+ // Standard tech dispatch path
+ ArrayList<ResolveInfo> matches = new ArrayList<ResolveInfo>();
+ ArrayList<ComponentInfo> registered = mTechListFilters.getComponents();
+ PackageManager pm = mContext.getPackageManager();
+
+ // Check each registered activity to see if it matches
+ for (ComponentInfo info : registered) {
+ // Don't allow wild card matching
+ if (filterMatch(tagTechs, info.techs) &&
+ isComponentEnabled(pm, info.resolveInfo)) {
+ // Add the activity as a match if it's not already in the list
+ if (!matches.contains(info.resolveInfo)) {
+ matches.add(info.resolveInfo);
+ }
+ }
+ }
+
+ if (matches.size() == 1) {
+ // Single match, launch directly
+ intent = buildTagIntent(tag, msgs, NfcAdapter.ACTION_TECH_DISCOVERED);
+ ResolveInfo info = matches.get(0);
+ intent.setClassName(info.activityInfo.packageName, info.activityInfo.name);
+ try {
+ mContext.startActivity(intent);
+ return true;
+ } catch (ActivityNotFoundException e) {
+ if (DBG) Log.w(TAG, "No activities for technology handling of " + intent);
+ }
+ } else if (matches.size() > 1) {
+ // Multiple matches, show a custom activity chooser dialog
+ intent = new Intent(mContext, TechListChooserActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.putExtra(Intent.EXTRA_INTENT,
+ buildTagIntent(tag, msgs, NfcAdapter.ACTION_TECH_DISCOVERED));
+ intent.putParcelableArrayListExtra(TechListChooserActivity.EXTRA_RESOLVE_INFOS,
+ matches);
+ try {
+ mContext.startActivity(intent);
+ return true;
+ } catch (ActivityNotFoundException e) {
+ if (DBG) Log.w(TAG, "No activities for technology handling of " + intent);
+ }
+ } else {
+ // No matches, move on
+ if (DBG) Log.w(TAG, "No activities for technology handling");
+ }
+ }
+
+ //
+ // Try the generic intent
+ //
+ intent = buildTagIntent(tag, msgs, NfcAdapter.ACTION_TAG_DISCOVERED);
+ if (startDispatchActivity(intent, overrideIntent, overrideFilters, overrideTechLists)) {
+ return true;
+ } else {
+ Log.e(TAG, "No tag fallback activity found for " + intent);
+ return false;
+ }
+ }
+
+ private boolean startDispatchActivity(Intent intent, PendingIntent overrideIntent,
+ IntentFilter[] overrideFilters, String[][] overrideTechLists)
+ throws CanceledException {
+ if (overrideIntent != null) {
+ boolean found = false;
+ if (overrideFilters == null && overrideTechLists == null) {
+ // No filters means to always dispatch regardless of match
+ found = true;
+ } else if (overrideFilters != null) {
+ for (IntentFilter filter : overrideFilters) {
+ if (filter.match(mContext.getContentResolver(), intent, false, TAG) >= 0) {
+ found = true;
+ break;
+ }
+ }
+ }
+
+ if (found) {
+ Log.i(TAG, "Dispatching to override intent " + overrideIntent);
+ overrideIntent.send(mContext, Activity.RESULT_OK, intent);
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ try {
+ // If the current app called stopAppSwitches() then our startActivity()
+ // can be delayed for several seconds. This happens with the default home
+ // screen. As a system service we can override this behavior with
+ // resumeAppSwitches()
+ mIActivityManager.resumeAppSwitches();
+ } catch (RemoteException e) { }
+ try {
+ mContext.startActivity(intent);
+ return true;
+ } catch (ActivityNotFoundException e) {
+ return false;
+ }
+ }
+ }
+
+ /** Returns true if the tech list filter matches the techs on the tag */
+ private boolean filterMatch(String[] tagTechs, String[] filterTechs) {
+ if (filterTechs == null || filterTechs.length == 0) return false;
+
+ for (String tech : filterTechs) {
+ if (Arrays.binarySearch(tagTechs, tech) < 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean setTypeOrDataFromNdef(Intent intent, NdefRecord record) {
+ short tnf = record.getTnf();
+ byte[] type = record.getType();
+ try {
+ switch (tnf) {
+ case NdefRecord.TNF_MIME_MEDIA: {
+ intent.setType(new String(type, Charsets.US_ASCII));
+ return true;
+ }
+
+ case NdefRecord.TNF_ABSOLUTE_URI: {
+ intent.setData(Uri.parse(new String(type, Charsets.UTF_8)));
+ return true;
+ }
+
+ case NdefRecord.TNF_WELL_KNOWN: {
+ byte[] payload = record.getPayload();
+ if (payload == null || payload.length == 0) return false;
+ if (Arrays.equals(type, NdefRecord.RTD_TEXT)) {
+ intent.setType("text/plain");
+ return true;
+ } else if (Arrays.equals(type, NdefRecord.RTD_SMART_POSTER)) {
+ // Parse the smart poster looking for the URI
+ try {
+ NdefMessage msg = new NdefMessage(record.getPayload());
+ for (NdefRecord subRecord : msg.getRecords()) {
+ short subTnf = subRecord.getTnf();
+ if (subTnf == NdefRecord.TNF_WELL_KNOWN
+ && Arrays.equals(subRecord.getType(),
+ NdefRecord.RTD_URI)) {
+ intent.setData(NdefRecord.parseWellKnownUriRecord(subRecord));
+ return true;
+ } else if (subTnf == NdefRecord.TNF_ABSOLUTE_URI) {
+ intent.setData(Uri.parse(new String(subRecord.getType(),
+ Charsets.UTF_8)));
+ return true;
+ }
+ }
+ } catch (FormatException e) {
+ return false;
+ }
+ } else if (Arrays.equals(type, NdefRecord.RTD_URI)) {
+ intent.setData(NdefRecord.parseWellKnownUriRecord(record));
+ return true;
+ }
+ return false;
+ }
+
+ case NdefRecord.TNF_EXTERNAL_TYPE: {
+ intent.setData(Uri.parse("vnd.android.nfc://ext/" +
+ new String(record.getType(), Charsets.US_ASCII)));
+ return true;
+ }
+ }
+ return false;
+ } catch (Exception e) {
+ Log.e(TAG, "failed to parse record", e);
+ return false;
+ }
+ }
+
+ private static boolean isComponentEnabled(PackageManager pm, ResolveInfo info) {
+ boolean enabled = false;
+ ComponentName compname = new ComponentName(
+ info.activityInfo.packageName, info.activityInfo.name);
+ try {
+ // Note that getActivityInfo() will internally call
+ // isEnabledLP() to determine whether the component
+ // enabled. If it's not, null is returned.
+ if (pm.getActivityInfo(compname,0) != null) {
+ enabled = true;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ enabled = false;
+ }
+ if (!enabled) {
+ Log.d(TAG, "Component not enabled: " + compname);
+ }
+ return enabled;
+ }
+}
diff --git a/src/com/android/nfc/NfcService.java b/src/com/android/nfc/NfcService.java
index e3a7008..c923c82 100755
--- a/src/com/android/nfc/NfcService.java
+++ b/src/com/android/nfc/NfcService.java
@@ -27,6 +27,7 @@
import android.app.ActivityManagerNative;
import android.app.Application;
import android.app.IActivityManager;
+import android.app.KeyguardManager;
import android.app.PendingIntent;
import android.app.PendingIntent.CanceledException;
import android.app.StatusBarManager;
@@ -39,6 +40,7 @@
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.content.pm.PackageManager;
import android.net.Uri;
import android.nfc.ApduList;
import android.nfc.ErrorCodes;
@@ -46,6 +48,7 @@
import android.nfc.ILlcpConnectionlessSocket;
import android.nfc.ILlcpServiceSocket;
import android.nfc.ILlcpSocket;
+import android.nfc.INdefPushCallback;
import android.nfc.INfcAdapter;
import android.nfc.INfcAdapterExtras;
import android.nfc.INfcTag;
@@ -58,6 +61,7 @@
import android.nfc.Tag;
import android.nfc.TechListParcel;
import android.nfc.TransceiveResult;
+import android.nfc.tech.TagTechnology;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
@@ -86,6 +90,7 @@
private static final String ACTION_MASTER_CLEAR_NOTIFICATION = "android.intent.action.MASTER_CLEAR_NOTIFICATION";
static final boolean DBG = false;
+ static final String TAG = "NfcService";
private static final String MY_TAG_FILE_NAME = "mytag";
private static final String SE_RESET_SCRIPT_FILE_NAME = "/system/etc/se-reset-script";
@@ -94,54 +99,8 @@
System.loadLibrary("nfc_jni");
}
- /**
- * NFC Forum "URI Record Type Definition"
- *
- * This is a mapping of "URI Identifier Codes" to URI string prefixes,
- * per section 3.2.2 of the NFC Forum URI Record Type Definition document.
- */
- private static final String[] URI_PREFIX_MAP = new String[] {
- "", // 0x00
- "http://www.", // 0x01
- "https://www.", // 0x02
- "http://", // 0x03
- "https://", // 0x04
- "tel:", // 0x05
- "mailto:", // 0x06
- "ftp://anonymous:anonymous@", // 0x07
- "ftp://ftp.", // 0x08
- "ftps://", // 0x09
- "sftp://", // 0x0A
- "smb://", // 0x0B
- "nfs://", // 0x0C
- "ftp://", // 0x0D
- "dav://", // 0x0E
- "news:", // 0x0F
- "telnet://", // 0x10
- "imap:", // 0x11
- "rtsp://", // 0x12
- "urn:", // 0x13
- "pop:", // 0x14
- "sip:", // 0x15
- "sips:", // 0x16
- "tftp:", // 0x17
- "btspp://", // 0x18
- "btl2cap://", // 0x19
- "btgoep://", // 0x1A
- "tcpobex://", // 0x1B
- "irdaobex://", // 0x1C
- "file://", // 0x1D
- "urn:epc:id:", // 0x1E
- "urn:epc:tag:", // 0x1F
- "urn:epc:pat:", // 0x20
- "urn:epc:raw:", // 0x21
- "urn:epc:", // 0x22
- };
-
public static final String SERVICE_NAME = "nfc";
- private static final String TAG = "NfcService";
-
private static final String NFC_PERM = android.Manifest.permission.NFC;
private static final String NFC_PERM_ERROR = "NFC permission required";
private static final String ADMIN_PERM = android.Manifest.permission.WRITE_SECURE_SETTINGS;
@@ -149,7 +108,7 @@
private static final String NFCEE_ADMIN_PERM = "com.android.nfc.permission.NFCEE_ADMIN";
private static final String NFCEE_ADMIN_PERM_ERROR = "NFCEE_ADMIN permission required";
- private static final String PREF = "NfcServicePrefs";
+ /*package*/ static final String PREF = "NfcServicePrefs";
private static final String PREF_NFC_ON = "nfc_on";
private static final boolean NFC_ON_DEFAULT = true;
@@ -234,10 +193,6 @@
"com.android.nfc_extras.action.AID_SELECTED";
public static final String EXTRA_AID = "com.android.nfc_extras.extra.AID";
- // Locked on mNfcAdapter
- PendingIntent mDispatchOverrideIntent;
- IntentFilter[] mDispatchOverrideFilters;
- String[][] mDispatchOverrideTechLists;
// TODO: none of these appear to be synchronized but are
// read/written from different threads (notably Binder threads)...
@@ -255,7 +210,7 @@
// fields below are used in multiple threads and protected by synchronized(this)
private final HashMap<Integer, Object> mObjectMap = new HashMap<Integer, Object>();
private final HashMap<Integer, Object> mSocketMap = new HashMap<Integer, Object>();
- private boolean mScreenOn;
+ private boolean mScreenUnlocked;
private String mSePackageName;
// fields below are final after onCreate()
@@ -264,10 +219,10 @@
private SharedPreferences mPrefs;
private SharedPreferences.Editor mPrefsEditor;
private PowerManager.WakeLock mWakeLock;
- private IActivityManager mIActivityManager;
NdefPushClient mNdefPushClient;
NdefPushServer mNdefPushServer;
- RegisteredComponentCache mTechListFilters;
+ private NfcDispatcher mNfcDispatcher;
+ private KeyguardManager mKeyguard;
private static NfcService sService;
@@ -297,27 +252,25 @@
sService = this;
mContext = this;
- mManager = new NativeNfcManager(mContext, this);
+ mManager = new NativeNfcManager(this, this);
mManager.initializeNativeStructure();
mNdefPushClient = new NdefPushClient(this);
mNdefPushServer = new NdefPushServer();
-
- mTechListFilters = new RegisteredComponentCache(this,
- NfcAdapter.ACTION_TECH_DISCOVERED, NfcAdapter.ACTION_TECH_DISCOVERED);
+ mNfcDispatcher = new NfcDispatcher(this, mNdefPushClient);
mSecureElement = new NativeNfcSecureElement();
- mPrefs = mContext.getSharedPreferences(PREF, Context.MODE_PRIVATE);
+ mPrefs = getSharedPreferences(PREF, Context.MODE_PRIVATE);
mPrefsEditor = mPrefs.edit();
mIsNfcEnabled = false; // real preference read later
- PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
- mScreenOn = pm.isScreenOn();
- mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "NfcService");
+ PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
- mIActivityManager = ActivityManagerNative.getDefault();
+ mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "NfcService");
+ mKeyguard = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
+ mScreenUnlocked = !mKeyguard.isKeyguardLocked() && !mKeyguard.isKeyguardSecure();
ServiceManager.addService(SERVICE_NAME, mNfcAdapter);
@@ -325,13 +278,14 @@
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_SCREEN_ON);
filter.addAction(ACTION_MASTER_CLEAR_NOTIFICATION);
- mContext.registerReceiver(mReceiver, filter);
+ filter.addAction(Intent.ACTION_USER_PRESENT);
+ registerReceiver(mReceiver, filter);
filter = new IntentFilter();
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addDataScheme("package");
- mContext.registerReceiver(mReceiver, filter);
+ registerReceiver(mReceiver, filter);
Thread t = new Thread() {
@Override
@@ -413,26 +367,14 @@
techLists = techListsParcel.getTechLists();
}
- synchronized (this) {
- if (mDispatchOverrideIntent != null) {
- Log.e(TAG, "Replacing active dispatch overrides");
- }
- mDispatchOverrideIntent = intent;
- mDispatchOverrideFilters = filters;
- mDispatchOverrideTechLists = techLists;
- }
+ mNfcDispatcher.enableForegroundDispatch(intent, filters, techLists);
}
@Override
public void disableForegroundDispatch(ComponentName activity) {
mContext.enforceCallingOrSelfPermission(NFC_PERM, NFC_PERM_ERROR);
- synchronized (this) {
- if (mDispatchOverrideIntent == null) {
- Log.e(TAG, "No active foreground dispatching");
- }
- mDispatchOverrideIntent = null;
- mDispatchOverrideFilters = null;
- }
+
+ mNfcDispatcher.disableForegroundDispatch();
}
@Override
@@ -447,9 +389,23 @@
}
@Override
+ public void enableForegroundNdefPushWithCallback(ComponentName activity,
+ INdefPushCallback callback) {
+ mContext.enforceCallingOrSelfPermission(NFC_PERM, NFC_PERM_ERROR);
+ if (activity == null || callback == null) {
+ throw new IllegalArgumentException();
+ }
+ if (mNdefPushClient.setForegroundCallback(callback)) {
+ 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)) {
+ boolean hadMsg = mNdefPushClient.setForegroundMessage(null);
+ boolean hadCallback = mNdefPushClient.setForegroundCallback(null);
+ if (!hadMsg || !hadCallback) {
Log.e(TAG, "No active foreground NDEF push message");
}
}
@@ -1285,6 +1241,10 @@
return ErrorCodes.ERROR_DISCONNECT;
}
+ if (technology == TagTechnology.NFC_B) {
+ return ErrorCodes.ERROR_NOT_SUPPORTED;
+ }
+
// Note that on most tags, all technologies are behind a single
// handle. This means that the connect at the lower levels
// will do nothing, as the tag is already connected to that handle.
@@ -1540,10 +1500,17 @@
}
@Override
- public void resetIsoDepTimeout() throws RemoteException {
+ public void setFelicaTimeout(int timeout) throws RemoteException {
mContext.enforceCallingOrSelfPermission(NFC_PERM, NFC_PERM_ERROR);
- mManager.resetIsoDepTimeout();
+ mManager.setFelicaTimeout(timeout);
+ }
+
+ @Override
+ public void resetTimeouts() throws RemoteException {
+ mContext.enforceCallingOrSelfPermission(NFC_PERM, NFC_PERM_ERROR);
+
+ mManager.resetTimeouts();
}
};
@@ -1830,7 +1797,7 @@
throw new SecurityException("Wrong PID");
}
- mManager.doResetIsoDepTimeout();
+ mManager.resetTimeouts();
mSecureElement.doDisconnect(mOpenEe.handle);
mOpenEe = null;
@@ -1960,11 +1927,7 @@
if (DBG) Log.d(TAG, "NFC success of deinitialize = " + isSuccess);
if (isSuccess) {
mIsNfcEnabled = false;
- // Clear out any old dispatch overrides and NDEF push message
- synchronized (this) {
- mDispatchOverrideFilters = null;
- mDispatchOverrideIntent = null;
- }
+ mNfcDispatcher.disableForegroundDispatch();
mNdefPushClient.setForegroundMessage(null);
}
@@ -1976,7 +1939,7 @@
/** apply NFC discovery and EE routing */
private synchronized void applyRouting() {
if (mIsNfcEnabled && mOpenEe == null) {
- if (mScreenOn) {
+ if (mScreenUnlocked) {
if (mEeRoutingState == ROUTE_ON_WHEN_SCREEN_ON) {
Log.d(TAG, "NFC-EE routing ON");
mManager.doSelectSecureElement(SECURE_ELEMENT_ID);
@@ -2396,7 +2359,7 @@
new Bundle[] { });
Log.d(TAG, "mock NDEF tag, starting corresponding activity");
Log.d(TAG, tag.toString());
- dispatchTag(tag, new NdefMessage[] { ndefMsg });
+ mNfcDispatcher.dispatchTag(tag, new NdefMessage[] { ndefMsg });
break;
}
@@ -2553,349 +2516,43 @@
}
}
- private Intent buildTagIntent(Tag tag, NdefMessage[] msgs, String action) {
- Intent intent = new Intent(action);
- intent.putExtra(NfcAdapter.EXTRA_TAG, tag);
- intent.putExtra(NfcAdapter.EXTRA_ID, tag.getId());
- intent.putExtra(NfcAdapter.EXTRA_NDEF_MESSAGES, msgs);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- return intent;
- }
-
private void dispatchNativeTag(NativeNfcTag nativeTag, NdefMessage[] msgs) {
Tag tag = new Tag(nativeTag.getUid(), nativeTag.getTechList(),
nativeTag.getTechExtras(), nativeTag.getHandle(), mNfcTagService);
registerTagObject(nativeTag);
- if (!dispatchTag(tag, msgs)) {
+ if (!mNfcDispatcher.dispatchTag(tag, msgs)) {
unregisterObject(nativeTag.getHandle());
nativeTag.disconnect();
}
}
-
- public byte[] concat(byte[]... arrays) {
- int length = 0;
- for (byte[] array : arrays) {
- length += array.length;
- }
- byte[] result = new byte[length];
- int pos = 0;
- for (byte[] array : arrays) {
- System.arraycopy(array, 0, result, pos, array.length);
- pos += array.length;
- }
- return result;
- }
-
- private Uri parseWellKnownUriRecord(NdefRecord record) {
- byte[] payload = record.getPayload();
-
- /*
- * payload[0] contains the URI Identifier Code, per the
- * NFC Forum "URI Record Type Definition" section 3.2.2.
- *
- * payload[1]...payload[payload.length - 1] contains the rest of
- * the URI.
- */
- String prefix = URI_PREFIX_MAP[(payload[0] & 0xff)];
- byte[] fullUri = concat(prefix.getBytes(Charsets.UTF_8),
- Arrays.copyOfRange(payload, 1, payload.length));
- return Uri.parse(new String(fullUri, Charsets.UTF_8));
- }
-
- private boolean setTypeOrDataFromNdef(Intent intent, NdefRecord record) {
- short tnf = record.getTnf();
- byte[] type = record.getType();
- try {
- switch (tnf) {
- case NdefRecord.TNF_MIME_MEDIA: {
- intent.setType(new String(type, Charsets.US_ASCII));
- return true;
- }
-
- case NdefRecord.TNF_ABSOLUTE_URI: {
- intent.setData(Uri.parse(new String(type, Charsets.UTF_8)));
- return true;
- }
-
- case NdefRecord.TNF_WELL_KNOWN: {
- byte[] payload = record.getPayload();
- if (payload == null || payload.length == 0) return false;
- if (Arrays.equals(type, NdefRecord.RTD_TEXT)) {
- intent.setType("text/plain");
- return true;
- } else if (Arrays.equals(type, NdefRecord.RTD_SMART_POSTER)) {
- // Parse the smart poster looking for the URI
- try {
- NdefMessage msg = new NdefMessage(record.getPayload());
- for (NdefRecord subRecord : msg.getRecords()) {
- short subTnf = subRecord.getTnf();
- if (subTnf == NdefRecord.TNF_WELL_KNOWN
- && Arrays.equals(subRecord.getType(),
- NdefRecord.RTD_URI)) {
- intent.setData(parseWellKnownUriRecord(subRecord));
- return true;
- } else if (subTnf == NdefRecord.TNF_ABSOLUTE_URI) {
- intent.setData(Uri.parse(new String(subRecord.getType(),
- Charsets.UTF_8)));
- return true;
- }
- }
- } catch (FormatException e) {
- return false;
- }
- } else if (Arrays.equals(type, NdefRecord.RTD_URI)) {
- intent.setData(parseWellKnownUriRecord(record));
- return true;
- }
- return false;
- }
-
- case NdefRecord.TNF_EXTERNAL_TYPE: {
- intent.setData(Uri.parse("vnd.android.nfc://ext/" +
- new String(record.getType(), Charsets.US_ASCII)));
- return true;
- }
- }
- return false;
- } catch (Exception e) {
- Log.e(TAG, "failed to parse record", e);
- return false;
- }
- }
-
- /** Returns false if no activities were found to dispatch to */
- private boolean dispatchTag(Tag tag, NdefMessage[] msgs) {
- if (DBG) {
- Log.d(TAG, "Dispatching tag");
- Log.d(TAG, tag.toString());
- }
-
- IntentFilter[] overrideFilters;
- PendingIntent overrideIntent;
- String[][] overrideTechLists;
- boolean foregroundNdefPush = mNdefPushClient.getForegroundMessage() != null;
- synchronized (mNfcAdapter) {
- overrideFilters = mDispatchOverrideFilters;
- overrideIntent = mDispatchOverrideIntent;
- overrideTechLists = mDispatchOverrideTechLists;
- }
-
- // First look for dispatch overrides
- if (overrideIntent != null) {
- if (DBG) Log.d(TAG, "Attempting to dispatch tag with override");
- try {
- if (dispatchTagInternal(tag, msgs, overrideIntent, overrideFilters,
- overrideTechLists)) {
- if (DBG) Log.d(TAG, "Dispatched to override");
- return true;
- }
- Log.w(TAG, "Dispatch override registered, but no filters matched");
- } catch (CanceledException e) {
- Log.w(TAG, "Dispatch overrides pending intent was canceled");
- synchronized (mNfcAdapter) {
- mDispatchOverrideFilters = null;
- mDispatchOverrideIntent = null;
- mDispatchOverrideTechLists = null;
- }
- }
- }
-
- // If there is not foreground NDEF push setup try a normal dispatch.
- //
- // This is avoided when disabled in the NDEF push case to avoid the situation where each
- // user has a different app in the foreground, causing each to launch itself on the
- // remote device and the apps swapping which is in the foreground on each phone.
- if (!foregroundNdefPush) {
- try {
- return dispatchTagInternal(tag, msgs, null, null, null);
- } catch (CanceledException e) {
- Log.e(TAG, "CanceledException unexpected here", e);
- return false;
- }
- }
-
- return false;
- }
-
- /** Returns true if the tech list filter matches the techs on the tag */
- private boolean filterMatch(String[] tagTechs, String[] filterTechs) {
- if (filterTechs == null || filterTechs.length == 0) return false;
-
- for (String tech : filterTechs) {
- if (Arrays.binarySearch(tagTechs, tech) < 0) {
- return false;
- }
- }
- return true;
- }
-
- // Dispatch to either an override pending intent or a standard startActivity()
- private boolean dispatchTagInternal(Tag tag, NdefMessage[] msgs,
- PendingIntent overrideIntent, IntentFilter[] overrideFilters,
- String[][] overrideTechLists)
- throws CanceledException{
- Intent intent;
-
- //
- // Try the NDEF content specific dispatch
- //
- if (msgs != null && msgs.length > 0) {
- NdefMessage msg = msgs[0];
- NdefRecord[] records = msg.getRecords();
- if (records.length > 0) {
- // Found valid NDEF data, try to dispatch that first
- NdefRecord record = records[0];
-
- intent = buildTagIntent(tag, msgs, NfcAdapter.ACTION_NDEF_DISCOVERED);
- if (setTypeOrDataFromNdef(intent, record)) {
- // The record contains filterable data, try to start a matching activity
- if (startDispatchActivity(intent, overrideIntent, overrideFilters,
- overrideTechLists)) {
- // If an activity is found then skip further dispatching
- return true;
- } else {
- if (DBG) Log.d(TAG, "No activities for NDEF handling of " + intent);
- }
- }
- }
- }
-
- //
- // Try the technology specific dispatch
- //
- String[] tagTechs = tag.getTechList();
- Arrays.sort(tagTechs);
-
- if (overrideIntent != null) {
- // There are dispatch overrides in place
- if (overrideTechLists != null) {
- for (String[] filterTechs : overrideTechLists) {
- if (filterMatch(tagTechs, filterTechs)) {
- // An override matched, send it to the foreground activity.
- intent = buildTagIntent(tag, msgs,
- NfcAdapter.ACTION_TECH_DISCOVERED);
- overrideIntent.send(mContext, Activity.RESULT_OK, intent);
- return true;
- }
- }
- }
- } else {
- // Standard tech dispatch path
- ArrayList<ResolveInfo> matches = new ArrayList<ResolveInfo>();
- ArrayList<ComponentInfo> registered = mTechListFilters.getComponents();
-
- // Check each registered activity to see if it matches
- for (ComponentInfo info : registered) {
- // Don't allow wild card matching
- if (filterMatch(tagTechs, info.techs)) {
- // Add the activity as a match if it's not already in the list
- if (!matches.contains(info.resolveInfo)) {
- matches.add(info.resolveInfo);
- }
- }
- }
-
- if (matches.size() == 1) {
- // Single match, launch directly
- intent = buildTagIntent(tag, msgs, NfcAdapter.ACTION_TECH_DISCOVERED);
- ResolveInfo info = matches.get(0);
- intent.setClassName(info.activityInfo.packageName, info.activityInfo.name);
- try {
- mContext.startActivity(intent);
- return true;
- } catch (ActivityNotFoundException e) {
- if (DBG) Log.w(TAG, "No activities for technology handling of " + intent);
- }
- } else if (matches.size() > 1) {
- // Multiple matches, show a custom activity chooser dialog
- intent = new Intent(mContext, TechListChooserActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.putExtra(Intent.EXTRA_INTENT,
- buildTagIntent(tag, msgs, NfcAdapter.ACTION_TECH_DISCOVERED));
- intent.putParcelableArrayListExtra(TechListChooserActivity.EXTRA_RESOLVE_INFOS,
- matches);
- try {
- mContext.startActivity(intent);
- return true;
- } catch (ActivityNotFoundException e) {
- if (DBG) Log.w(TAG, "No activities for technology handling of " + intent);
- }
- } else {
- // No matches, move on
- if (DBG) Log.w(TAG, "No activities for technology handling");
- }
- }
-
- //
- // Try the generic intent
- //
- intent = buildTagIntent(tag, msgs, NfcAdapter.ACTION_TAG_DISCOVERED);
- if (startDispatchActivity(intent, overrideIntent, overrideFilters, overrideTechLists)) {
- return true;
- } else {
- Log.e(TAG, "No tag fallback activity found for " + intent);
- return false;
- }
- }
-
- private boolean startDispatchActivity(Intent intent, PendingIntent overrideIntent,
- IntentFilter[] overrideFilters, String[][] overrideTechLists)
- throws CanceledException {
- if (overrideIntent != null) {
- boolean found = false;
- if (overrideFilters == null && overrideTechLists == null) {
- // No filters means to always dispatch regardless of match
- found = true;
- } else if (overrideFilters != null) {
- for (IntentFilter filter : overrideFilters) {
- if (filter.match(mContext.getContentResolver(), intent, false, TAG) >= 0) {
- found = true;
- break;
- }
- }
- }
-
- if (found) {
- Log.i(TAG, "Dispatching to override intent " + overrideIntent);
- overrideIntent.send(mContext, Activity.RESULT_OK, intent);
- return true;
- } else {
- return false;
- }
- } else {
- try {
- // If the current app called stopAppSwitches() then our startActivity()
- // can be delayed for several seconds. This happens with the default home
- // screen. As a system service we can override this behavior with
- // resumeAppSwitches()
- mIActivityManager.resumeAppSwitches();
- } catch (RemoteException e) { }
- try {
- mContext.startActivity(intent);
- return true;
- } catch (ActivityNotFoundException e) {
- return false;
- }
- }
- }
}
private NfcServiceHandler mHandler = new NfcServiceHandler();
private class EnableDisableDiscoveryTask extends AsyncTask<Boolean, Void, Void> {
@Override
- protected Void doInBackground(Boolean... enable) {
- if (enable != null && enable.length > 0 && enable[0]) {
+ protected Void doInBackground(Boolean... params) {
+ if (DBG) Log.d(TAG, "EnableDisableDiscoveryTask: enable = " + params[0]);
+
+ if (params != null && params.length > 0 && params[0]) {
synchronized (NfcService.this) {
- mScreenOn = true;
- applyRouting();
+ if (!mScreenUnlocked) {
+ mScreenUnlocked = true;
+ applyRouting();
+ } else {
+ if (DBG) Log.d(TAG, "Ignoring enable request");
+ }
}
} else {
mWakeLock.acquire();
synchronized (NfcService.this) {
- mScreenOn = false;
- applyRouting();
- maybeDisconnectTarget();
+ if (mScreenUnlocked) {
+ mScreenUnlocked = false;
+ applyRouting();
+ maybeDisconnectTarget();
+ } else {
+ if (DBG) Log.d(TAG, "Ignoring disable request");
+ }
}
mWakeLock.release();
}
@@ -2918,13 +2575,22 @@
// NFC stack wedges. This is *not* the correct way to fix this issue -
// configuration of the local NFC adapter should be very quick and should
// be safe on the main thread, and the NFC stack should not wedge.
- new EnableDisableDiscoveryTask().execute(Boolean.TRUE);
+
+ // Only enable if the screen is unlocked. If the screen is locked
+ // Intent.ACTION_USER_PRESENT will be broadcast when the screen is
+ // unlocked.
+ boolean enable = !mKeyguard.isKeyguardLocked() && !mKeyguard.isKeyguardSecure();
+
+ new EnableDisableDiscoveryTask().execute(enable);
} else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
// Perform discovery disable in thread to protect against ANR when the
// NFC stack wedges. This is *not* the correct way to fix this issue -
// configuration of the local NFC adapter should be very quick and should
// be safe on the main thread, and the NFC stack should not wedge.
- new EnableDisableDiscoveryTask().execute(new Boolean(false));
+ new EnableDisableDiscoveryTask().execute(Boolean.FALSE);
+ } else if (intent.getAction().equals(Intent.ACTION_USER_PRESENT)) {
+ // The user has unlocked the screen. Enabled!
+ new EnableDisableDiscoveryTask().execute(Boolean.TRUE);
} else if (intent.getAction().equals(ACTION_MASTER_CLEAR_NOTIFICATION)) {
executeSeReset();
} else if (intent.getAction().equals(Intent.ACTION_PACKAGE_REMOVED)) {
diff --git a/src/com/android/nfc/ndefpush/NdefPushClient.java b/src/com/android/nfc/ndefpush/NdefPushClient.java
index 2a61696..46e101f 100755
--- a/src/com/android/nfc/ndefpush/NdefPushClient.java
+++ b/src/com/android/nfc/ndefpush/NdefPushClient.java
@@ -24,9 +24,11 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.nfc.INdefPushCallback;
import android.nfc.NdefMessage;
import android.nfc.NfcAdapter;
import android.os.AsyncTask;
+import android.os.RemoteException;
import android.util.Log;
import java.io.IOException;
@@ -43,6 +45,7 @@
/** Locked on MyTagClient.class */
NdefMessage mForegroundMsg;
+ INdefPushCallback mCallback;
public NdefPushClient(Context context) {
context.registerReceiver(this, new IntentFilter(NfcAdapter.ACTION_LLCP_LINK_STATE_CHANGED));
@@ -56,6 +59,14 @@
}
}
+ public boolean setForegroundCallback(INdefPushCallback callback) {
+ synchronized (this) {
+ boolean set = mCallback != null;
+ mCallback = callback;
+ return set;
+ }
+ }
+
public NdefMessage getForegroundMessage() {
synchronized (this) {
return mForegroundMsg;
@@ -78,6 +89,19 @@
foregroundMsg = mForegroundMsg;
}
+ INdefPushCallback callback;
+ synchronized (this) {
+ callback = mCallback;
+ }
+
+ if (callback != null) {
+ try {
+ foregroundMsg = callback.onConnect();
+ } catch (RemoteException e) {
+ // Ignore
+ }
+ }
+
NfcAdapter adapter = NfcAdapter.getDefaultAdapter(context);
NdefMessage myTag = adapter.getLocalNdefMessage();
@@ -140,6 +164,20 @@
}
}
}
+
+ INdefPushCallback callback;
+ synchronized (this) {
+ callback = mCallback;
+ }
+
+ if (callback != null) {
+ try {
+ callback.onMessagePushed();
+ } catch (RemoteException e) {
+ // Ignore
+ }
+ }
+
return null;
}
}