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;
         }
     }