Merge "Allow foreground override in provision mode."
diff --git a/nci/jni/Android.mk b/nci/jni/Android.mk
index 1e33ca6..e386861 100644
--- a/nci/jni/Android.mk
+++ b/nci/jni/Android.mk
@@ -10,7 +10,7 @@
 LOCAL_CFLAGS += -DNCI_VERSION=$(NCI_VERSION) -O0 -g
 endif
 
-LOCAL_CFLAGS += -Wall -Wextra
+LOCAL_CFLAGS += -Wall -Wextra -Wno-unused-parameter -Werror
 
 LOCAL_SRC_FILES := $(call all-subdir-cpp-files) $(call all-subdir-c-files)
 
diff --git a/nci/jni/NativeLlcpConnectionlessSocket.cpp b/nci/jni/NativeLlcpConnectionlessSocket.cpp
index fb0b96d..b877acb 100644
--- a/nci/jni/NativeLlcpConnectionlessSocket.cpp
+++ b/nci/jni/NativeLlcpConnectionlessSocket.cpp
@@ -74,7 +74,7 @@
     }
     size_t byte_count = bytes.size();
 
-    ALOGD("NFA_P2pSendUI: len = %d", byte_count);
+    ALOGD("NFA_P2pSendUI: len = %zu", byte_count);
     UINT8* raw_ptr = const_cast<UINT8*>(reinterpret_cast<const UINT8*>(&bytes[0])); // TODO: API bug; NFA_P2pSendUI should take const*!
     tNFA_STATUS status = NFA_P2pSendUI((tNFA_HANDLE) handle, nsap, byte_count, raw_ptr);
 
diff --git a/nci/jni/NativeNfcManager.cpp b/nci/jni/NativeNfcManager.cpp
index e42ddb5..4f0b427 100755
--- a/nci/jni/NativeNfcManager.cpp
+++ b/nci/jni/NativeNfcManager.cpp
@@ -109,10 +109,6 @@
 namespace android
 {
 static jint                 sLastError = ERROR_BUFFER_TOO_SMALL;
-static jmethodID            sCachedNfcManagerNotifySeApduReceived;
-static jmethodID            sCachedNfcManagerNotifySeMifareAccess;
-static jmethodID            sCachedNfcManagerNotifySeEmvCardRemoval;
-static jmethodID            sCachedNfcManagerNotifyTargetDeselected;
 static SyncEvent            sNfaEnableEvent;  //event for NFA_Enable()
 static SyncEvent            sNfaDisableEvent;  //event for NFA_Disable()
 static SyncEvent            sNfaEnableDisablePollingEvent;  //event for NFA_EnablePolling(), NFA_DisablePolling()
@@ -128,6 +124,8 @@
 static bool                 sP2pEnabled = false;
 static bool                 sP2pActive = false; // whether p2p was last active
 static bool                 sAbortConnlessWait = false;
+static jint                 sLfT3tMax = 0;
+
 #define CONFIG_UPDATE_TECH_MASK     (1 << 1)
 #define DEFAULT_TECH_MASK           (NFA_TECHNOLOGY_MASK_A \
                                      | NFA_TECHNOLOGY_MASK_B \
@@ -575,13 +573,13 @@
             "notifyLlcpLinkFirstPacketReceived", "(Lcom/android/nfc/dhimpl/NativeP2pDevice;)V");
 
     gCachedNfcManagerNotifyHostEmuActivated = e->GetMethodID(cls.get(),
-            "notifyHostEmuActivated", "()V");
+            "notifyHostEmuActivated", "(I)V");
 
     gCachedNfcManagerNotifyHostEmuData = e->GetMethodID(cls.get(),
-            "notifyHostEmuData", "([B)V");
+            "notifyHostEmuData", "(I[B)V");
 
     gCachedNfcManagerNotifyHostEmuDeactivated = e->GetMethodID(cls.get(),
-            "notifyHostEmuDeactivated", "()V");
+            "notifyHostEmuDeactivated", "(I)V");
 
     gCachedNfcManagerNotifyRfFieldActivated = e->GetMethodID(cls.get(),
             "notifyRfFieldActivated", "()V");
@@ -830,6 +828,74 @@
 
 /*******************************************************************************
 **
+** Function:        nfcManager_doRegisterT3tIdentifier
+**
+** Description:     Registers LF_T3T_IDENTIFIER for NFC-F.
+**                  e: JVM environment.
+**                  o: Java object.
+**                  t3tIdentifier: LF_T3T_IDENTIFIER value (10 or 18 bytes)
+**
+** Returns:         Handle retrieve from RoutingManager.
+**
+*******************************************************************************/
+static jint nfcManager_doRegisterT3tIdentifier(JNIEnv* e, jobject, jbyteArray t3tIdentifier)
+{
+    ALOGD ("%s: enter", __FUNCTION__);
+
+    ScopedByteArrayRO bytes(e, t3tIdentifier);
+    uint8_t* buf = const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(&bytes[0]));
+    size_t bufLen = bytes.size();
+    int handle = RoutingManager::getInstance().registerT3tIdentifier(buf, bufLen);
+
+    ALOGD ("%s: handle=%d", __FUNCTION__, handle);
+    ALOGD ("%s: exit", __FUNCTION__);
+
+    return handle;
+}
+
+/*******************************************************************************
+**
+** Function:        nfcManager_doDeregisterT3tIdentifier
+**
+** Description:     Deregisters LF_T3T_IDENTIFIER for NFC-F.
+**                  e: JVM environment.
+**                  o: Java object.
+**                  handle: Handle retrieve from libnfc-nci.
+**
+** Returns:         None
+**
+*******************************************************************************/
+static void nfcManager_doDeregisterT3tIdentifier(JNIEnv*, jobject, jint handle)
+{
+    ALOGD ("%s: enter; handle=%d", __FUNCTION__, handle);
+
+    RoutingManager::getInstance().deregisterT3tIdentifier(handle);
+
+    ALOGD ("%s: exit", __FUNCTION__);
+}
+
+/*******************************************************************************
+**
+** Function:        nfcManager_getLfT3tMax
+**
+** Description:     Returns LF_T3T_MAX value.
+**                  e: JVM environment.
+**                  o: Java object.
+**
+** Returns:         LF_T3T_MAX value.
+**
+*******************************************************************************/
+static jint nfcManager_getLfT3tMax(JNIEnv*, jobject)
+{
+    ALOGD ("%s: enter", __FUNCTION__);
+    ALOGD ("LF_T3T_MAX=%d", sLfT3tMax);
+    ALOGD ("%s: exit", __FUNCTION__);
+
+    return sLfT3tMax;
+}
+
+/*******************************************************************************
+**
 ** Function:        nfcManager_doInitialize
 **
 ** Description:     Turn on NFC.
@@ -915,6 +981,21 @@
 
                 NFA_SetRfDiscoveryDuration(nat->discovery_duration);
 
+                // get LF_T3T_MAX
+                {
+                    SyncEventGuard guard (sNfaGetConfigEvent);
+                    tNFA_PMID configParam[1] = {NCI_PARAM_ID_LF_T3T_MAX};
+                    stat = NFA_GetConfig(1, configParam);
+                    if (stat == NFA_STATUS_OK)
+                    {
+                        sNfaGetConfigEvent.wait ();
+                        if (sCurrentConfigLen >= 4 || sConfig[1] == NCI_PARAM_ID_LF_T3T_MAX) {
+                            ALOGD("%s: lfT3tMax=%d", __FUNCTION__, sConfig[3]);
+                            sLfT3tMax = sConfig[3];
+                        }
+                    }
+                }
+
                 // Do custom NFCA startup configuration.
                 doStartupConfig();
                 goto TheEnd;
@@ -972,8 +1053,6 @@
         return;
     }
 
-    tNFA_STATUS stat = NFA_STATUS_OK;
-
     PowerSwitch::getInstance ().setLevel (PowerSwitch::FULL_POWER);
 
     if (sRfEnabled) {
@@ -1271,6 +1350,7 @@
     sIsDisabling = false;
     sP2pEnabled = false;
     gActivated = false;
+    sLfT3tMax = 0;
 
     {
         //unblock NFA_EnablePolling() and NFA_DisablePolling()
@@ -1650,6 +1730,15 @@
     {"commitRouting", "()Z",
             (void*) nfcManager_commitRouting},
 
+    {"doRegisterT3tIdentifier", "([B)I",
+            (void*) nfcManager_doRegisterT3tIdentifier},
+
+    {"doDeregisterT3tIdentifier", "(I)V",
+            (void*) nfcManager_doDeregisterT3tIdentifier},
+
+    {"getLfT3tMax", "()I",
+            (void*) nfcManager_getLfT3tMax},
+
     {"doEnableDiscovery", "(IZZZZZ)V",
             (void*) nfcManager_enableDiscovery},
 
diff --git a/nci/jni/NativeNfcTag.cpp b/nci/jni/NativeNfcTag.cpp
index a25c3d7..5ebc561 100755
--- a/nci/jni/NativeNfcTag.cpp
+++ b/nci/jni/NativeNfcTag.cpp
@@ -994,7 +994,7 @@
             break;
         }
 
-        ALOGD ("%s: response %d bytes", __FUNCTION__, sRxDataBuffer.size());
+        ALOGD ("%s: response %zu bytes", __FUNCTION__, sRxDataBuffer.size());
 
         if ((natTag.getProtocol () == NFA_PROTOCOL_T2T) &&
             natTag.isT2tNackResponse (sRxDataBuffer.data(), sRxDataBuffer.size()))
diff --git a/nci/jni/PeerToPeer.cpp b/nci/jni/PeerToPeer.cpp
index da4cf30..8074f69 100644
--- a/nci/jni/PeerToPeer.cpp
+++ b/nci/jni/PeerToPeer.cpp
@@ -1709,7 +1709,7 @@
 
     if (maxInfoUnit > (int)LLCP_MIU)
     {
-        ALOGD ("%s: overriding the miu passed by the app(%d) with stack miu(%d)", fn, maxInfoUnit, LLCP_MIU);
+        ALOGD ("%s: overriding the miu passed by the app(%d) with stack miu(%zu)", fn, maxInfoUnit, LLCP_MIU);
         maxInfoUnit = LLCP_MIU;
     }
 
diff --git a/nci/jni/RouteDataSet.cpp b/nci/jni/RouteDataSet.cpp
index 9935d88..bb259b3 100644
--- a/nci/jni/RouteDataSet.cpp
+++ b/nci/jni/RouteDataSet.cpp
@@ -140,7 +140,7 @@
 void RouteDataSet::deleteDatabase ()
 {
     static const char fn [] = "RouteDataSet::deleteDatabase";
-    ALOGD ("%s: default db size=%u; sec elem db size=%u", fn, mDefaultRouteDatabase.size(), mSecElemRouteDatabase.size());
+    ALOGD ("%s: default db size=%zu; sec elem db size=%zu", fn, mDefaultRouteDatabase.size(), mSecElemRouteDatabase.size());
     Database::iterator it;
 
     for (it = mDefaultRouteDatabase.begin(); it != mDefaultRouteDatabase.end(); it++)
@@ -266,7 +266,7 @@
     actualWritten = fwrite (routesXml, sizeof(char), strlen(routesXml), fh);
     retval = actualWritten == strlen(routesXml);
     fclose (fh);
-    ALOGD ("%s: wrote %u bytes", fn, actualWritten);
+    ALOGD ("%s: wrote %zu bytes", fn, actualWritten);
     if (retval == false)
         ALOGE ("%s: error during write", fn);
 
@@ -313,7 +313,7 @@
         routesXml.append (buffer, actual);
     }
     fclose (fh);
-    ALOGD ("%s: read %u bytes", fn, routesXml.length());
+    ALOGD ("%s: read %zu bytes", fn, routesXml.length());
     return true;
 }
 
diff --git a/nci/jni/RoutingManager.cpp b/nci/jni/RoutingManager.cpp
old mode 100644
new mode 100755
index c0827ca..6a8a452
--- a/nci/jni/RoutingManager.cpp
+++ b/nci/jni/RoutingManager.cpp
@@ -54,6 +54,13 @@
     else
         mActiveSe = 0x00;
 
+    // Get the active SE for Nfc-F
+    if (GetNumValue("ACTIVE_SE_NFCF", &num, sizeof(num)))
+        mActiveSeNfcF = num;
+    else
+        mActiveSeNfcF = 0x00;
+    ALOGD("%s: Active SE for Nfc-F is 0x%02X", fn, mActiveSeNfcF);
+
     // Get the "default" route
     if (GetNumValue("DEFAULT_ISODEP_ROUTE", &num, sizeof(num)))
         mDefaultEe = num;
@@ -61,6 +68,13 @@
         mDefaultEe = 0x00;
     ALOGD("%s: default route is 0x%02X", fn, mDefaultEe);
 
+    // Get the "default" route for Nfc-F
+    if (GetNumValue("DEFAULT_NFCF_ROUTE", &num, sizeof(num)))
+        mDefaultEeNfcF = num;
+    else
+        mDefaultEeNfcF = 0x00;
+    ALOGD("%s: default route for Nfc-F is 0x%02X", fn, mDefaultEeNfcF);
+
     // Get the default "off-host" route.  This is hard-coded at the Java layer
     // but we can override it here to avoid forcing Java changes.
     if (GetNumValue("DEFAULT_OFFHOST_ROUTE", &num, sizeof(num)))
@@ -78,6 +92,8 @@
     memset (&mEeInfo, 0, sizeof(mEeInfo));
     mReceivedEeInfo = false;
     mSeTechMask = 0x00;
+
+    mNfcFOnDhHandle = NFA_HANDLE_INVALID;
 }
 
 RoutingManager::~RoutingManager ()
@@ -105,7 +121,9 @@
 
     mRxDataBuffer.clear ();
 
-    if (mActiveSe != 0) {
+    if ((mActiveSe != 0) || (mActiveSeNfcF != 0))
+    {
+        ALOGD ("%s: Technology Routing (NfcASe:0x%02x, NfcFSe:0x%02x)", fn, mActiveSe, mActiveSeNfcF);
         {
             // Wait for EE info if needed
             SyncEventGuard guard (mEeInfoEvent);
@@ -115,31 +133,46 @@
                 mEeInfoEvent.wait();
             }
         }
+
+        ALOGD ("%s: Number of EE is %d", fn, mEeInfo.num_ee);
         for (UINT8 i = 0; i < mEeInfo.num_ee; i++)
         {
-             ALOGD ("%s   EE[%u] Handle: 0x%04x  techA: 0x%02x  techB: 0x%02x  techF: 0x%02x  techBprime: 0x%02x",
-                fn, i, mEeInfo.ee_disc_info[i].ee_handle,
-                mEeInfo.ee_disc_info[i].la_protocol,
-                mEeInfo.ee_disc_info[i].lb_protocol,
-                mEeInfo.ee_disc_info[i].lf_protocol,
-                mEeInfo.ee_disc_info[i].lbp_protocol);
-             if (mEeInfo.ee_disc_info[i].ee_handle == (mActiveSe | NFA_HANDLE_GROUP_EE))
-             {
-                 if (mEeInfo.ee_disc_info[i].la_protocol != 0) mSeTechMask |= NFA_TECHNOLOGY_MASK_A;
+            tNFA_HANDLE eeHandle = mEeInfo.ee_disc_info[i].ee_handle;
+            tNFA_TECHNOLOGY_MASK seTechMask = 0;
 
-                 if (mSeTechMask != 0x00)
-                 {
-                     ALOGD("Configuring tech mask 0x%02x on EE 0x%04x", mSeTechMask, mEeInfo.ee_disc_info[i].ee_handle);
-                     nfaStat = NFA_CeConfigureUiccListenTech(mEeInfo.ee_disc_info[i].ee_handle, mSeTechMask);
-                     if (nfaStat != NFA_STATUS_OK)
-                         ALOGE ("Failed to configure UICC listen technologies.");
-                     // Set technology routes to UICC if it's there
-                     nfaStat = NFA_EeSetDefaultTechRouting(mEeInfo.ee_disc_info[i].ee_handle, mSeTechMask, mSeTechMask,
-                             mSeTechMask);
-                     if (nfaStat != NFA_STATUS_OK)
-                         ALOGE ("Failed to configure UICC technology routing.");
-                 }
-             }
+            ALOGD ("%s   EE[%u] Handle: 0x%04x  techA: 0x%02x  techB: 0x%02x  techF: 0x%02x  techBprime: 0x%02x",
+                   fn, i, eeHandle,
+                   mEeInfo.ee_disc_info[i].la_protocol,
+                   mEeInfo.ee_disc_info[i].lb_protocol,
+                   mEeInfo.ee_disc_info[i].lf_protocol,
+                   mEeInfo.ee_disc_info[i].lbp_protocol);
+            if ((mActiveSe != 0) && (eeHandle == (mActiveSe | NFA_HANDLE_GROUP_EE)))
+            {
+                if (mEeInfo.ee_disc_info[i].la_protocol != 0)
+                    seTechMask |= NFA_TECHNOLOGY_MASK_A;
+            }
+            if ((mActiveSeNfcF != 0) && (eeHandle == (mActiveSeNfcF | NFA_HANDLE_GROUP_EE)))
+            {
+                if (mEeInfo.ee_disc_info[i].lf_protocol != 0)
+                    seTechMask |= NFA_TECHNOLOGY_MASK_F;
+            }
+
+            ALOGD ("%s: seTechMask[%u]=0x%02x", fn, i, seTechMask);
+            if (seTechMask != 0x00)
+            {
+                ALOGD("Configuring tech mask 0x%02x on EE 0x%04x", seTechMask, eeHandle);
+
+                nfaStat = NFA_CeConfigureUiccListenTech(eeHandle, seTechMask);
+                if (nfaStat != NFA_STATUS_OK)
+                    ALOGE ("Failed to configure UICC listen technologies.");
+
+                // Set technology routes to UICC if it's there
+                nfaStat = NFA_EeSetDefaultTechRouting(eeHandle, seTechMask, seTechMask, seTechMask);
+                if (nfaStat != NFA_STATUS_OK)
+                    ALOGE ("Failed to configure UICC technology routing.");
+
+                mSeTechMask |= seTechMask;
+            }
         }
     }
 
@@ -165,51 +198,130 @@
 void RoutingManager::enableRoutingToHost()
 {
     tNFA_STATUS nfaStat;
+    tNFA_TECHNOLOGY_MASK techMask;
+    tNFA_PROTOCOL_MASK protoMask;
+    SyncEventGuard guard (mRoutingEvent);
 
+    // Set default routing at one time when the NFCEE IDs for Nfc-A and Nfc-F are same
+    if (mDefaultEe == mDefaultEeNfcF)
     {
-        SyncEventGuard guard (mRoutingEvent);
-
-        // Route Nfc-A to host if we don't have a SE
-        if (mSeTechMask == 0)
+        // Route Nfc-A/Nfc-F to host if we don't have a SE
+        techMask = (mSeTechMask ^ (NFA_TECHNOLOGY_MASK_A | NFA_TECHNOLOGY_MASK_F));
+        if (techMask != 0)
         {
-            nfaStat = NFA_EeSetDefaultTechRouting (mDefaultEe, NFA_TECHNOLOGY_MASK_A, 0, 0);
+            nfaStat = NFA_EeSetDefaultTechRouting (mDefaultEe, techMask, 0, 0);
             if (nfaStat == NFA_STATUS_OK)
                 mRoutingEvent.wait ();
             else
-                ALOGE ("Fail to set default tech routing");
+                ALOGE ("Fail to set default tech routing for Nfc-A/Nfc-F");
         }
-
-        // Default routing for IsoDep protocol
-        nfaStat = NFA_EeSetDefaultProtoRouting(mDefaultEe, NFA_PROTOCOL_MASK_ISO_DEP, 0, 0);
+        // Default routing for IsoDep and T3T protocol
+        protoMask = (NFA_PROTOCOL_MASK_ISO_DEP | NFA_PROTOCOL_MASK_T3T);
+        nfaStat = NFA_EeSetDefaultProtoRouting(mDefaultEe, protoMask, 0, 0);
         if (nfaStat == NFA_STATUS_OK)
             mRoutingEvent.wait ();
         else
-            ALOGE ("Fail to set default proto routing");
+            ALOGE ("Fail to set default proto routing for IsoDep and T3T");
+    }
+    else
+    {
+        // Route Nfc-A to host if we don't have a SE
+        techMask = NFA_TECHNOLOGY_MASK_A;
+        if ((mSeTechMask & NFA_TECHNOLOGY_MASK_A) == 0)
+        {
+            nfaStat = NFA_EeSetDefaultTechRouting (mDefaultEe, techMask, 0, 0);
+            if (nfaStat == NFA_STATUS_OK)
+                mRoutingEvent.wait ();
+            else
+                ALOGE ("Fail to set default tech routing for Nfc-A");
+        }
+        // Default routing for IsoDep protocol
+        protoMask = NFA_PROTOCOL_MASK_ISO_DEP;
+        nfaStat = NFA_EeSetDefaultProtoRouting(mDefaultEe, protoMask, 0, 0);
+        if (nfaStat == NFA_STATUS_OK)
+            mRoutingEvent.wait ();
+        else
+            ALOGE ("Fail to set default proto routing for IsoDep");
+
+        // Route Nfc-F to host if we don't have a SE
+        techMask = NFA_TECHNOLOGY_MASK_F;
+        if ((mSeTechMask & NFA_TECHNOLOGY_MASK_F) == 0)
+        {
+            nfaStat = NFA_EeSetDefaultTechRouting (mDefaultEeNfcF, techMask, 0, 0);
+            if (nfaStat == NFA_STATUS_OK)
+                mRoutingEvent.wait ();
+            else
+                ALOGE ("Fail to set default tech routing for Nfc-F");
+        }
+        // Default routing for T3T protocol
+        protoMask = NFA_PROTOCOL_MASK_T3T;
+        nfaStat = NFA_EeSetDefaultProtoRouting(mDefaultEeNfcF, protoMask, 0, 0);
+        if (nfaStat == NFA_STATUS_OK)
+            mRoutingEvent.wait ();
+        else
+            ALOGE ("Fail to set default proto routing for T3T");
     }
 }
 
 void RoutingManager::disableRoutingToHost()
 {
     tNFA_STATUS nfaStat;
+    tNFA_TECHNOLOGY_MASK techMask;
+    SyncEventGuard guard (mRoutingEvent);
 
+    // Set default routing at one time when the NFCEE IDs for Nfc-A and Nfc-F are same
+    if (mDefaultEe == mDefaultEeNfcF)
     {
-        SyncEventGuard guard (mRoutingEvent);
-        // Default routing for NFC-A technology if we don't have a SE
-        if (mSeTechMask == 0)
+        // Default routing for Nfc-A/Nfc-F technology if we don't have a SE
+        techMask = (mSeTechMask ^ (NFA_TECHNOLOGY_MASK_A | NFA_TECHNOLOGY_MASK_F));
+        if (techMask != 0)
         {
             nfaStat = NFA_EeSetDefaultTechRouting (mDefaultEe, 0, 0, 0);
             if (nfaStat == NFA_STATUS_OK)
                 mRoutingEvent.wait ();
             else
-                ALOGE ("Fail to set default tech routing");
+                ALOGE ("Fail to set default tech routing for Nfc-A/Nfc-F");
         }
-
+        // Default routing for IsoDep and T3T protocol
+        nfaStat = NFA_EeSetDefaultProtoRouting(mDefaultEe, 0, 0, 0);
+        if (nfaStat == NFA_STATUS_OK)
+            mRoutingEvent.wait ();
+        else
+            ALOGE ("Fail to set default proto routing for IsoDep and T3T");
+    }
+    else
+    {
+        // Default routing for Nfc-A technology if we don't have a SE
+        if ((mSeTechMask & NFA_TECHNOLOGY_MASK_A) == 0)
+        {
+            nfaStat = NFA_EeSetDefaultTechRouting (mDefaultEe, 0, 0, 0);
+            if (nfaStat == NFA_STATUS_OK)
+                mRoutingEvent.wait ();
+            else
+                ALOGE ("Fail to set default tech routing for Nfc-A");
+        }
         // Default routing for IsoDep protocol
         nfaStat = NFA_EeSetDefaultProtoRouting(mDefaultEe, 0, 0, 0);
         if (nfaStat == NFA_STATUS_OK)
             mRoutingEvent.wait ();
         else
-            ALOGE ("Fail to set default proto routing");
+            ALOGE ("Fail to set default proto routing for IsoDep");
+
+        // Default routing for Nfc-F technology if we don't have a SE
+        if ((mSeTechMask & NFA_TECHNOLOGY_MASK_F) == 0)
+        {
+            nfaStat = NFA_EeSetDefaultTechRouting (mDefaultEeNfcF, 0, 0, 0);
+            if (nfaStat == NFA_STATUS_OK)
+                mRoutingEvent.wait ();
+            else
+                ALOGE ("Fail to set default tech routing for Nfc-F");
+        }
+        // Default routing for T3T protocol
+        nfaStat = NFA_EeSetDefaultProtoRouting(mDefaultEeNfcF, 0, 0, 0);
+        if (nfaStat == NFA_STATUS_OK)
+            mRoutingEvent.wait ();
+        else
+            ALOGE ("Fail to set default proto routing for T3T");
     }
 }
 
@@ -303,7 +415,7 @@
     }
 }
 
-void RoutingManager::notifyActivated ()
+void RoutingManager::notifyActivated (UINT8 technology)
 {
     JNIEnv* e = NULL;
     ScopedAttach attach(mNativeData->vm, &e);
@@ -313,7 +425,7 @@
         return;
     }
 
-    e->CallVoidMethod (mNativeData->manager, android::gCachedNfcManagerNotifyHostEmuActivated);
+    e->CallVoidMethod (mNativeData->manager, android::gCachedNfcManagerNotifyHostEmuActivated, (int)technology);
     if (e->ExceptionCheck())
     {
         e->ExceptionClear();
@@ -321,7 +433,7 @@
     }
 }
 
-void RoutingManager::notifyDeactivated ()
+void RoutingManager::notifyDeactivated (UINT8 technology)
 {
     mRxDataBuffer.clear();
     JNIEnv* e = NULL;
@@ -332,7 +444,7 @@
         return;
     }
 
-    e->CallVoidMethod (mNativeData->manager, android::gCachedNfcManagerNotifyHostEmuDeactivated);
+    e->CallVoidMethod (mNativeData->manager, android::gCachedNfcManagerNotifyHostEmuDeactivated, (int)technology);
     if (e->ExceptionCheck())
     {
         e->ExceptionClear();
@@ -340,7 +452,7 @@
     }
 }
 
-void RoutingManager::handleData (const UINT8* data, UINT32 dataLen, tNFA_STATUS status)
+void RoutingManager::handleData (UINT8 technology, const UINT8* data, UINT32 dataLen, tNFA_STATUS status)
 {
     if (dataLen <= 0)
     {
@@ -389,7 +501,8 @@
             goto TheEnd;
         }
 
-        e->CallVoidMethod (mNativeData->manager, android::gCachedNfcManagerNotifyHostEmuData, dataJavaArray.get());
+        e->CallVoidMethod (mNativeData->manager, android::gCachedNfcManagerNotifyHostEmuData,
+                (int)technology, dataJavaArray.get());
         if (e->ExceptionCheck())
         {
             e->ExceptionClear();
@@ -424,7 +537,7 @@
 
     case NFA_CE_ACTIVATED_EVT:
         {
-            routingManager.notifyActivated();
+            routingManager.notifyActivated(NFA_TECHNOLOGY_MASK_A);
         }
         break;
 
@@ -432,7 +545,7 @@
     case NFA_CE_DEACTIVATED_EVT:
         {
             ALOGD("%s: NFA_DEACTIVATED_EVT, NFA_CE_DEACTIVATED_EVT", fn);
-            routingManager.notifyDeactivated();
+            routingManager.notifyDeactivated(NFA_TECHNOLOGY_MASK_A);
             SyncEventGuard g (gDeactivatedEvent);
             gActivated = false; //guard this variable from multi-threaded access
             gDeactivatedEvent.notifyOne ();
@@ -443,7 +556,7 @@
         {
             tNFA_CE_DATA& ce_data = eventData->ce_data;
             ALOGD("%s: NFA_CE_DATA_EVT; stat=0x%X; h=0x%X; data len=%u", fn, ce_data.status, ce_data.handle, ce_data.len);
-            getInstance().handleData(ce_data.p_data, ce_data.len, ce_data.status);
+            getInstance().handleData(NFA_TECHNOLOGY_MASK_A, ce_data.p_data, ce_data.len, ce_data.status);
         }
         break;
     }
@@ -568,6 +681,113 @@
     }
 }
 
+int RoutingManager::registerT3tIdentifier(UINT8* t3tId, UINT8 t3tIdLen)
+{
+    static const char fn [] = "RoutingManager::registerT3tIdentifier";
+
+    ALOGD ("%s: Start to register NFC-F system on DH", fn);
+
+    if (t3tIdLen != (2 + NCI_RF_F_UID_LEN))
+    {
+        ALOGE ("%s: Invalid length of T3T Identifier", fn);
+        return NFA_HANDLE_INVALID;
+    }
+
+    SyncEventGuard guard (mRoutingEvent);
+    mNfcFOnDhHandle = NFA_HANDLE_INVALID;
+
+    int systemCode;
+    UINT8 nfcid2[NCI_RF_F_UID_LEN];
+
+    systemCode = (((int)t3tId[0] << 8) | ((int)t3tId[1] << 0));
+    memcpy(nfcid2, t3tId + 2, NCI_RF_F_UID_LEN);
+
+    tNFA_STATUS nfaStat = NFA_CeRegisterFelicaSystemCodeOnDH (systemCode, nfcid2, nfcFCeCallback);
+    if (nfaStat == NFA_STATUS_OK)
+    {
+        mRoutingEvent.wait ();
+    }
+    else
+    {
+        ALOGE ("%s: Fail to register NFC-F system on DH", fn);
+        return NFA_HANDLE_INVALID;
+    }
+
+    ALOGD ("%s: Succeed to register NFC-F system on DH", fn);
+
+    return mNfcFOnDhHandle;
+}
+
+void RoutingManager::deregisterT3tIdentifier(int handle)
+{
+    static const char fn [] = "RoutingManager::deregisterT3tIdentifier";
+
+    ALOGD ("%s: Start to deregister NFC-F system on DH", fn);
+
+    SyncEventGuard guard (mRoutingEvent);
+    tNFA_STATUS nfaStat = NFA_CeDeregisterFelicaSystemCodeOnDH (handle);
+    if (nfaStat == NFA_STATUS_OK)
+    {
+        mRoutingEvent.wait ();
+        ALOGD ("%s: Succeeded in deregistering NFC-F system on DH", fn);
+    }
+    else
+    {
+        ALOGE ("%s: Fail to deregister NFC-F system on DH", fn);
+    }
+}
+
+void RoutingManager::nfcFCeCallback (UINT8 event, tNFA_CONN_EVT_DATA* eventData)
+{
+    static const char fn [] = "RoutingManager::nfcFCeCallback";
+    RoutingManager& routingManager = RoutingManager::getInstance();
+
+    ALOGD("%s: 0x%x", __FUNCTION__, event);
+
+    switch (event)
+    {
+    case NFA_CE_REGISTERED_EVT:
+        {
+            ALOGD ("%s: registerd event notified", fn);
+            routingManager.mNfcFOnDhHandle = eventData->ce_registered.handle;
+            SyncEventGuard guard(routingManager.mRoutingEvent);
+            routingManager.mRoutingEvent.notifyOne();
+        }
+        break;
+    case NFA_CE_DEREGISTERED_EVT:
+        {
+            ALOGD ("%s: deregisterd event notified", fn);
+            SyncEventGuard guard(routingManager.mRoutingEvent);
+            routingManager.mRoutingEvent.notifyOne();
+        }
+        break;
+    case NFA_CE_ACTIVATED_EVT:
+        {
+            ALOGD ("%s: activated event notified", fn);
+            routingManager.notifyActivated(NFA_TECHNOLOGY_MASK_F);
+        }
+        break;
+    case NFA_CE_DEACTIVATED_EVT:
+        {
+            ALOGD ("%s: deactivated event notified", fn);
+            routingManager.notifyDeactivated(NFA_TECHNOLOGY_MASK_F);
+        }
+        break;
+    case NFA_CE_DATA_EVT:
+        {
+            ALOGD ("%s: data event notified", fn);
+            tNFA_CE_DATA& ce_data = eventData->ce_data;
+            routingManager.handleData(NFA_TECHNOLOGY_MASK_F, ce_data.p_data, ce_data.len, ce_data.status);
+        }
+        break;
+    default:
+        {
+            ALOGE ("%s: unknown event=%u ????", fn, event);
+        }
+        break;
+    }
+}
+
 int RoutingManager::registerJniFunctions (JNIEnv* e)
 {
     static const char fn [] = "RoutingManager::registerJniFunctions";
diff --git a/nci/jni/RoutingManager.h b/nci/jni/RoutingManager.h
old mode 100644
new mode 100755
index ea04066..f6d4639
--- a/nci/jni/RoutingManager.h
+++ b/nci/jni/RoutingManager.h
@@ -38,6 +38,8 @@
     bool addAidRouting(const UINT8* aid, UINT8 aidLen, int route);
     bool removeAidRouting(const UINT8* aid, UINT8 aidLen);
     bool commitRouting();
+    int registerT3tIdentifier(UINT8* t3tId, UINT8 t3tIdLen);
+    void deregisterT3tIdentifier(int handle);
     void onNfccShutdown();
     int registerJniFunctions (JNIEnv* e);
 private:
@@ -46,9 +48,9 @@
     RoutingManager(const RoutingManager&);
     RoutingManager& operator=(const RoutingManager&);
 
-    void handleData (const UINT8* data, UINT32 dataLen, tNFA_STATUS status);
-    void notifyActivated ();
-    void notifyDeactivated ();
+    void handleData (UINT8 technology, const UINT8* data, UINT32 dataLen, tNFA_STATUS status);
+    void notifyActivated (UINT8 technology);
+    void notifyDeactivated (UINT8 technology);
 
     // See AidRoutingManager.java for corresponding
     // AID_MATCHING_ constants
@@ -62,6 +64,8 @@
 
     static void nfaEeCallback (tNFA_EE_EVT event, tNFA_EE_CBACK_DATA* eventData);
     static void stackCallback (UINT8 event, tNFA_CONN_EVT_DATA* eventData);
+    static void nfcFCeCallback (UINT8 event, tNFA_CONN_EVT_DATA* eventData);
+
     static int com_android_nfc_cardemulation_doGetDefaultRouteDestination (JNIEnv* e);
     static int com_android_nfc_cardemulation_doGetDefaultOffHostRouteDestination (JNIEnv* e);
     static int com_android_nfc_cardemulation_doGetAidMatchingMode (JNIEnv* e);
@@ -71,9 +75,12 @@
     // Fields below are final after initialize()
     nfc_jni_native_data* mNativeData;
     int mDefaultEe;
+    int mDefaultEeNfcF;
     int mOffHostEe;
     int mActiveSe;
+    int mActiveSeNfcF;
     int mAidMatchingMode;
+    int mNfcFOnDhHandle;
     bool mReceivedEeInfo;
     tNFA_EE_DISCOVER_REQ mEeInfo;
     tNFA_TECHNOLOGY_MASK mSeTechMask;
diff --git a/nci/jni/extns/pn54x/src/mifare/phFriNfc_MifareStdMap.c b/nci/jni/extns/pn54x/src/mifare/phFriNfc_MifareStdMap.c
index a2291fc..66da24b 100755
--- a/nci/jni/extns/pn54x/src/mifare/phFriNfc_MifareStdMap.c
+++ b/nci/jni/extns/pn54x/src/mifare/phFriNfc_MifareStdMap.c
@@ -2796,8 +2796,8 @@
                             + PH_FRINFC_MIFARESTD_VAL2):
                             PH_FRINFC_MIFARESTD_VAL0);
 
-            if(( NdefMap->SendRecvBuf[TempLength] ==
-                        PH_FRINFC_MIFARESTD_VAL0))
+            if(NdefMap->SendRecvBuf[TempLength] ==
+                        PH_FRINFC_MIFARESTD_VAL0)
             {
                 Result = PHNFCSTVAL(CID_FRI_NFC_NDEF_MAP,
                             NFCSTATUS_INVALID_PARAMETER);
@@ -3687,8 +3687,8 @@
                 {
                     NdefMap->StdMifareContainer.NFCforumSectFlag =
                         (((NdefMap->StdMifareContainer.currentBlock == 64) &&
-                        (NdefMap->CardType == PH_FRINFC_NDEFMAP_MIFARE_STD_4K_CARD)||
-                        (NdefMap->CardType == PH_FRINFC_NDEFMAP_MIFARE_STD_2K_CARD))?
+                        ((NdefMap->CardType == PH_FRINFC_NDEFMAP_MIFARE_STD_4K_CARD) ||
+                        (NdefMap->CardType == PH_FRINFC_NDEFMAP_MIFARE_STD_2K_CARD))) ?
                         NdefMap->StdMifareContainer.NFCforumSectFlag:
                                             PH_FRINFC_MIFARESTD_FLAG1);
                 }
diff --git a/nci/jni/extns/pn54x/src/mifare/phNxpExtns_MifareStd.c b/nci/jni/extns/pn54x/src/mifare/phNxpExtns_MifareStd.c
index c0c3bc1..6f58e08 100755
--- a/nci/jni/extns/pn54x/src/mifare/phNxpExtns_MifareStd.c
+++ b/nci/jni/extns/pn54x/src/mifare/phNxpExtns_MifareStd.c
@@ -1124,7 +1124,7 @@
     else if( ((p_data[0] == phNfc_eMifareTransfer) || (p_data[0] == phNfc_eMifareRestore)) && (len == 2) )
     {
         NdefMap->Cmd.MfCmd = p_data[0];
-        if ((p_data[0] == phNfc_eMifareRestore))
+        if (p_data[0] == phNfc_eMifareRestore)
         {
             EXTNS_SetCallBackFlag(FALSE);
             gphNxpExtns_Context.RawWriteCallBack = TRUE;
@@ -2101,7 +2101,7 @@
         status = phLibNfc_SendIncDecCmd(pTransceiveInfo, &tNciTranscvInfo, Cmd.MfCmd);
 
     }
-    else if( (Cmd.MfCmd == phNfc_eMifareRestore ) )
+    else if(Cmd.MfCmd == phNfc_eMifareRestore)
     {
         pTransceiveInfo->addr = SendRecvBuf[i++];
         length = SendLength - i;
@@ -2116,7 +2116,7 @@
     }
     else if ((Cmd.MfCmd == phNfc_eMifareRaw) || (Cmd.MfCmd == phNfc_eMifareTransfer ))
     {
-        pTransceiveInfo->cmd.MfCmd = phNciNfc_eT2TRaw;
+        pTransceiveInfo->cmd.MfCmd = (phNfc_eMifareCmdList_t) phNciNfc_eT2TRaw;
         memcpy(pTransceiveInfo->sSendData.buffer, SendRecvBuf, length);
         pTransceiveInfo->sSendData.length = length;
         pTransceiveInfo->sRecvData.length = MAX_BUFF_SIZE;
diff --git a/nci/jni/extns/pn54x/src/phNxpExtns.c b/nci/jni/extns/pn54x/src/phNxpExtns.c
index f39342c..478e5f4 100755
--- a/nci/jni/extns/pn54x/src/phNxpExtns.c
+++ b/nci/jni/extns/pn54x/src/phNxpExtns.c
@@ -627,6 +627,8 @@
     if (sem_timedwait (&gAuthCmdBuf.semPresenceCheck, &ts))
     {
         ALOGE ("%s: failed to wait (errno=0x%08x)", __FUNCTION__, errno);
+        sem_destroy (&gAuthCmdBuf.semPresenceCheck);
+        gAuthCmdBuf.auth_sent = FALSE;
         return NFCSTATUS_FAILED;
     }
     if (sem_destroy (&gAuthCmdBuf.semPresenceCheck))
diff --git a/nci/src/com/android/nfc/dhimpl/NativeNfcManager.java b/nci/src/com/android/nfc/dhimpl/NativeNfcManager.java
index 99ee5d5..1ea5b2a 100755
--- a/nci/src/com/android/nfc/dhimpl/NativeNfcManager.java
+++ b/nci/src/com/android/nfc/dhimpl/NativeNfcManager.java
@@ -26,6 +26,10 @@
 import com.android.nfc.LlcpException;
 import com.android.nfc.NfcDiscoveryParameters;
 
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.HashMap;
+
 /**
  * Native interface to the NFC Manager functions
  */
@@ -42,13 +46,14 @@
         System.loadLibrary("nfc_nci_jni");
     }
 
-
     /* Native structure */
     private long mNative;
 
     private final DeviceHostListener mListener;
     private final Context mContext;
 
+    private final Object mLock = new Object();
+    private final HashMap<Integer, byte[]> mT3tIdentifiers = new HashMap<Integer, byte[]>();
 
     public NativeNfcManager(Context context, DeviceHostListener listener) {
         mListener = listener;
@@ -98,6 +103,46 @@
     @Override
     public native boolean commitRouting();
 
+    public native int doRegisterT3tIdentifier(byte[] t3tIdentifier);
+
+    @Override
+    public void registerT3tIdentifier(byte[] t3tIdentifier) {
+        synchronized (mLock) {
+            int handle = doRegisterT3tIdentifier(t3tIdentifier);
+            if (handle != 0xffff) {
+                mT3tIdentifiers.put(Integer.valueOf(handle), t3tIdentifier);
+            }
+        }
+    }
+
+    public native void doDeregisterT3tIdentifier(int handle);
+
+    @Override
+    public void deregisterT3tIdentifier(byte[] t3tIdentifier) {
+        synchronized (mLock) {
+            Iterator<Integer> it = mT3tIdentifiers.keySet().iterator();
+            while (it.hasNext()) {
+                int handle = it.next().intValue();
+                byte[] value = mT3tIdentifiers.get(handle);
+                if (Arrays.equals(value, t3tIdentifier)) {
+                    doDeregisterT3tIdentifier(handle);
+                    mT3tIdentifiers.remove(handle);
+                    break;
+                }
+            }
+        }
+    }
+
+    @Override
+    public void clearT3tIdentifiersCache() {
+        synchronized (mLock) {
+            mT3tIdentifiers.clear();
+        }
+    }
+
+    @Override
+    public native int getLfT3tMax();
+
     private native void doEnableDiscovery(int techMask,
                                           boolean enableLowPowerPolling,
                                           boolean enableReaderMode,
@@ -326,16 +371,16 @@
         mListener.onLlcpFirstPacketReceived(device);
     }
 
-    private void notifyHostEmuActivated() {
-        mListener.onHostCardEmulationActivated();
+    private void notifyHostEmuActivated(int technology) {
+        mListener.onHostCardEmulationActivated(technology);
     }
 
-    private void notifyHostEmuData(byte[] data) {
-        mListener.onHostCardEmulationData(data);
+    private void notifyHostEmuData(int technology, byte[] data) {
+        mListener.onHostCardEmulationData(technology, data);
     }
 
-    private void notifyHostEmuDeactivated() {
-        mListener.onHostCardEmulationDeactivated();
+    private void notifyHostEmuDeactivated(int technology) {
+        mListener.onHostCardEmulationDeactivated(technology);
     }
 
     private void notifyRfFieldActivated() {
diff --git a/nxp/src/com/android/nfc/dhimpl/NativeNfcManager.java b/nxp/src/com/android/nfc/dhimpl/NativeNfcManager.java
index 805e1ea..60a08a2 100755
--- a/nxp/src/com/android/nfc/dhimpl/NativeNfcManager.java
+++ b/nxp/src/com/android/nfc/dhimpl/NativeNfcManager.java
@@ -135,29 +135,45 @@
     }
 
     @Override
-    public boolean sendRawFrame(byte[] data)
-    {
+    public boolean sendRawFrame(byte[] data) {
         return false;
     }
 
     @Override
-    public boolean routeAid(byte[] aid, int route)
-    {
+    public boolean routeAid(byte[] aid, int route) {
         return false;
     }
 
     @Override
-    public boolean unrouteAid(byte[] aid)
-    {
+    public boolean unrouteAid(byte[] aid) {
        return false;
     }
 
     @Override
-    public boolean commitRouting()
-    {
+    public boolean commitRouting() {
         return false;
     }
 
+    @Override
+    public void registerT3tIdentifier(byte[] t3tIdentifier) {
+        return;
+    }
+
+    @Override
+    public void deregisterT3tIdentifier(byte[] t3tIdentifier) {
+        return;
+    }
+
+    @Override
+    public void clearT3tIdentifiersCache() {
+        return;
+    }
+
+    @Override
+    public int getLfT3tMax() {
+        return 0;
+    }
+
     private native void doEnableDiscovery(int techMask,
                                           boolean enableLowPowerPolling,
                                           boolean enableReaderMode,
@@ -375,6 +391,18 @@
         mListener.onLlcpLinkDeactivated(device);
     }
 
+    private void notifyHostEmuActivated(int technology) {
+        mListener.onHostCardEmulationActivated(technology);
+    }
+
+    private void notifyHostEmuData(int technology, byte[] data) {
+        mListener.onHostCardEmulationData(technology, data);
+    }
+
+    private void notifyHostEmuDeactivated(int technology) {
+        mListener.onHostCardEmulationDeactivated(technology);
+    }
+
     private void notifyRfFieldActivated() {
         mListener.onRemoteFieldActivated();
     }
diff --git a/res/values-az-rAZ/provisioning.xml b/res/values-az-rAZ/provisioning.xml
new file mode 100644
index 0000000..676cad9
--- /dev/null
+++ b/res/values-az-rAZ/provisioning.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 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.
+ -->
+
+<!--  NFC resources that may need to be customized
+     for different hardware or product builds.  -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+  <string-array name="provisioning_mime_types">
+    <item msgid="3675637130021393293">"application/com.android.managedprovisioning"</item>
+    <item msgid="9182300251635024136">"application/com.android.managedprovisioning.v2"</item>
+  </string-array>
+</resources>
diff --git a/res/values-az-rAZ/strings.xml b/res/values-az-rAZ/strings.xml
index b1071ae..0fc6031 100644
--- a/res/values-az-rAZ/strings.xml
+++ b/res/values-az-rAZ/strings.xml
@@ -18,15 +18,32 @@
     <string name="beam_touch_to_view" msgid="7853129156831642630">"Görüntüləmək üçün toxunun"</string>
     <string name="beam_handover_not_supported" msgid="4083165921751489015">"Qəbul edənin cihazı beam vasitəsilə böyük fayl ötürülməsini dəstəkləmir."</string>
     <string name="beam_try_again" msgid="3364677301009783455">"Cihazları yenidən bir yerə gətir"</string>
-    <string name="connecting_headset" msgid="3929250919225573008">"Qoşulur..."</string>
-    <string name="connected_headset" msgid="4047751837023241955">"Qoşulu"</string>
-    <string name="connect_headset_failed" msgid="7500801585498094863">"Qoşula bilmədi"</string>
-    <string name="disconnecting_headset" msgid="868262189044372780">"Əlaqə kəsilir"</string>
-    <string name="disconnected_headset" msgid="4066109452701733916">"Əlaqə kəsilib"</string>
-    <string name="pairing_headset" msgid="6443461444755646137">"Qoşalama"</string>
-    <string name="pairing_headset_failed" msgid="6509629663883514688">"Qoşalaya bilmədi"</string>
+    <string name="beam_busy" msgid="5253335587620612576">"Beam hazırda məşğuldur. Əvvəlki ötürmə tamamlandıqda yenidən cəhd edin."</string>
+    <string name="device" msgid="4459621591392478151">"cihaz"</string>
+    <string name="connecting_peripheral" msgid="1296182660525660935">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> cihazına qoşulur"</string>
+    <string name="connected_peripheral" msgid="20748648543160091">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> cihazına qoşulub"</string>
+    <string name="connect_peripheral_failed" msgid="7925702596242839275">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> cihazına qoşula bilmədi"</string>
+    <string name="disconnecting_peripheral" msgid="1443699384809097200">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> cihazından ayrılır"</string>
+    <string name="disconnected_peripheral" msgid="4470578100296504366">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> cihazından ayrıldı"</string>
+    <string name="pairing_peripheral" msgid="6983626861540899365">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> cütləşir"</string>
+    <string name="pairing_peripheral_failed" msgid="6087643307743264679">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ilə cütləşə bilmir"</string>
     <string name="failed_to_enable_bt" msgid="7229153323594758077">"Bluetooth aktiv edilə bilmədi"</string>
     <string name="confirm_pairing" msgid="4112568077038265363">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> Bluetooth cihazını qoşalamaq istədiyinizdən əminsiniz?"</string>
     <string name="pair_yes" msgid="3525614878559994448">"Bəli"</string>
     <string name="pair_no" msgid="5022308368904055020">"Xeyr"</string>
+    <string name="tap_again_to_pay" msgid="5754988005412859897">"<xliff:g id="APP">%1$s</xliff:g> Tətbiqi ilə ödəmək üçün yenidən tıklayın"</string>
+    <string name="tap_again_to_complete" msgid="5423640945118279123">"<xliff:g id="APP">%1$s</xliff:g> Tətbiqi ilə tamamlamaq üçün tıklayın"</string>
+    <string name="transaction_failure" msgid="7828102078637936513">"Bu əməliyyat <xliff:g id="APP">%1$s</xliff:g> tətbiqi ilə tamamlana bilməz."</string>
+    <string name="could_not_use_app" msgid="8137587876138569083">"<xliff:g id="APP">%1$s</xliff:g> Tətbiqi istifadə edə bilmədi."</string>
+    <string name="pay_with" msgid="5531545488795798945">"İlə ödə"</string>
+    <string name="complete_with" msgid="6797459104103012992">"İlə tamamla"</string>
+    <string name="default_pay_app_removed" msgid="4108250545457437360">"Tıkla və ödə üçün tərcih etdiyiniz xidmət silindi. Digərisi seçilsin?"</string>
+    <string name="ask_nfc_tap" msgid="2925239870458286340">"Tamamlamaq üçün digər cihaza tıklayın"</string>
+    <string name="wifi_connect" msgid="6250727951843550671">"Qoşul"</string>
+    <string name="status_unable_to_connect" msgid="9183908200295307657">"Şəbəkəyə qoşulmaq mümkün deyil"</string>
+    <string name="status_wifi_connected" msgid="5893022897732105739">"Qoşuldu"</string>
+    <string name="title_connect_to_network" msgid="2474034615817280146">"Şəbəkəyə qoşulun"</string>
+    <string name="prompt_connect_to_network" msgid="8511683573657516114">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> şəbəkəsinə birləşdirilsin?"</string>
+    <string name="beam_requires_nfc_enabled" msgid="2800366967218600534">"Android Beam aktivləşmək üçün NFC tələb edir. Aktivləşdirmək istəyirsiniz?"</string>
+    <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
 </resources>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 2a58467..ce34a07 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -25,10 +25,10 @@
     <string name="connect_peripheral_failed" msgid="7925702596242839275">"No s\'ha pogut connectar <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
     <string name="disconnecting_peripheral" msgid="1443699384809097200">"S\'està desconnectant <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
     <string name="disconnected_peripheral" msgid="4470578100296504366">"S\'ha desconnectat <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
-    <string name="pairing_peripheral" msgid="6983626861540899365">"S\'està emparellant <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
-    <string name="pairing_peripheral_failed" msgid="6087643307743264679">"No s\'ha pogut emparellar <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+    <string name="pairing_peripheral" msgid="6983626861540899365">"S\'està vinculant <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+    <string name="pairing_peripheral_failed" msgid="6087643307743264679">"No s\'ha pogut vincular <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
     <string name="failed_to_enable_bt" msgid="7229153323594758077">"No es pot activar el Bluetooth"</string>
-    <string name="confirm_pairing" msgid="4112568077038265363">"Estàs segur que vols emparellar el dispositiu Bluetooth <xliff:g id="DEVICE_NAME">%1$s</xliff:g>?"</string>
+    <string name="confirm_pairing" msgid="4112568077038265363">"Estàs segur que vols vincular el dispositiu Bluetooth <xliff:g id="DEVICE_NAME">%1$s</xliff:g>?"</string>
     <string name="pair_yes" msgid="3525614878559994448">"Sí"</string>
     <string name="pair_no" msgid="5022308368904055020">"No"</string>
     <string name="tap_again_to_pay" msgid="5754988005412859897">"Torna a tocar per pagar amb <xliff:g id="APP">%1$s</xliff:g>"</string>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 0b35ed8..430c162 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -37,7 +37,7 @@
     <string name="could_not_use_app" msgid="8137587876138569083">"Aplikaci <xliff:g id="APP">%1$s</xliff:g> nelze použít."</string>
     <string name="pay_with" msgid="5531545488795798945">"Platební metoda"</string>
     <string name="complete_with" msgid="6797459104103012992">"Dokončit pomocí"</string>
-    <string name="default_pay_app_removed" msgid="4108250545457437360">"Preferovaná služba pro platbu mobilem byla odstraněna. Chcete vybrat jinou?"</string>
+    <string name="default_pay_app_removed" msgid="4108250545457437360">"Preferovaná služba pro platbu dotykem telefonu byla odstraněna. Chcete vybrat jinou?"</string>
     <string name="ask_nfc_tap" msgid="2925239870458286340">"Akci dokončíte tím, že se zařízením dotknete jiného zařízení"</string>
     <string name="wifi_connect" msgid="6250727951843550671">"Připojit"</string>
     <string name="status_unable_to_connect" msgid="9183908200295307657">"K síti se nelze připojit."</string>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index beb17b7..2d0821e 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -6,7 +6,7 @@
     <string name="inbound_me_profile_title" msgid="6146013785225412693">"Kontakt per NFC erhalten"</string>
     <string name="inbound_me_profile_text" msgid="2342757196108092923">"Zum Hinzufügen dieser Person als Kontakt tippen"</string>
     <string name="outbound_me_profile_title" msgid="2523625031572784769">"NFC-Interaktion abgeschlossen"</string>
-    <string name="outbound_me_profile_text" msgid="5594998841143667989">"Zum Weitergeben Ihrer Kontaktinformationen an diese Person tippen"</string>
+    <string name="outbound_me_profile_text" msgid="5594998841143667989">"Zum Weitergeben deiner Kontaktinformationen an diese Person tippen"</string>
     <string name="accessibility_nfc_enabled" msgid="7796246979948787735">"NFC aktiviert"</string>
     <string name="touch" msgid="4727218133711188355">"Zum Beamen berühren"</string>
     <string name="beam_progress" msgid="7453634884807323920">"Beam wird empfangen..."</string>
@@ -18,7 +18,7 @@
     <string name="beam_touch_to_view" msgid="7853129156831642630">"Zum Anzeigen berühren"</string>
     <string name="beam_handover_not_supported" msgid="4083165921751489015">"Das Gerät des Empfängers unterstützt die Übertragung großer Dateien durch Beamen nicht."</string>
     <string name="beam_try_again" msgid="3364677301009783455">"Geräte erneut nebeneinander platzieren"</string>
-    <string name="beam_busy" msgid="5253335587620612576">"Android Beam ist momentan ausgelastet. Bitte versuchen Sie es erneut, wenn die vorherige Übertragung abgeschlossen ist."</string>
+    <string name="beam_busy" msgid="5253335587620612576">"Android Beam ist momentan ausgelastet. Bitte versuche es erneut, wenn die vorherige Übertragung abgeschlossen ist."</string>
     <string name="device" msgid="4459621591392478151">"Gerät"</string>
     <string name="connecting_peripheral" msgid="1296182660525660935">"Verbindung zu <xliff:g id="DEVICE_NAME">%1$s</xliff:g> wird hergestellt"</string>
     <string name="connected_peripheral" msgid="20748648543160091">"Mit <xliff:g id="DEVICE_NAME">%1$s</xliff:g> verbunden"</string>
@@ -28,22 +28,22 @@
     <string name="pairing_peripheral" msgid="6983626861540899365">"Pairing mit <xliff:g id="DEVICE_NAME">%1$s</xliff:g> wird durchgeführt"</string>
     <string name="pairing_peripheral_failed" msgid="6087643307743264679">"Pairing mit <xliff:g id="DEVICE_NAME">%1$s</xliff:g> nicht möglich"</string>
     <string name="failed_to_enable_bt" msgid="7229153323594758077">"Bluetooth konnte nicht aktiviert werden."</string>
-    <string name="confirm_pairing" msgid="4112568077038265363">"Möchten Sie das Bluetooth-Gerät <xliff:g id="DEVICE_NAME">%1$s</xliff:g> wirklich koppeln?"</string>
+    <string name="confirm_pairing" msgid="4112568077038265363">"Möchtest du das Bluetooth-Gerät <xliff:g id="DEVICE_NAME">%1$s</xliff:g> wirklich koppeln?"</string>
     <string name="pair_yes" msgid="3525614878559994448">"Ja"</string>
     <string name="pair_no" msgid="5022308368904055020">"Nein"</string>
-    <string name="tap_again_to_pay" msgid="5754988005412859897">"Halten Sie Ihr Gerät erneut ans Terminal, um mit <xliff:g id="APP">%1$s</xliff:g> zu bezahlen."</string>
-    <string name="tap_again_to_complete" msgid="5423640945118279123">"Halten Sie Ihr Gerät erneut ans Terminal, um den Vorgang mit <xliff:g id="APP">%1$s</xliff:g> durchzuführen."</string>
+    <string name="tap_again_to_pay" msgid="5754988005412859897">"Halte dein Gerät erneut ans Terminal, um mit <xliff:g id="APP">%1$s</xliff:g> zu bezahlen."</string>
+    <string name="tap_again_to_complete" msgid="5423640945118279123">"Halte dein Gerät erneut ans Terminal, um den Vorgang mit <xliff:g id="APP">%1$s</xliff:g> durchzuführen."</string>
     <string name="transaction_failure" msgid="7828102078637936513">"Diese Transaktion konnte nicht mit <xliff:g id="APP">%1$s</xliff:g> durchgeführt werden."</string>
     <string name="could_not_use_app" msgid="8137587876138569083">"Fehler bei der Verwendung von <xliff:g id="APP">%1$s</xliff:g>"</string>
     <string name="pay_with" msgid="5531545488795798945">"Zahlen mit"</string>
     <string name="complete_with" msgid="6797459104103012992">"Durchführen mit"</string>
-    <string name="default_pay_app_removed" msgid="4108250545457437360">"Der von Ihnen bevorzugte Dienst für das mobile Bezahlen wurde entfernt. Möchten Sie einen anderen auswählen?"</string>
+    <string name="default_pay_app_removed" msgid="4108250545457437360">"Der von dir bevorzugte Dienst für das mobile Bezahlen wurde entfernt. Möchtest du einen anderen auswählen?"</string>
     <string name="ask_nfc_tap" msgid="2925239870458286340">"Zum Abschluss auf ein anderes Gerät tippen"</string>
     <string name="wifi_connect" msgid="6250727951843550671">"Verbinden"</string>
     <string name="status_unable_to_connect" msgid="9183908200295307657">"Verbindung zum Netzwerk konnte nicht hergestellt werden."</string>
     <string name="status_wifi_connected" msgid="5893022897732105739">"Verbunden"</string>
     <string name="title_connect_to_network" msgid="2474034615817280146">"Mit Netzwerk verbinden"</string>
     <string name="prompt_connect_to_network" msgid="8511683573657516114">"Verbindung zum Netzwerk <xliff:g id="NETWORK_SSID">%1$s</xliff:g> herstellen?"</string>
-    <string name="beam_requires_nfc_enabled" msgid="2800366967218600534">"Android Beam erfordert zur Aktivierung die Nahfeldkommunikation (NFC). Möchten Sie sie aktivieren?"</string>
+    <string name="beam_requires_nfc_enabled" msgid="2800366967218600534">"Android Beam erfordert zur Aktivierung die Nahfeldkommunikation (NFC). Möchtest du sie aktivieren?"</string>
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
 </resources>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index a3fe0e7..e995f4b 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -26,11 +26,11 @@
     <string name="disconnecting_peripheral" msgid="1443699384809097200">"در حال قطع ارتباط <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="disconnected_peripheral" msgid="4470578100296504366">"ارتباط <xliff:g id="DEVICE_NAME">%1$s</xliff:g> قطع شد"</string>
     <string name="pairing_peripheral" msgid="6983626861540899365">"در حال مرتبط‌سازی <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="pairing_peripheral_failed" msgid="6087643307743264679">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> مرتبط نشد"</string>
+    <string name="pairing_peripheral_failed" msgid="6087643307743264679">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> مرتبط‌سازی نشد"</string>
     <string name="failed_to_enable_bt" msgid="7229153323594758077">"فعال کردن بلوتوث امکانپذیر نیست"</string>
-    <string name="confirm_pairing" msgid="4112568077038265363">"آیا مطمئن هستید که می‌خواهید دستگاه بلوتوث <xliff:g id="DEVICE_NAME">%1$s</xliff:g> را جفت کنید؟"</string>
+    <string name="confirm_pairing" msgid="4112568077038265363">"آیا مطمئنید که می‌خواهید با دستگاه بلوتوث <xliff:g id="DEVICE_NAME">%1$s</xliff:g> مرتبط‌سازی کنید؟"</string>
     <string name="pair_yes" msgid="3525614878559994448">"بله"</string>
-    <string name="pair_no" msgid="5022308368904055020">"خیر"</string>
+    <string name="pair_no" msgid="5022308368904055020">"نه"</string>
     <string name="tap_again_to_pay" msgid="5754988005412859897">"برای پرداخت با <xliff:g id="APP">%1$s</xliff:g>، دوباره ضربه بزنید"</string>
     <string name="tap_again_to_complete" msgid="5423640945118279123">"برای تکمیل از طریق <xliff:g id="APP">%1$s</xliff:g>، دوباره ضربه بزنید"</string>
     <string name="transaction_failure" msgid="7828102078637936513">"این تراکنش با <xliff:g id="APP">%1$s</xliff:g> تکمیل نشد."</string>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 382cf20..88d193a 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -37,7 +37,7 @@
     <string name="could_not_use_app" msgid="8137587876138569083">"<xliff:g id="APP">%1$s</xliff:g>を使用できませんでした。"</string>
     <string name="pay_with" msgid="5531545488795798945">"お支払い:"</string>
     <string name="complete_with" msgid="6797459104103012992">"完了:"</string>
-    <string name="default_pay_app_removed" msgid="4108250545457437360">"「タップしてお支払い」用のサービスが削除されました。別のサービスを選択しますか?"</string>
+    <string name="default_pay_app_removed" msgid="4108250545457437360">"「タップ&ペイ」用のサービスが削除されました。別のサービスを選択しますか?"</string>
     <string name="ask_nfc_tap" msgid="2925239870458286340">"完了するには別の端末をタップしてください"</string>
     <string name="wifi_connect" msgid="6250727951843550671">"接続"</string>
     <string name="status_unable_to_connect" msgid="9183908200295307657">"ネットワークに接続できません"</string>
diff --git a/res/values-ml-rIN/strings.xml b/res/values-ml-rIN/strings.xml
index d9bb0a5..6b9a83b 100644
--- a/res/values-ml-rIN/strings.xml
+++ b/res/values-ml-rIN/strings.xml
@@ -37,7 +37,7 @@
     <string name="could_not_use_app" msgid="8137587876138569083">"<xliff:g id="APP">%1$s</xliff:g> ഉപയോഗിക്കാനായില്ല."</string>
     <string name="pay_with" msgid="5531545488795798945">"ഇതുപയോഗിച്ച് പണമടയ്‌ക്കുക"</string>
     <string name="complete_with" msgid="6797459104103012992">"ഇതുപയോഗിച്ച് പൂർത്തിയാക്കുക"</string>
-    <string name="default_pay_app_removed" msgid="4108250545457437360">"ടാപ്പുചെയ്‌ത് പണമടയ്‌ക്കുക എന്നതിനായി നിങ്ങൾ തിരഞ്ഞെടുത്ത സേവനം നീക്കംചെയ്‌തു. മറ്റൊന്ന് തിരഞ്ഞെടുക്കണോ?"</string>
+    <string name="default_pay_app_removed" msgid="4108250545457437360">"\'മൊബൈൽ തൊടീച്ച് പണമടയ്ക്കുക\' എന്നതിനായി നിങ്ങൾ തിരഞ്ഞെടുത്ത സേവനം നീക്കംചെയ്‌തു. മറ്റൊന്ന് തിരഞ്ഞെടുക്കണോ?"</string>
     <string name="ask_nfc_tap" msgid="2925239870458286340">"പൂർത്തിയാക്കുന്നതിന് മറ്റൊരു ഉപകരണം ടാപ്പുചെയ്യുക"</string>
     <string name="wifi_connect" msgid="6250727951843550671">"കണക്റ്റുചെയ്യുക"</string>
     <string name="status_unable_to_connect" msgid="9183908200295307657">"നെറ്റ്‌വർക്കിലേക്ക് കണക്റ്റുചെയ്യാനായില്ല"</string>
diff --git a/res/values-mr-rIN/strings.xml b/res/values-mr-rIN/strings.xml
index f63cf04..0863e32 100644
--- a/res/values-mr-rIN/strings.xml
+++ b/res/values-mr-rIN/strings.xml
@@ -27,8 +27,8 @@
     <string name="disconnected_peripheral" msgid="4470578100296504366">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> डिस्‍कनेक्‍ट केले"</string>
     <string name="pairing_peripheral" msgid="6983626861540899365">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> जोडणी करीत आहे"</string>
     <string name="pairing_peripheral_failed" msgid="6087643307743264679">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> जोडणे शक्य झाले नाही"</string>
-    <string name="failed_to_enable_bt" msgid="7229153323594758077">"ब सक्षम करू शकलो नाही"</string>
-    <string name="confirm_pairing" msgid="4112568077038265363">"आपण <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ब डिव्हाइस जोडू इच्छिता याची आपल्याला खात्री आहे?"</string>
+    <string name="failed_to_enable_bt" msgid="7229153323594758077">"ब्लूटुथ सक्षम करू शकलो नाही"</string>
+    <string name="confirm_pairing" msgid="4112568077038265363">"आपण <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ब्लूटुथ डिव्हाइस जोडू इच्छिता याची आपल्याला खात्री आहे?"</string>
     <string name="pair_yes" msgid="3525614878559994448">"होय"</string>
     <string name="pair_no" msgid="5022308368904055020">"नाही"</string>
     <string name="tap_again_to_pay" msgid="5754988005412859897">"<xliff:g id="APP">%1$s</xliff:g> सह देय देण्यासाठी पुन्हा टॅप करा"</string>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index cea1dd0..e262688 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -27,7 +27,7 @@
     <string name="disconnected_peripheral" msgid="4470578100296504366">"Frakoblet <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="pairing_peripheral" msgid="6983626861540899365">"Sammenkobler <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="pairing_peripheral_failed" msgid="6087643307743264679">"Kunne ikke sammenkoble <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="failed_to_enable_bt" msgid="7229153323594758077">"Kunne ikke aktivere Bluetooth"</string>
+    <string name="failed_to_enable_bt" msgid="7229153323594758077">"Kunne ikke slå på Bluetooth"</string>
     <string name="confirm_pairing" msgid="4112568077038265363">"Er du sikker på at koble til Bluetooth-enheten <xliff:g id="DEVICE_NAME">%1$s</xliff:g>?"</string>
     <string name="pair_yes" msgid="3525614878559994448">"Ja"</string>
     <string name="pair_no" msgid="5022308368904055020">"Nei"</string>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 11b2c8f..6822046 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -6,7 +6,7 @@
     <string name="inbound_me_profile_title" msgid="6146013785225412693">"Contact ontvangen via NFC"</string>
     <string name="inbound_me_profile_text" msgid="2342757196108092923">"Raak aan om deze persoon toe te voegen als contact."</string>
     <string name="outbound_me_profile_title" msgid="2523625031572784769">"NFC-interactie voltooid"</string>
-    <string name="outbound_me_profile_text" msgid="5594998841143667989">"Raak aan om deze persoon uw contactgegevens te geven."</string>
+    <string name="outbound_me_profile_text" msgid="5594998841143667989">"Raak aan om deze persoon je contactgegevens te geven."</string>
     <string name="accessibility_nfc_enabled" msgid="7796246979948787735">"NFC ingeschakeld."</string>
     <string name="touch" msgid="4727218133711188355">"Tikken om uit te zenden"</string>
     <string name="beam_progress" msgid="7453634884807323920">"Inkomende beam..."</string>
@@ -28,7 +28,7 @@
     <string name="pairing_peripheral" msgid="6983626861540899365">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> koppelen"</string>
     <string name="pairing_peripheral_failed" msgid="6087643307743264679">"Kan <xliff:g id="DEVICE_NAME">%1$s</xliff:g> niet koppelen"</string>
     <string name="failed_to_enable_bt" msgid="7229153323594758077">"Kan Bluetooth niet inschakelen"</string>
-    <string name="confirm_pairing" msgid="4112568077038265363">"Weet u zeker dat u het Bluetooth-apparaat <xliff:g id="DEVICE_NAME">%1$s</xliff:g> wilt koppelen?"</string>
+    <string name="confirm_pairing" msgid="4112568077038265363">"Weet je zeker dat je het Bluetooth-apparaat <xliff:g id="DEVICE_NAME">%1$s</xliff:g> wilt koppelen?"</string>
     <string name="pair_yes" msgid="3525614878559994448">"Ja"</string>
     <string name="pair_no" msgid="5022308368904055020">"Nee"</string>
     <string name="tap_again_to_pay" msgid="5754988005412859897">"Tik nogmaals om te betalen met <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -37,13 +37,13 @@
     <string name="could_not_use_app" msgid="8137587876138569083">"Kan <xliff:g id="APP">%1$s</xliff:g> niet gebruiken."</string>
     <string name="pay_with" msgid="5531545488795798945">"Betalen met"</string>
     <string name="complete_with" msgid="6797459104103012992">"Voltooien met"</string>
-    <string name="default_pay_app_removed" msgid="4108250545457437360">"Uw voorkeursservice voor tikken en betalen is verwijderd. Een andere app kiezen?"</string>
+    <string name="default_pay_app_removed" msgid="4108250545457437360">"Je voorkeursservice voor tikken en betalen is verwijderd. Een andere app kiezen?"</string>
     <string name="ask_nfc_tap" msgid="2925239870458286340">"Tik op een ander apparaat om te voltooien"</string>
     <string name="wifi_connect" msgid="6250727951843550671">"Verbinding maken"</string>
     <string name="status_unable_to_connect" msgid="9183908200295307657">"Kan geen verbinding maken met het netwerk"</string>
     <string name="status_wifi_connected" msgid="5893022897732105739">"Verbonden"</string>
     <string name="title_connect_to_network" msgid="2474034615817280146">"Verbinding maken met netwerk"</string>
     <string name="prompt_connect_to_network" msgid="8511683573657516114">"Verbinding maken met netwerk <xliff:g id="NETWORK_SSID">%1$s</xliff:g>?"</string>
-    <string name="beam_requires_nfc_enabled" msgid="2800366967218600534">"NFC moet zijn ingeschakeld voor Android Beam. Wilt u dit inschakelen?"</string>
+    <string name="beam_requires_nfc_enabled" msgid="2800366967218600534">"NFC moet zijn ingeschakeld voor Android Beam. Wil je dit inschakelen?"</string>
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
 </resources>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 0f7a5d8..b720f77 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -4,19 +4,19 @@
     <string name="app_name" msgid="78565911793142902">"Serviciul NFC"</string>
     <string name="nfcUserLabel" msgid="7708535817084357357">"NFC"</string>
     <string name="inbound_me_profile_title" msgid="6146013785225412693">"Persoană din agendă primită prin NFC"</string>
-    <string name="inbound_me_profile_text" msgid="2342757196108092923">"Atingeţi pentru a adăuga această persoană în Agendă."</string>
-    <string name="outbound_me_profile_title" msgid="2523625031572784769">"Interacţiune NFC completă"</string>
-    <string name="outbound_me_profile_text" msgid="5594998841143667989">"Atingeţi pentru a oferi acestei persoane datele dvs. de contact"</string>
+    <string name="inbound_me_profile_text" msgid="2342757196108092923">"Atingeți pentru a adăuga această persoană în Agendă."</string>
+    <string name="outbound_me_profile_title" msgid="2523625031572784769">"Interacțiune NFC completă"</string>
+    <string name="outbound_me_profile_text" msgid="5594998841143667989">"Atingeți pentru a oferi acestei persoane datele dvs. de contact"</string>
     <string name="accessibility_nfc_enabled" msgid="7796246979948787735">"NFC activat."</string>
-    <string name="touch" msgid="4727218133711188355">"Atingeţi pentru transmitere"</string>
+    <string name="touch" msgid="4727218133711188355">"Atingeți pentru transmitere"</string>
     <string name="beam_progress" msgid="7453634884807323920">"Transmitere primită..."</string>
     <string name="beam_outgoing" msgid="4679536649779123495">"Se transmite..."</string>
     <string name="beam_complete" msgid="477026736424637435">"Transmitere încheiată"</string>
     <string name="beam_failed" msgid="5116241718189888630">"Transmiterea nu este finalizată"</string>
     <string name="beam_canceled" msgid="5425192751826544741">"Transmitere anulată"</string>
     <string name="cancel" msgid="61873902552555096">"Anulați"</string>
-    <string name="beam_touch_to_view" msgid="7853129156831642630">"Atingeţi pentru a afişa"</string>
-    <string name="beam_handover_not_supported" msgid="4083165921751489015">"Dispozitivul destinatarului nu acceptă transferul fişierelor mari prin fascicul."</string>
+    <string name="beam_touch_to_view" msgid="7853129156831642630">"Atingeți pentru a afișa"</string>
+    <string name="beam_handover_not_supported" msgid="4083165921751489015">"Dispozitivul destinatarului nu acceptă transferul fișierelor mari prin fascicul."</string>
     <string name="beam_try_again" msgid="3364677301009783455">"Apropiați dispozitivele din nou"</string>
     <string name="beam_busy" msgid="5253335587620612576">"Beam este ocupat. Încercați după finalizarea transferului anterior."</string>
     <string name="device" msgid="4459621591392478151">"dispozitiv"</string>
@@ -27,8 +27,8 @@
     <string name="disconnected_peripheral" msgid="4470578100296504366">"S-a deconectat <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="pairing_peripheral" msgid="6983626861540899365">"Se asociază <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="pairing_peripheral_failed" msgid="6087643307743264679">"Nu s-a putut asocia <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="failed_to_enable_bt" msgid="7229153323594758077">"Activarea Bluetooth a eşuat"</string>
-    <string name="confirm_pairing" msgid="4112568077038265363">"Sigur doriţi să împerecheaţi dispozitivul Bluetooth <xliff:g id="DEVICE_NAME">%1$s</xliff:g>?"</string>
+    <string name="failed_to_enable_bt" msgid="7229153323594758077">"Activarea Bluetooth a eșuat"</string>
+    <string name="confirm_pairing" msgid="4112568077038265363">"Sigur doriți să împerecheați dispozitivul Bluetooth <xliff:g id="DEVICE_NAME">%1$s</xliff:g>?"</string>
     <string name="pair_yes" msgid="3525614878559994448">"Da"</string>
     <string name="pair_no" msgid="5022308368904055020">"Nu"</string>
     <string name="tap_again_to_pay" msgid="5754988005412859897">"Atingeți din nou pentru a plăti cu <xliff:g id="APP">%1$s</xliff:g>"</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index ea338e9..f69fe21 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -38,7 +38,7 @@
     <string name="pay_with" msgid="5531545488795798945">"Способ оплаты"</string>
     <string name="complete_with" msgid="6797459104103012992">"Использовать"</string>
     <string name="default_pay_app_removed" msgid="4108250545457437360">"Вы удалили приложение для бесконтактной оплаты, используемое по умолчанию. Выбрать другое?"</string>
-    <string name="ask_nfc_tap" msgid="2925239870458286340">"Нажмите на экран другого устройства, чтобы передать данные"</string>
+    <string name="ask_nfc_tap" msgid="2925239870458286340">"Коснитесь другого устройства, чтобы передать данные"</string>
     <string name="wifi_connect" msgid="6250727951843550671">"Подключиться"</string>
     <string name="status_unable_to_connect" msgid="9183908200295307657">"Не удалось подключиться к сети"</string>
     <string name="status_wifi_connected" msgid="5893022897732105739">"Соединение установлено"</string>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 3c33636..07d6eed 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -31,14 +31,14 @@
     <string name="confirm_pairing" msgid="4112568077038265363">"您確定要與藍牙裝置「<xliff:g id="DEVICE_NAME">%1$s</xliff:g>」配對嗎?"</string>
     <string name="pair_yes" msgid="3525614878559994448">"是"</string>
     <string name="pair_no" msgid="5022308368904055020">"否"</string>
-    <string name="tap_again_to_pay" msgid="5754988005412859897">"再輕按一下即可使用「<xliff:g id="APP">%1$s</xliff:g>」付款"</string>
-    <string name="tap_again_to_complete" msgid="5423640945118279123">"再輕按一下即可使用「<xliff:g id="APP">%1$s</xliff:g>」完成付款"</string>
+    <string name="tap_again_to_pay" msgid="5754988005412859897">"再輕觸一下即可使用「<xliff:g id="APP">%1$s</xliff:g>」付款"</string>
+    <string name="tap_again_to_complete" msgid="5423640945118279123">"再輕觸一下即可使用「<xliff:g id="APP">%1$s</xliff:g>」完成付款"</string>
     <string name="transaction_failure" msgid="7828102078637936513">"無法使用「<xliff:g id="APP">%1$s</xliff:g>」完成這項交易作業。"</string>
     <string name="could_not_use_app" msgid="8137587876138569083">"無法使用「<xliff:g id="APP">%1$s</xliff:g>」。"</string>
     <string name="pay_with" msgid="5531545488795798945">"使用以下應用程式完成付款:"</string>
     <string name="complete_with" msgid="6797459104103012992">"使用以下應用程式完成操作:"</string>
     <string name="default_pay_app_removed" msgid="4108250545457437360">"您偏好的感應付款服務已遭移除,要選擇其他服務嗎?"</string>
-    <string name="ask_nfc_tap" msgid="2925239870458286340">"輕按另一個裝置即可完成"</string>
+    <string name="ask_nfc_tap" msgid="2925239870458286340">"輕觸另一個裝置即可完成"</string>
     <string name="wifi_connect" msgid="6250727951843550671">"連線"</string>
     <string name="status_unable_to_connect" msgid="9183908200295307657">"無法連線至網路"</string>
     <string name="status_wifi_connected" msgid="5893022897732105739">"已連線"</string>
diff --git a/src/com/android/nfc/BeamShareActivity.java b/src/com/android/nfc/BeamShareActivity.java
index cff601c..76629b8 100644
--- a/src/com/android/nfc/BeamShareActivity.java
+++ b/src/com/android/nfc/BeamShareActivity.java
@@ -143,7 +143,13 @@
                 ClipData.Item item = clipData.getItemAt(i);
                 // First try to get an Uri
                 Uri uri = item.getUri();
-                String plainText = item.coerceToText(this).toString();
+                String plainText = null;
+                try {
+                    plainText = item.coerceToText(this).toString();
+                } catch (IllegalStateException e) {
+                    if (DBG) Log.d(TAG, e.getMessage());
+                    continue;
+                }
                 if (uri != null) {
                     if (DBG) Log.d(TAG, "Found uri in ClipData.");
                     tryUri(uri);
diff --git a/src/com/android/nfc/DeviceHost.java b/src/com/android/nfc/DeviceHost.java
index 21d720e..a123dde 100644
--- a/src/com/android/nfc/DeviceHost.java
+++ b/src/com/android/nfc/DeviceHost.java
@@ -28,9 +28,9 @@
 
         /**
          */
-        public void onHostCardEmulationActivated();
-        public void onHostCardEmulationData(byte[] data);
-        public void onHostCardEmulationDeactivated();
+        public void onHostCardEmulationActivated(int technology);
+        public void onHostCardEmulationData(int technology, byte[] data);
+        public void onHostCardEmulationDeactivated(int technology);
 
         /**
          * Notifies P2P Device detected, to activate LLCP link
@@ -189,6 +189,14 @@
 
     public boolean commitRouting();
 
+    public void registerT3tIdentifier(byte[] t3tIdentifier);
+
+    public void deregisterT3tIdentifier(byte[] t3tIdentifier);
+
+    public void clearT3tIdentifiersCache();
+
+    public int getLfT3tMax();
+
     public LlcpConnectionlessSocket createLlcpConnectionlessSocket(int nSap, String sn)
             throws LlcpException;
 
diff --git a/src/com/android/nfc/NfcDispatcher.java b/src/com/android/nfc/NfcDispatcher.java
index 912b897..531d322 100644
--- a/src/com/android/nfc/NfcDispatcher.java
+++ b/src/com/android/nfc/NfcDispatcher.java
@@ -592,6 +592,9 @@
         intent.putExtra(PeripheralHandoverService.EXTRA_PERIPHERAL_DEVICE, handover.device);
         intent.putExtra(PeripheralHandoverService.EXTRA_PERIPHERAL_NAME, handover.name);
         intent.putExtra(PeripheralHandoverService.EXTRA_PERIPHERAL_TRANSPORT, handover.transport);
+        if (handover.oobData != null) {
+            intent.putExtra(PeripheralHandoverService.EXTRA_PERIPHERAL_OOB_DATA, handover.oobData);
+        }
         mContext.startServiceAsUser(intent, UserHandle.CURRENT);
 
         return true;
diff --git a/src/com/android/nfc/NfcService.java b/src/com/android/nfc/NfcService.java
index 08779aa..ca99c16 100755
--- a/src/com/android/nfc/NfcService.java
+++ b/src/com/android/nfc/NfcService.java
@@ -42,6 +42,7 @@
 import android.nfc.INfcAdapter;
 import android.nfc.INfcAdapterExtras;
 import android.nfc.INfcCardEmulation;
+import android.nfc.INfcFCardEmulation;
 import android.nfc.INfcTag;
 import android.nfc.INfcUnlockHandler;
 import android.nfc.NdefMessage;
@@ -79,6 +80,7 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.nio.ByteBuffer;
 import java.util.Arrays;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -114,6 +116,8 @@
     static final int MSG_RF_FIELD_ACTIVATED = 9;
     static final int MSG_RF_FIELD_DEACTIVATED = 10;
     static final int MSG_RESUME_POLLING = 11;
+    static final int MSG_REGISTER_T3T_IDENTIFIER = 12;
+    static final int MSG_DEREGISTER_T3T_IDENTIFIER = 13;
 
     static final long MAX_POLLING_PAUSE_TIMEOUT = 40000;
 
@@ -210,6 +214,7 @@
     NfcAdapterService mNfcAdapter;
     boolean mIsDebugBuild;
     boolean mIsHceCapable;
+    boolean mIsHceFCapable;
     boolean mPollingPaused;
 
     private NfcDispatcher mNfcDispatcher;
@@ -238,23 +243,23 @@
      * Notifies transaction
      */
     @Override
-    public void onHostCardEmulationActivated() {
+    public void onHostCardEmulationActivated(int technology) {
         if (mCardEmulationManager != null) {
-            mCardEmulationManager.onHostCardEmulationActivated();
+            mCardEmulationManager.onHostCardEmulationActivated(technology);
         }
     }
 
     @Override
-    public void onHostCardEmulationData(byte[] data) {
+    public void onHostCardEmulationData(int technology, byte[] data) {
         if (mCardEmulationManager != null) {
-            mCardEmulationManager.onHostCardEmulationData(data);
+            mCardEmulationManager.onHostCardEmulationData(technology, data);
         }
     }
 
     @Override
-    public void onHostCardEmulationDeactivated() {
+    public void onHostCardEmulationDeactivated(int technology) {
         if (mCardEmulationManager != null) {
-            mCardEmulationManager.onHostCardEmulationDeactivated();
+            mCardEmulationManager.onHostCardEmulationDeactivated(technology);
         }
     }
 
@@ -379,7 +384,11 @@
         updatePackageCache();
 
         PackageManager pm = mContext.getPackageManager();
-        mIsHceCapable = pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION);
+        mIsHceCapable =
+                pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION) ||
+                pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION_NFCF);
+        mIsHceFCapable =
+                pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION_NFCF);
         if (mIsHceCapable) {
             mCardEmulationManager = new CardEmulationManager(mContext);
         }
@@ -876,6 +885,15 @@
         }
 
         @Override
+        public INfcFCardEmulation getNfcFCardEmulationInterface() {
+            if (mIsHceFCapable) {
+                return mCardEmulationManager.getNfcFCardEmulationInterface();
+            } else {
+                return null;
+            }
+        }
+
+        @Override
         public int getState() throws RemoteException {
             synchronized (NfcService.this) {
                 return mState;
@@ -905,6 +923,10 @@
         public void setReaderMode(IBinder binder, IAppCallback callback, int flags, Bundle extras)
                 throws RemoteException {
             synchronized (NfcService.this) {
+                if (!isNfcEnabled()) {
+                    Log.e(TAG, "setReaderMode() called while NFC is not enabled.");
+                    return;
+                }
                 if (flags != 0) {
                     try {
                         mReaderModeParams = new ReaderModeParams();
@@ -1603,6 +1625,41 @@
         sendMessage(MSG_UNROUTE_AID, aid);
     }
 
+    private byte[] getT3tIdentifierBytes(String systemCode, String nfcId2) {
+        ByteBuffer buffer = ByteBuffer.allocate(2 + 8);
+        buffer.put(hexStringToBytes(systemCode));
+        buffer.put(hexStringToBytes(nfcId2));
+
+        byte[] t3tIdBytes = new byte[buffer.position()];
+        buffer.position(0);
+        buffer.get(t3tIdBytes);
+
+        return t3tIdBytes;
+    }
+
+    public void registerT3tIdentifier(String systemCode, String nfcId2) {
+        Log.d(TAG, "request to register LF_T3T_IDENTIFIER");
+
+        byte[] t3tIdentifier = getT3tIdentifierBytes(systemCode, nfcId2);
+        sendMessage(MSG_REGISTER_T3T_IDENTIFIER, t3tIdentifier);
+    }
+
+    public void deregisterT3tIdentifier(String systemCode, String nfcId2) {
+        Log.d(TAG, "request to deregister LF_T3T_IDENTIFIER");
+
+        byte[] t3tIdentifier = getT3tIdentifierBytes(systemCode, nfcId2);
+        sendMessage(MSG_DEREGISTER_T3T_IDENTIFIER, t3tIdentifier);
+    }
+
+    public void clearT3tIdentifiersCache() {
+        Log.d(TAG, "clear T3t Identifiers Cache");
+        mDeviceHost.clearT3tIdentifiersCache();
+    }
+
+    public int getLfT3tMax() {
+        return mDeviceHost.getLfT3tMax();
+    }
+
     public void commitRouting() {
         mHandler.sendEmptyMessage(MSG_COMMIT_ROUTING);
     }
@@ -1634,6 +1691,30 @@
                     mDeviceHost.unrouteAid(hexStringToBytes(aid));
                     break;
                 }
+                case MSG_REGISTER_T3T_IDENTIFIER: {
+                    Log.d(TAG, "message to register LF_T3T_IDENTIFIER");
+                    mDeviceHost.disableDiscovery();
+
+                    byte[] t3tIdentifier = (byte[]) msg.obj;
+                    mDeviceHost.registerT3tIdentifier(t3tIdentifier);
+
+                    NfcDiscoveryParameters params = computeDiscoveryParameters(mScreenState);
+                    boolean shouldRestart = mCurrentDiscoveryParameters.shouldEnableDiscovery();
+                    mDeviceHost.enableDiscovery(params, shouldRestart);
+                    break;
+                }
+                case MSG_DEREGISTER_T3T_IDENTIFIER: {
+                    Log.d(TAG, "message to deregister LF_T3T_IDENTIFIER");
+                    mDeviceHost.disableDiscovery();
+
+                    byte[] t3tIdentifier = (byte[]) msg.obj;
+                    mDeviceHost.deregisterT3tIdentifier(t3tIdentifier);
+
+                    NfcDiscoveryParameters params = computeDiscoveryParameters(mScreenState);
+                    boolean shouldRestart = mCurrentDiscoveryParameters.shouldEnableDiscovery();
+                    mDeviceHost.enableDiscovery(params, shouldRestart);
+                    break;
+                }
                 case MSG_INVOKE_BEAM: {
                     mP2pLinkManager.onManualBeamInvoke((BeamShareData)msg.obj);
                     break;
diff --git a/src/com/android/nfc/cardemulation/CardEmulationManager.java b/src/com/android/nfc/cardemulation/CardEmulationManager.java
index f34522a..73ed881 100644
--- a/src/com/android/nfc/cardemulation/CardEmulationManager.java
+++ b/src/com/android/nfc/cardemulation/CardEmulationManager.java
@@ -23,9 +23,12 @@
 import android.content.Context;
 import android.content.Intent;
 import android.nfc.INfcCardEmulation;
+import android.nfc.INfcFCardEmulation;
 import android.nfc.cardemulation.AidGroup;
 import android.nfc.cardemulation.ApduServiceInfo;
+import android.nfc.cardemulation.NfcFServiceInfo;
 import android.nfc.cardemulation.CardEmulation;
+import android.nfc.cardemulation.NfcFCardEmulation;
 import android.os.Binder;
 import android.os.RemoteException;
 import android.os.UserHandle;
@@ -33,57 +36,100 @@
 import android.util.Log;
 
 import com.android.nfc.NfcPermissions;
+import com.android.nfc.NfcService;
 import com.android.nfc.cardemulation.RegisteredServicesCache;
+import com.android.nfc.cardemulation.RegisteredNfcFServicesCache;
 
 /**
  * CardEmulationManager is the central entity
  * responsible for delegating to individual components
  * implementing card emulation:
  * - RegisteredServicesCache keeping track of HCE and SE services on the device
+ * - RegisteredNfcFServicesCache keeping track of HCE-F services on the device
  * - RegisteredAidCache keeping track of AIDs registered by those services and manages
  *   the routing table in the NFCC.
+ * - RegisteredT3tIdentifiersCache keeping track of T3T Identifier registered by
+ *   those services and manages the routing table in the NFCC.
  * - HostEmulationManager handles incoming APDUs for the host and forwards to HCE
  *   services as necessary.
+ * - HostNfcFEmulationManager handles incoming NFC-F packets for the host and
+ *   forwards to HCE-F services as necessary.
  */
 public class CardEmulationManager implements RegisteredServicesCache.Callback,
-        PreferredServices.Callback {
+        RegisteredNfcFServicesCache.Callback, PreferredServices.Callback,
+        EnabledNfcFServices.Callback {
     static final String TAG = "CardEmulationManager";
     static final boolean DBG = false;
 
+    static final int NFC_HCE_APDU = 0x01;
+    static final int NFC_HCE_NFCF = 0x04;
+
     final RegisteredAidCache mAidCache;
+    final RegisteredT3tIdentifiersCache mT3tIdentifiersCache;
     final RegisteredServicesCache mServiceCache;
+    final RegisteredNfcFServicesCache mNfcFServicesCache;
     final HostEmulationManager mHostEmulationManager;
+    final HostNfcFEmulationManager mHostNfcFEmulationManager;
     final PreferredServices mPreferredServices;
+    final EnabledNfcFServices mEnabledNfcFServices;
     final Context mContext;
     final CardEmulationInterface mCardEmulationInterface;
+    final NfcFCardEmulationInterface mNfcFCardEmulationInterface;
 
     public CardEmulationManager(Context context) {
         mContext = context;
         mCardEmulationInterface = new CardEmulationInterface();
+        mNfcFCardEmulationInterface = new NfcFCardEmulationInterface();
         mAidCache = new RegisteredAidCache(context);
+        mT3tIdentifiersCache = new RegisteredT3tIdentifiersCache(context);
         mHostEmulationManager = new HostEmulationManager(context, mAidCache);
+        mHostNfcFEmulationManager = new HostNfcFEmulationManager(context, mT3tIdentifiersCache);
         mServiceCache = new RegisteredServicesCache(context, this);
+        mNfcFServicesCache = new RegisteredNfcFServicesCache(context, this);
         mPreferredServices = new PreferredServices(context, mServiceCache, mAidCache, this);
-
+        mEnabledNfcFServices = new EnabledNfcFServices(
+                context, mNfcFServicesCache, mT3tIdentifiersCache, this);
         mServiceCache.initialize();
+        mNfcFServicesCache.initialize();
     }
 
     public INfcCardEmulation getNfcCardEmulationInterface() {
         return mCardEmulationInterface;
     }
 
-    public void onHostCardEmulationActivated() {
-        mHostEmulationManager.onHostEmulationActivated();
-        mPreferredServices.onHostEmulationActivated();
+    public INfcFCardEmulation getNfcFCardEmulationInterface() {
+        return mNfcFCardEmulationInterface;
     }
 
-    public void onHostCardEmulationData(byte[] data) {
-        mHostEmulationManager.onHostEmulationData(data);
+
+    public void onHostCardEmulationActivated(int technology) {
+        if (technology == NFC_HCE_APDU) {
+            mHostEmulationManager.onHostEmulationActivated();
+            mPreferredServices.onHostEmulationActivated();
+        } else if (technology == NFC_HCE_NFCF) {
+            mHostNfcFEmulationManager.onHostEmulationActivated();
+            mNfcFServicesCache.onHostEmulationActivated();
+            mEnabledNfcFServices.onHostEmulationActivated();
+        }
     }
 
-    public void onHostCardEmulationDeactivated() {
-        mHostEmulationManager.onHostEmulationDeactivated();
-        mPreferredServices.onHostEmulationDeactivated();
+    public void onHostCardEmulationData(int technology, byte[] data) {
+        if (technology == NFC_HCE_APDU) {
+            mHostEmulationManager.onHostEmulationData(data);
+        } else if (technology == NFC_HCE_NFCF) {
+            mHostNfcFEmulationManager.onHostEmulationData(data);
+        }
+    }
+
+    public void onHostCardEmulationDeactivated(int technology) {
+        if (technology == NFC_HCE_APDU) {
+            mHostEmulationManager.onHostEmulationDeactivated();
+            mPreferredServices.onHostEmulationDeactivated();
+        } else if (technology == NFC_HCE_NFCF) {
+            mHostNfcFEmulationManager.onHostEmulationDeactivated();
+            mNfcFServicesCache.onHostEmulationDeactivated();
+            mEnabledNfcFServices.onHostEmulationDeactivated();
+        }
     }
 
     public void onOffHostAidSelected() {
@@ -91,23 +137,42 @@
     }
 
     public void onUserSwitched(int userId) {
+        // for HCE
         mServiceCache.invalidateCache(userId);
         mPreferredServices.onUserSwitched(userId);
+        // for HCE-F
+        mHostNfcFEmulationManager.onUserSwitched();
+        mT3tIdentifiersCache.onUserSwitched();
+        mEnabledNfcFServices.onUserSwitched(userId);
+        mNfcFServicesCache.invalidateCache(userId);
     }
 
     public void onNfcEnabled() {
+        // for HCE
         mAidCache.onNfcEnabled();
+        // for HCE-F
+        mT3tIdentifiersCache.onNfcEnabled();
     }
 
     public void onNfcDisabled() {
+        // for HCE
         mAidCache.onNfcDisabled();
+        // for HCE-F
+        mHostNfcFEmulationManager.onNfcDisabled();
+        mNfcFServicesCache.onNfcDisabled();
+        mT3tIdentifiersCache.onNfcDisabled();
+        mEnabledNfcFServices.onNfcDisabled();
     }
 
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         mServiceCache.dump(fd, pw, args);
+        mNfcFServicesCache.dump(fd, pw ,args);
         mPreferredServices.dump(fd, pw, args);
+        mEnabledNfcFServices.dump(fd, pw, args);
         mAidCache.dump(fd, pw, args);
+        mT3tIdentifiersCache.dump(fd, pw, args);
         mHostEmulationManager.dump(fd, pw, args);
+        mHostNfcFEmulationManager.dump(fd, pw, args);
     }
 
     @Override
@@ -120,6 +185,14 @@
         mPreferredServices.onServicesUpdated();
     }
 
+    @Override
+    public void onNfcFServicesUpdated(int userId, List<NfcFServiceInfo> services) {
+        // Update the T3T identifier cache
+        mT3tIdentifiersCache.onServicesUpdated(userId, services);
+        // Update the enabled services list
+        mEnabledNfcFServices.onServicesUpdated();
+    }
+
     void verifyDefaults(int userId, List<ApduServiceInfo> services) {
         ComponentName defaultPaymentService =
                 getDefaultServiceForCategory(userId, CardEmulation.CATEGORY_PAYMENT, false);
@@ -247,6 +320,20 @@
         return mServiceCache.hasService(userId, service);
     }
 
+    boolean isNfcFServiceInstalled(int userId, ComponentName service) {
+        boolean serviceFound = mNfcFServicesCache.hasService(userId, service);
+        if (!serviceFound) {
+            // If we don't know about this service yet, it may have just been enabled
+            // using PackageManager.setComponentEnabledSetting(). The PackageManager
+            // broadcasts are delayed by 10 seconds in that scenario, which causes
+            // calls to our APIs referencing that service to fail.
+            // Hence, update the cache in case we don't know about the service.
+            if (DBG) Log.d(TAG, "Didn't find passed in service, invalidating cache.");
+            mNfcFServicesCache.invalidateCache(userId);
+        }
+        return mNfcFServicesCache.hasService(userId, service);
+    }
+
     /**
      * Returns whether a service in this package is preferred,
      * either because it's the default payment app or it's running
@@ -257,10 +344,8 @@
     }
 
     /**
-     * This class implements the application-facing APIs
-     * and are called from binder. All calls must be
-     * permission-checked.
-     *
+     * This class implements the application-facing APIs and are called
+     * from binder. All calls must be permission-checked.
      */
     final class CardEmulationInterface extends INfcCardEmulation.Stub {
         @Override
@@ -370,7 +455,6 @@
             NfcPermissions.enforceUserPermissions(mContext);
             return mPreferredServices.unregisteredPreferredForegroundService(
                     Binder.getCallingUid());
-
         }
 
         @Override
@@ -379,6 +463,106 @@
         }
     }
 
+    /**
+     * This class implements the application-facing APIs and are called
+     * from binder. All calls must be permission-checked.
+     */
+    final class NfcFCardEmulationInterface extends INfcFCardEmulation.Stub {
+        @Override
+        public String getSystemCodeForService(int userId, ComponentName service)
+                throws RemoteException {
+            NfcPermissions.validateUserId(userId);
+            NfcPermissions.enforceUserPermissions(mContext);
+            if (!isNfcFServiceInstalled(userId, service)) {
+                return null;
+            }
+            return mNfcFServicesCache.getSystemCodeForService(
+                    userId, Binder.getCallingUid(), service);
+        }
+
+        @Override
+        public boolean registerSystemCodeForService(int userId, ComponentName service,
+                String systemCode)
+                throws RemoteException {
+            NfcPermissions.validateUserId(userId);
+            NfcPermissions.enforceUserPermissions(mContext);
+            if (!isNfcFServiceInstalled(userId, service)) {
+                return false;
+            }
+            return mNfcFServicesCache.registerSystemCodeForService(
+                    userId, Binder.getCallingUid(), service, systemCode);
+        }
+
+        @Override
+        public boolean removeSystemCodeForService(int userId, ComponentName service)
+                throws RemoteException {
+            NfcPermissions.validateUserId(userId);
+            NfcPermissions.enforceUserPermissions(mContext);
+            if (!isNfcFServiceInstalled(userId, service)) {
+                return false;
+            }
+            return mNfcFServicesCache.removeSystemCodeForService(
+                    userId, Binder.getCallingUid(), service);
+        }
+
+        @Override
+        public String getNfcid2ForService(int userId, ComponentName service)
+                throws RemoteException {
+            NfcPermissions.validateUserId(userId);
+            NfcPermissions.enforceUserPermissions(mContext);
+            if (!isNfcFServiceInstalled(userId, service)) {
+                return null;
+            }
+            return mNfcFServicesCache.getNfcid2ForService(
+                    userId, Binder.getCallingUid(), service);
+        }
+
+        @Override
+        public boolean setNfcid2ForService(int userId,
+                ComponentName service, String nfcid2) throws RemoteException {
+            NfcPermissions.validateUserId(userId);
+            NfcPermissions.enforceUserPermissions(mContext);
+            if (!isNfcFServiceInstalled(userId, service)) {
+                return false;
+            }
+            return mNfcFServicesCache.setNfcid2ForService(
+                    userId, Binder.getCallingUid(), service, nfcid2);
+        }
+
+        @Override
+        public boolean enableNfcFForegroundService(ComponentName service)
+                throws RemoteException {
+            NfcPermissions.enforceUserPermissions(mContext);
+            if (isNfcFServiceInstalled(UserHandle.getCallingUserId(), service)) {
+                return mEnabledNfcFServices.registerEnabledForegroundService(service,
+                        Binder.getCallingUid());
+            }
+            return false;
+        }
+
+        @Override
+        public boolean disableNfcFForegroundService() throws RemoteException {
+            NfcPermissions.enforceUserPermissions(mContext);
+            return mEnabledNfcFServices.unregisteredEnabledForegroundService(
+                    Binder.getCallingUid());
+        }
+
+        @Override
+        public List<NfcFServiceInfo> getNfcFServices(int userId)
+                throws RemoteException {
+            NfcPermissions.validateUserId(userId);
+            NfcPermissions.enforceUserPermissions(mContext);
+            return mNfcFServicesCache.getServices(userId);
+        }
+
+        @Override
+        public int getMaxNumOfRegisterableSystemCodes()
+                throws RemoteException {
+            NfcPermissions.enforceUserPermissions(mContext);
+            return NfcService.getInstance().getLfT3tMax();
+        }
+    }
+
     @Override
     public void onPreferredPaymentServiceChanged(ComponentName service) {
         mAidCache.onPreferredPaymentServiceChanged(service);
@@ -389,5 +573,11 @@
     public void onPreferredForegroundServiceChanged(ComponentName service) {
         mAidCache.onPreferredForegroundServiceChanged(service);
         mHostEmulationManager.onPreferredForegroundServiceChanged(service);
-    };
+    }
+
+    @Override
+    public void onEnabledForegroundNfcFServiceChanged(ComponentName service) {
+        mT3tIdentifiersCache.onEnabledForegroundNfcFServiceChanged(service);
+        mHostNfcFEmulationManager.onEnabledForegroundNfcFServiceChanged(service);
+    }
 }
diff --git a/src/com/android/nfc/cardemulation/EnabledNfcFServices.java b/src/com/android/nfc/cardemulation/EnabledNfcFServices.java
new file mode 100644
index 0000000..1d2acb5
--- /dev/null
+++ b/src/com/android/nfc/cardemulation/EnabledNfcFServices.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2015 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.cardemulation;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+import com.android.nfc.ForegroundUtils;
+
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.nfc.cardemulation.NfcFServiceInfo;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+
+public class EnabledNfcFServices implements com.android.nfc.ForegroundUtils.Callback {
+    static final String TAG = "EnabledNfcFCardEmulationServices";
+    static final boolean DBG = false;
+
+    final Context mContext;
+    final RegisteredNfcFServicesCache mNfcFServiceCache;
+    final RegisteredT3tIdentifiersCache mT3tIdentifiersCache;
+    final Callback mCallback;
+    final ForegroundUtils mForegroundUtils = ForegroundUtils.getInstance();
+    final Handler mHandler = new Handler(Looper.getMainLooper());
+
+    final Object mLock = new Object();
+    // Variables below synchronized on mLock
+    ComponentName mForegroundComponent = null; // The computed enabled foreground component
+    ComponentName mForegroundRequested = null; // The component requested to be enabled by fg app
+    int mForegroundUid = -1; // The UID of the fg app, or -1 if fg app didn't request
+
+    boolean mComputeFgRequested = false;
+    boolean mActivated = false;
+
+    public interface Callback {
+        void onEnabledForegroundNfcFServiceChanged(ComponentName service);
+    }
+
+    public EnabledNfcFServices(Context context,
+            RegisteredNfcFServicesCache nfcFServiceCache,
+            RegisteredT3tIdentifiersCache t3tIdentifiersCache, Callback callback) {
+        if (DBG) Log.d(TAG, "EnabledNfcFServices");
+        mContext = context;
+        mNfcFServiceCache = nfcFServiceCache;
+        mT3tIdentifiersCache = t3tIdentifiersCache;
+        mCallback = callback;
+    }
+
+    void computeEnabledForegroundService() {
+        if (DBG) Log.d(TAG, "computeEnabledForegroundService");
+        ComponentName foregroundRequested = null;
+        boolean changed = false;
+        synchronized (mLock) {
+            if (mActivated) {
+                Log.d(TAG, "configuration will be postponed until deactivation");
+                mComputeFgRequested = true;
+                return;
+            }
+            mComputeFgRequested = false;
+            foregroundRequested = mForegroundRequested;
+            if (mForegroundRequested != null &&
+                    (mForegroundComponent == null ||
+                    !mForegroundRequested.equals(mForegroundComponent))) {
+                mForegroundComponent = mForegroundRequested;
+                changed = true;
+            } else if (mForegroundRequested == null && mForegroundComponent != null){
+                mForegroundComponent = mForegroundRequested;
+                changed = true;
+            }
+        }
+        // Notify if anything changed
+        if (changed) {
+            mCallback.onEnabledForegroundNfcFServiceChanged(foregroundRequested);
+        }
+    }
+
+    public void onServicesUpdated() {
+        if (DBG) Log.d(TAG, "onServicesUpdated");
+        // If enabled foreground service is set, remove it
+        boolean changed = false;
+        synchronized (mLock) {
+            if (mForegroundComponent != null) {
+                Log.d(TAG, "Removing foreground enabled service because of service update.");
+                mForegroundRequested = null;
+                mForegroundUid = -1;
+                changed = true;
+            }
+        }
+        if (changed) {
+            computeEnabledForegroundService();
+        }
+    }
+
+    public boolean registerEnabledForegroundService(ComponentName service, int callingUid) {
+        if (DBG) Log.d(TAG, "registerEnabledForegroundService");
+        boolean success = false;
+        synchronized (mLock) {
+            NfcFServiceInfo serviceInfo = mNfcFServiceCache.getService(
+                    ActivityManager.getCurrentUser(), service);
+            if (serviceInfo == null) {
+                return false;
+            } else {
+                if (serviceInfo.getSystemCode().equalsIgnoreCase("NULL") ||
+                        serviceInfo.getNfcid2().equalsIgnoreCase("NULL")) {
+                    return false;
+                }
+            }
+            if (service.equals(mForegroundRequested)) {
+                Log.e(TAG, "The servcie is already requested to the foreground service.");
+                return true;
+            }
+            if (mForegroundUtils.registerUidToBackgroundCallback(this, callingUid)) {
+                mForegroundRequested = service;
+                mForegroundUid = callingUid;
+                success = true;
+            } else {
+                Log.e(TAG, "Calling UID is not in the foreground, ignorning!");
+            }
+        }
+        if (success) {
+            computeEnabledForegroundService();
+        }
+        return success;
+    }
+
+    boolean unregisterForegroundService(int uid) {
+        if (DBG) Log.d(TAG, "unregisterForegroundService");
+        boolean success = false;
+        synchronized (mLock) {
+            if (mForegroundUid == uid) {
+                mForegroundRequested = null;
+                mForegroundUid = -1;
+                success = true;
+            } // else, other UID in foreground
+        }
+        if (success) {
+            computeEnabledForegroundService();
+        }
+        return success;
+    }
+
+    public boolean unregisteredEnabledForegroundService(int callingUid) {
+        if (DBG) Log.d(TAG, "unregisterEnabledForegroundService");
+        // Verify the calling UID is in the foreground
+        if (mForegroundUtils.isInForeground(callingUid)) {
+            return unregisterForegroundService(callingUid);
+        } else {
+            Log.e(TAG, "Calling UID is not in the foreground, ignorning!");
+            return false;
+        }
+    }
+
+    @Override
+    public void onUidToBackground(int uid) {
+        if (DBG) Log.d(TAG, "onUidToBackground");
+        unregisterForegroundService(uid);
+    }
+
+    public void onHostEmulationActivated() {
+        if (DBG) Log.d(TAG, "onHostEmulationActivated");
+        synchronized (mLock) {
+            mActivated = true;
+        }
+    }
+
+    public void onHostEmulationDeactivated() {
+        if (DBG) Log.d(TAG, "onHostEmulationDeactivated");
+        boolean needComputeFg = false;
+        synchronized (mLock) {
+            mActivated = false;
+            if (mComputeFgRequested) {
+                needComputeFg = true;
+            }
+        }
+        if (needComputeFg) {
+            Log.d(TAG, "do postponed configuration");
+            computeEnabledForegroundService();
+        }
+    }
+
+    public void onNfcDisabled() {
+        synchronized (mLock) {
+            mForegroundComponent = null;
+            mForegroundRequested = null;
+            mActivated = false;
+            mComputeFgRequested = false;
+            mForegroundUid = -1;
+        }
+    }
+
+    public void onUserSwitched(int userId) {
+        synchronized (mLock) {
+            mForegroundComponent = null;
+            mForegroundRequested = null;
+            mActivated = false;
+            mComputeFgRequested = false;
+            mForegroundUid = -1;
+        }
+    }
+
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+    }
+}
diff --git a/src/com/android/nfc/cardemulation/HostNfcFEmulationManager.java b/src/com/android/nfc/cardemulation/HostNfcFEmulationManager.java
new file mode 100644
index 0000000..db61a06
--- /dev/null
+++ b/src/com/android/nfc/cardemulation/HostNfcFEmulationManager.java
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2015 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.cardemulation;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.nfc.cardemulation.NfcFServiceInfo;
+import android.nfc.cardemulation.HostNfcFService;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+
+import com.android.nfc.NfcService;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+public class HostNfcFEmulationManager {
+    static final String TAG = "HostNfcFEmulationManager";
+    static final boolean DBG = false;
+
+    static final int STATE_IDLE = 0;
+    static final int STATE_W4_SERVICE = 1;
+    static final int STATE_XFER = 2;
+
+    /** NFCID2 length */
+    static final int NFCID2_LENGTH = 8;
+
+    /** Minimum NFC-F packets including length, command code and NFCID2 */
+    static final int MINIMUM_NFCF_PACKET_LENGTH = 10;
+
+    final Context mContext;
+    final RegisteredT3tIdentifiersCache mT3tIdentifiersCache;
+    final Messenger mMessenger = new Messenger (new MessageHandler());
+    final Object mLock;
+
+    // All variables below protected by mLock
+    ComponentName mEnabledFgServiceName;
+
+    Messenger mService;
+    boolean mServiceBound;
+    ComponentName mServiceName;
+
+    // mActiveService denotes the service interface
+    // that is the current active one, until a new packet
+    // comes in that may be resolved to a different service.
+    // On deactivation, mActiveService stops being valid.
+    Messenger mActiveService;
+    ComponentName mActiveServiceName;
+
+    int mState;
+    byte[] mPendingPacket;
+
+    public HostNfcFEmulationManager(Context context,
+            RegisteredT3tIdentifiersCache t3tIdentifiersCache) {
+        mContext = context;
+        mLock = new Object();
+        mEnabledFgServiceName = null;
+        mT3tIdentifiersCache = t3tIdentifiersCache;
+        mState = STATE_IDLE;
+    }
+
+    public void onEnabledForegroundNfcFServiceChanged(ComponentName service) {
+        synchronized (mLock) {
+            mEnabledFgServiceName = service;
+            if (service == null) {
+                sendDeactivateToActiveServiceLocked(HostNfcFService.DEACTIVATION_LINK_LOSS);
+                unbindServiceIfNeededLocked();
+            }
+        }
+    }
+
+    public void onHostEmulationActivated() {
+        if (DBG) Log.d(TAG, "notifyHostEmulationActivated");
+    }
+
+    public void onHostEmulationData(byte[] data) {
+        if (DBG) Log.d(TAG, "notifyHostEmulationData");
+        String nfcid2 = findNfcid2(data);
+        ComponentName resolvedServiceName = null;
+        synchronized (mLock) {
+            if (nfcid2 != null) {
+                NfcFServiceInfo resolvedService = mT3tIdentifiersCache.resolveNfcid2(nfcid2);
+                if (resolvedService != null) {
+                    resolvedServiceName = resolvedService.getComponent();
+                }
+            }
+            if (resolvedServiceName == null) {
+                if (mActiveServiceName == null) {
+                    return;
+                }
+                resolvedServiceName = mActiveServiceName;
+            }
+            // Check if resolvedService is actually currently enabled
+            if (mEnabledFgServiceName == null ||
+                    !mEnabledFgServiceName.equals(resolvedServiceName)) {
+                return;
+            }
+            if (DBG) Log.d(TAG, "resolvedServiceName: " + resolvedServiceName.toString() +
+                    "mState: " + String.valueOf(mState));
+            switch (mState) {
+            case STATE_IDLE:
+                Messenger existingService = bindServiceIfNeededLocked(resolvedServiceName);
+                if (existingService != null) {
+                    Log.d(TAG, "Binding to existing service");
+                    mState = STATE_XFER;
+                    sendDataToServiceLocked(existingService, data);
+                } else {
+                    // Waiting for service to be bound
+                    Log.d(TAG, "Waiting for new service.");
+                    // Queue packet to be used
+                    mPendingPacket = data;
+                    mState = STATE_W4_SERVICE;
+                }
+                break;
+            case STATE_W4_SERVICE:
+                Log.d(TAG, "Unexpected packet in STATE_W4_SERVICE");
+                break;
+            case STATE_XFER:
+                // Regular packet data
+                sendDataToServiceLocked(mActiveService, data);
+                break;
+            }
+        }
+    }
+
+    public void onHostEmulationDeactivated() {
+        if (DBG) Log.d(TAG, "notifyHostEmulationDeactivated");
+        synchronized (mLock) {
+            sendDeactivateToActiveServiceLocked(HostNfcFService.DEACTIVATION_LINK_LOSS);
+            mActiveService = null;
+            mActiveServiceName = null;
+            unbindServiceIfNeededLocked();
+            mState = STATE_IDLE;
+        }
+    }
+
+    public void onNfcDisabled() {
+        synchronized (mLock) {
+            sendDeactivateToActiveServiceLocked(HostNfcFService.DEACTIVATION_LINK_LOSS);
+            mEnabledFgServiceName = null;
+            mActiveService = null;
+            mActiveServiceName = null;
+            unbindServiceIfNeededLocked();
+            mState = STATE_IDLE;
+        }
+    }
+
+    public void onUserSwitched() {
+        synchronized (mLock) {
+            sendDeactivateToActiveServiceLocked(HostNfcFService.DEACTIVATION_LINK_LOSS);
+            mEnabledFgServiceName = null;
+            mActiveService = null;
+            mActiveServiceName = null;
+            unbindServiceIfNeededLocked();
+            mState = STATE_IDLE;
+        }
+    }
+
+    void sendDataToServiceLocked(Messenger service, byte[] data) {
+        if (DBG) Log.d(TAG, "sendDataToServiceLocked");
+        if (DBG) {
+            Log.d(TAG, "service: " +
+                    (service != null ? service.toString() : "null"));
+            Log.d(TAG, "mActiveService: " +
+                    (mActiveService != null ? mActiveService.toString() : "null"));
+        }
+        if (service != mActiveService) {
+            sendDeactivateToActiveServiceLocked(HostNfcFService.DEACTIVATION_LINK_LOSS);
+            mActiveService = service;
+            mActiveServiceName = mServiceName;
+        }
+        Message msg = Message.obtain(null, HostNfcFService.MSG_COMMAND_PACKET);
+        Bundle dataBundle = new Bundle();
+        dataBundle.putByteArray("data", data);
+        msg.setData(dataBundle);
+        msg.replyTo = mMessenger;
+        try {
+            Log.d(TAG, "Sending data to service");
+            if (DBG) Log.d(TAG, "data: " + getByteDump(data));
+            mActiveService.send(msg);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Remote service has died, dropping packet");
+        }
+    }
+
+    void sendDeactivateToActiveServiceLocked(int reason) {
+        if (DBG) Log.d(TAG, "sendDeactivateToActiveServiceLocked");
+        if (mActiveService == null) return;
+        Message msg = Message.obtain(null, HostNfcFService.MSG_DEACTIVATED);
+        msg.arg1 = reason;
+        try {
+            mActiveService.send(msg);
+        } catch (RemoteException e) {
+            // Don't care
+        }
+    }
+
+    Messenger bindServiceIfNeededLocked(ComponentName service) {
+        if (DBG) Log.d(TAG, "bindServiceIfNeededLocked");
+        if (mServiceBound && mServiceName.equals(service)) {
+            Log.d(TAG, "Service already bound.");
+            return mService;
+        } else {
+            Log.d(TAG, "Binding to service " + service);
+            unbindServiceIfNeededLocked();
+            Intent bindIntent = new Intent(HostNfcFService.SERVICE_INTERFACE);
+            bindIntent.setComponent(service);
+            if (mContext.bindServiceAsUser(bindIntent, mConnection,
+                    Context.BIND_AUTO_CREATE, UserHandle.CURRENT)) {
+            } else {
+                Log.e(TAG, "Could not bind service.");
+            }
+            return null;
+        }
+    }
+
+    void unbindServiceIfNeededLocked() {
+        if (DBG) Log.d(TAG, "unbindServiceIfNeededLocked");
+        if (mServiceBound) {
+            Log.d(TAG, "Unbinding from service " + mServiceName);
+            mContext.unbindService(mConnection);
+            mServiceBound = false;
+            mService = null;
+            mServiceName = null;
+        }
+    }
+
+    String findNfcid2(byte[] data) {
+        if (DBG) Log.d(TAG, "findNfcid2");
+        if (data == null || data.length < MINIMUM_NFCF_PACKET_LENGTH) {
+            if (DBG) Log.d(TAG, "Data size too small");
+            return null;
+        }
+        int nfcid2Offset = 2;
+        return bytesToString(data, nfcid2Offset, NFCID2_LENGTH);
+    }
+
+    private ServiceConnection mConnection = new ServiceConnection() {
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            synchronized (mLock) {
+                mService = new Messenger(service);
+                mServiceBound = true;
+                mServiceName = name;
+                Log.d(TAG, "Service bound");
+                mState = STATE_XFER;
+                // Send pending packet
+                if (mPendingPacket != null) {
+                    sendDataToServiceLocked(mService, mPendingPacket);
+                    mPendingPacket = null;
+                }
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            synchronized (mLock) {
+                Log.d(TAG, "Service unbound");
+                mService = null;
+                mServiceBound = false;
+                mServiceName = null;
+            }
+        }
+    };
+
+    class MessageHandler extends Handler {
+        @Override
+        public void handleMessage(Message msg) {
+            synchronized(mLock) {
+                if (mActiveService == null) {
+                    Log.d(TAG, "Dropping service response message; service no longer active.");
+                    return;
+                } else if (!msg.replyTo.getBinder().equals(mActiveService.getBinder())) {
+                    Log.d(TAG, "Dropping service response message; service no longer bound.");
+                    return;
+                }
+            }
+            if (msg.what == HostNfcFService.MSG_RESPONSE_PACKET) {
+                Bundle dataBundle = msg.getData();
+                if (dataBundle == null) {
+                    return;
+                }
+                byte[] data = dataBundle.getByteArray("data");
+                if (data == null) {
+                    return;
+                }
+                if (data.length == 0) {
+                    Log.e(TAG, "Invalid response packet");
+                    return;
+                }
+                if (data.length != (data[0] & 0xff)) {
+                    Log.e(TAG, "Invalid response packet");
+                    return;
+                }
+                int state;
+                synchronized(mLock) {
+                    state = mState;
+                }
+                if (state == STATE_XFER) {
+                    Log.d(TAG, "Sending data");
+                    if (DBG) Log.d(TAG, "data:" + getByteDump(data));
+                    NfcService.getInstance().sendData(data);
+                } else {
+                    Log.d(TAG, "Dropping data, wrong state " + Integer.toString(state));
+                }
+            }
+        }
+    }
+
+    static String bytesToString(byte[] bytes, int offset, int length) {
+        final char[] hexChars = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
+        char[] chars = new char[length * 2];
+        int byteValue;
+        for (int j = 0; j < length; j++) {
+            byteValue = bytes[offset + j] & 0xFF;
+            chars[j * 2] = hexChars[byteValue >>> 4];
+            chars[j * 2 + 1] = hexChars[byteValue & 0x0F];
+        }
+        return new String(chars);
+    }
+
+    private String getByteDump(final byte[] cmd) {
+        StringBuffer str = new StringBuffer("");
+        int letters = 8;
+        int i = 0;
+
+        if (cmd == null) {
+            str.append(" null\n");
+            return str.toString();
+        }
+
+        for (; i < cmd.length; i++) {
+            str.append(String.format(" %02X", cmd[i]));
+            if ((i % letters == letters - 1) || (i + 1 == cmd.length)) {
+                str.append("\n");
+            }
+        }
+
+        return str.toString();
+    }
+
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("Bound services: ");
+        if (mServiceBound) {
+            pw.println("    service: " + mServiceName);
+        }
+    }
+}
diff --git a/src/com/android/nfc/cardemulation/RegisteredNfcFServicesCache.java b/src/com/android/nfc/cardemulation/RegisteredNfcFServicesCache.java
new file mode 100644
index 0000000..cf188aa
--- /dev/null
+++ b/src/com/android/nfc/cardemulation/RegisteredNfcFServicesCache.java
@@ -0,0 +1,719 @@
+/*
+ * Copyright (C) 2015 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.cardemulation;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import android.app.ActivityManager;
+import android.content.BroadcastReceiver;
+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.content.pm.ServiceInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.nfc.cardemulation.NfcFServiceInfo;
+import android.nfc.cardemulation.NfcFCardEmulation;
+import android.nfc.cardemulation.HostNfcFService;
+import android.os.UserHandle;
+import android.util.AtomicFile;
+import android.util.Log;
+import android.util.SparseArray;
+import android.util.Xml;
+
+import com.android.internal.util.FastXmlSerializer;
+import com.google.android.collect.Maps;
+
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
+
+public class RegisteredNfcFServicesCache {
+    static final String XML_INDENT_OUTPUT_FEATURE = "http://xmlpull.org/v1/doc/features.html#indent-output";
+    static final String TAG = "RegisteredNfcFServicesCache";
+    static final boolean DBG = false;
+
+    final Context mContext;
+    final AtomicReference<BroadcastReceiver> mReceiver;
+
+    final Object mLock = new Object();
+    // All variables below synchronized on mLock
+
+    // mUserServices holds the card emulation services that are running for each user
+    final SparseArray<UserServices> mUserServices = new SparseArray<UserServices>();
+    final Callback mCallback;
+    final AtomicFile mDynamicSystemCodeNfcid2File;
+    boolean mActivated = false;
+
+    public interface Callback {
+        void onNfcFServicesUpdated(int userId, final List<NfcFServiceInfo> services);
+    };
+
+    static class DynamicSystemCode {
+        public final int uid;
+        public final String systemCode;
+
+        DynamicSystemCode(int uid, String systemCode) {
+            this.uid = uid;
+            this.systemCode = systemCode;
+        }
+    };
+
+    static class DynamicNfcid2 {
+        public final int uid;
+        public final String nfcid2;
+
+        DynamicNfcid2(int uid, String nfcid2) {
+            this.uid = uid;
+            this.nfcid2 = nfcid2;
+        }
+    };
+
+    private static class UserServices {
+        /**
+         * All services that have registered
+         */
+        final HashMap<ComponentName, NfcFServiceInfo> services =
+                Maps.newHashMap(); // Re-built at run-time
+        final HashMap<ComponentName, DynamicSystemCode> dynamicSystemCode =
+                Maps.newHashMap(); // In memory cache of dynamic System Code store
+        final HashMap<ComponentName, DynamicNfcid2> dynamicNfcid2 =
+                Maps.newHashMap(); // In memory cache of dynamic NFCID2 store
+    };
+
+    private UserServices findOrCreateUserLocked(int userId) {
+        UserServices userServices = mUserServices.get(userId);
+        if (userServices == null) {
+            userServices = new UserServices();
+            mUserServices.put(userId, userServices);
+        }
+        return userServices;
+    }
+
+    public RegisteredNfcFServicesCache(Context context, Callback callback) {
+        mContext = context;
+        mCallback = callback;
+
+        final BroadcastReceiver receiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+                String action = intent.getAction();
+                if (DBG) Log.d(TAG, "Intent action: " + action);
+                if (uid != -1) {
+                    boolean replaced = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false) &&
+                            (Intent.ACTION_PACKAGE_ADDED.equals(action) ||
+                             Intent.ACTION_PACKAGE_REMOVED.equals(action));
+                    if (!replaced) {
+                        int currentUser = ActivityManager.getCurrentUser();
+                        if (currentUser == UserHandle.getUserId(uid)) {
+                            invalidateCache(UserHandle.getUserId(uid));
+                        } else {
+                            // Cache will automatically be updated on user switch
+                        }
+                    } else {
+                        if (DBG) Log.d(TAG,
+                                "Ignoring package intent due to package being replaced.");
+                    }
+                }
+            }
+        };
+        mReceiver = new AtomicReference<BroadcastReceiver>(receiver);
+
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+        intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+        intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        intentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+        intentFilter.addAction(Intent.ACTION_PACKAGE_FIRST_LAUNCH);
+        intentFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
+        intentFilter.addDataScheme("package");
+        mContext.registerReceiverAsUser(mReceiver.get(), UserHandle.ALL, intentFilter, null, null);
+
+        // Register for events related to sdcard operations
+        IntentFilter sdFilter = new IntentFilter();
+        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
+        mContext.registerReceiverAsUser(mReceiver.get(), UserHandle.ALL, sdFilter, null, null);
+
+        File dataDir = mContext.getFilesDir();
+        mDynamicSystemCodeNfcid2File =
+                new AtomicFile(new File(dataDir, "dynamic_systemcode_nfcid2.xml"));
+    }
+
+    void initialize() {
+        synchronized (mLock) {
+            readDynamicSystemCodeNfcid2Locked();
+        }
+        invalidateCache(ActivityManager.getCurrentUser());
+    }
+
+    void dump(ArrayList<NfcFServiceInfo> services) {
+        for (NfcFServiceInfo service : services) {
+            Log.d(TAG, service.toString());
+        }
+    }
+
+    boolean containsServiceLocked(ArrayList<NfcFServiceInfo> services,
+            ComponentName componentName) {
+        for (NfcFServiceInfo service : services) {
+            if (service.getComponent().equals(componentName)) return true;
+        }
+        return false;
+    }
+
+    public boolean hasService(int userId, ComponentName componentName) {
+        return getService(userId, componentName) != null;
+    }
+
+    public NfcFServiceInfo getService(int userId, ComponentName componentName) {
+        synchronized (mLock) {
+            UserServices userServices = findOrCreateUserLocked(userId);
+            return userServices.services.get(componentName);
+        }
+    }
+
+    public List<NfcFServiceInfo> getServices(int userId) {
+        final ArrayList<NfcFServiceInfo> services = new ArrayList<NfcFServiceInfo>();
+        synchronized (mLock) {
+            UserServices userServices = findOrCreateUserLocked(userId);
+            services.addAll(userServices.services.values());
+        }
+        return services;
+    }
+
+    ArrayList<NfcFServiceInfo> getInstalledServices(int userId) {
+        if (DBG) Log.d(TAG, "getInstalledServices");
+        PackageManager pm;
+        try {
+            pm = mContext.createPackageContextAsUser("android", 0,
+                    new UserHandle(userId)).getPackageManager();
+        } catch (NameNotFoundException e) {
+            Log.e(TAG, "Could not create user package context");
+            return null;
+        }
+
+        ArrayList<NfcFServiceInfo> validServices = new ArrayList<NfcFServiceInfo>();
+
+        List<ResolveInfo> resolvedServices = pm.queryIntentServicesAsUser(
+                new Intent(HostNfcFService.SERVICE_INTERFACE),
+                PackageManager.GET_META_DATA, userId);
+
+        for (ResolveInfo resolvedService : resolvedServices) {
+            try {
+                ServiceInfo si = resolvedService.serviceInfo;
+                ComponentName componentName = new ComponentName(si.packageName, si.name);
+                // Check if the package holds the NFC permission
+                if (pm.checkPermission(android.Manifest.permission.NFC, si.packageName) !=
+                        PackageManager.PERMISSION_GRANTED) {
+                    Log.e(TAG, "Skipping NfcF service " + componentName +
+                            ": it does not require the permission " +
+                            android.Manifest.permission.NFC);
+                    continue;
+                }
+                if (!android.Manifest.permission.BIND_NFC_SERVICE.equals(
+                        si.permission)) {
+                    Log.e(TAG, "Skipping NfcF service " + componentName +
+                            ": it does not require the permission " +
+                            android.Manifest.permission.BIND_NFC_SERVICE);
+                    continue;
+                }
+                NfcFServiceInfo service = new NfcFServiceInfo(pm, resolvedService);
+                if (service != null) {
+                    validServices.add(service);
+                }
+            } catch (XmlPullParserException e) {
+                Log.w(TAG, "Unable to load component info " + resolvedService.toString(), e);
+            } catch (IOException e) {
+                Log.w(TAG, "Unable to load component info " + resolvedService.toString(), e);
+            }
+        }
+
+        return validServices;
+    }
+
+    public void invalidateCache(int userId) {
+        if (DBG) Log.d(TAG, "invalidateCache");
+        final ArrayList<NfcFServiceInfo> validServices = getInstalledServices(userId);
+        if (validServices == null) {
+            return;
+        }
+        ArrayList<NfcFServiceInfo> newServices = null;
+        synchronized (mLock) {
+            UserServices userServices = findOrCreateUserLocked(userId);
+
+            // Check update
+            ArrayList<NfcFServiceInfo> cachedServices =
+                    new ArrayList<NfcFServiceInfo>(userServices.services.values());
+            ArrayList<NfcFServiceInfo> toBeAdded = new ArrayList<NfcFServiceInfo>();
+            ArrayList<NfcFServiceInfo> toBeRemoved = new ArrayList<NfcFServiceInfo>();
+            boolean matched = false;
+            for (NfcFServiceInfo validService : validServices) {
+                for (NfcFServiceInfo cachedService : cachedServices) {
+                    if (validService.equals(cachedService)) {
+                        matched = true;
+                        break;
+                    }
+                }
+                if (!matched) {
+                    toBeAdded.add(validService);
+                }
+                matched = false;
+            }
+            for (NfcFServiceInfo cachedService : cachedServices) {
+                for (NfcFServiceInfo validService : validServices) {
+                    if (cachedService.equals(validService)) {
+                        matched = true;
+                        break;
+                    }
+                }
+                if (!matched) {
+                    toBeRemoved.add(cachedService);
+                }
+                matched = false;
+            }
+            if (toBeAdded.size() == 0 && toBeRemoved.size() == 0) {
+                Log.d(TAG, "Service unchanged, not updating");
+                return;
+            }
+
+            // Update cache
+            for (NfcFServiceInfo service : toBeAdded) {
+                userServices.services.put(service.getComponent(), service);
+                if (DBG) Log.d(TAG, "Added service: " + service.getComponent());
+            }
+            for (NfcFServiceInfo service : toBeRemoved) {
+                userServices.services.remove(service.getComponent());
+                if (DBG) Log.d(TAG, "Removed service: " + service.getComponent());
+            }
+            // Apply dynamic System Code mappings
+            ArrayList<ComponentName> toBeRemovedDynamicSystemCode =
+                    new ArrayList<ComponentName>();
+            for (Map.Entry<ComponentName, DynamicSystemCode> entry :
+                    userServices.dynamicSystemCode.entrySet()) {
+                // Verify component / uid match
+                ComponentName componentName = entry.getKey();
+                DynamicSystemCode dynamicSystemCode = entry.getValue();
+                NfcFServiceInfo service = userServices.services.get(componentName);
+                if (service == null || (service.getUid() != dynamicSystemCode.uid)) {
+                    toBeRemovedDynamicSystemCode.add(componentName);
+                    continue;
+                } else {
+                    service.setOrReplaceDynamicSystemCode(dynamicSystemCode.systemCode);
+                }
+            }
+            // Apply dynamic NFCID2 mappings
+            ArrayList<ComponentName> toBeRemovedDynamicNfcid2 =
+                    new ArrayList<ComponentName>();
+            for (Map.Entry<ComponentName, DynamicNfcid2> entry :
+                    userServices.dynamicNfcid2.entrySet()) {
+                // Verify component / uid match
+                ComponentName componentName = entry.getKey();
+                DynamicNfcid2 dynamicNfcid2 = entry.getValue();
+                NfcFServiceInfo service = userServices.services.get(componentName);
+                if (service == null || (service.getUid() != dynamicNfcid2.uid)) {
+                    toBeRemovedDynamicNfcid2.add(componentName);
+                    continue;
+                } else {
+                    service.setOrReplaceDynamicNfcid2(dynamicNfcid2.nfcid2);
+                }
+            }
+            for (ComponentName removedComponent : toBeRemovedDynamicSystemCode) {
+                Log.d(TAG, "Removing dynamic System Code registered by " +
+                        removedComponent);
+                userServices.dynamicSystemCode.remove(removedComponent);
+            }
+            for (ComponentName removedComponent : toBeRemovedDynamicNfcid2) {
+                Log.d(TAG, "Removing dynamic NFCID2 registered by " +
+                        removedComponent);
+                userServices.dynamicNfcid2.remove(removedComponent);
+            }
+            // Assign a NFCID2 for services requesting a random NFCID2, then apply
+            boolean nfcid2Assigned = false;
+            for (Map.Entry<ComponentName, NfcFServiceInfo> entry :
+                userServices.services.entrySet()) {
+                NfcFServiceInfo service = entry.getValue();
+                if (service.getNfcid2().equalsIgnoreCase("RANDOM")) {
+                    String randomNfcid2 = generateRandomNfcid2();
+                    service.setOrReplaceDynamicNfcid2(randomNfcid2);
+                    DynamicNfcid2 dynamicNfcid2 =
+                            new DynamicNfcid2(service.getUid(), randomNfcid2);
+                    userServices.dynamicNfcid2.put(entry.getKey(), dynamicNfcid2);
+                    nfcid2Assigned = true;
+                }
+            }
+
+            // Persist to filesystem
+            if (toBeRemovedDynamicSystemCode.size() > 0 ||
+                    toBeRemovedDynamicNfcid2.size() > 0 ||
+                    nfcid2Assigned) {
+                writeDynamicSystemCodeNfcid2Locked();
+            }
+
+            newServices = new ArrayList<NfcFServiceInfo>(userServices.services.values());
+        }
+        mCallback.onNfcFServicesUpdated(userId, Collections.unmodifiableList(newServices));
+        if (DBG) dump(newServices);
+    }
+
+    private void readDynamicSystemCodeNfcid2Locked() {
+        if (DBG) Log.d(TAG, "readDynamicSystemCodeNfcid2Locked");
+        FileInputStream fis = null;
+        try {
+            if (!mDynamicSystemCodeNfcid2File.getBaseFile().exists()) {
+                Log.d(TAG, "Dynamic System Code, NFCID2 file does not exist.");
+                return;
+            }
+            fis = mDynamicSystemCodeNfcid2File.openRead();
+            XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(fis, null);
+            int eventType = parser.getEventType();
+            while (eventType != XmlPullParser.START_TAG &&
+                    eventType != XmlPullParser.END_DOCUMENT) {
+                eventType = parser.next();
+            }
+            String tagName = parser.getName();
+            if ("services".equals(tagName)) {
+                ComponentName componentName = null;
+                int currentUid = -1;
+                String systemCode = null;
+                String nfcid2 = null;
+                String description = null;
+                while (eventType != XmlPullParser.END_DOCUMENT) {
+                    tagName = parser.getName();
+                    if (eventType == XmlPullParser.START_TAG) {
+                        if ("service".equals(tagName) && parser.getDepth() == 2) {
+                            String compString =
+                                    parser.getAttributeValue(null, "component");
+                            String uidString =
+                                    parser.getAttributeValue(null, "uid");
+                            String systemCodeString =
+                                    parser.getAttributeValue(null, "system-code");
+                            String descriptionString =
+                                    parser.getAttributeValue(null, "description");
+                            String nfcid2String =
+                                    parser.getAttributeValue(null, "nfcid2");
+                            if (compString == null || uidString == null) {
+                                Log.e(TAG, "Invalid service attributes");
+                            } else {
+                                try {
+                                    componentName = ComponentName.unflattenFromString(compString);
+                                    currentUid = Integer.parseInt(uidString);
+                                    systemCode = systemCodeString;
+                                    description = descriptionString;
+                                    nfcid2 = nfcid2String;
+                                } catch (NumberFormatException e) {
+                                    Log.e(TAG, "Could not parse service uid");
+                                }
+                            }
+                        }
+                    } else if (eventType == XmlPullParser.END_TAG) {
+                        if ("service".equals(tagName)) {
+                            // See if we have a valid service
+                            if (componentName != null && currentUid >= 0) {
+                                final int userId = UserHandle.getUserId(currentUid);
+                                UserServices userServices = findOrCreateUserLocked(userId);
+                                if (systemCode != null) {
+                                    DynamicSystemCode dynamicSystemCode =
+                                            new DynamicSystemCode(currentUid, systemCode);
+                                    userServices.dynamicSystemCode.put(
+                                            componentName, dynamicSystemCode);
+                                }
+                                if (nfcid2 != null) {
+                                    DynamicNfcid2 dynamicNfcid2 =
+                                            new DynamicNfcid2(currentUid, nfcid2);
+                                    userServices.dynamicNfcid2.put(
+                                            componentName, dynamicNfcid2);
+                                }
+                            }
+                            componentName = null;
+                            currentUid = -1;
+                            systemCode = null;
+                            description = null;
+                            nfcid2 = null;
+                        }
+                    }
+                    eventType = parser.next();
+                };
+            }
+        } catch (Exception e) {
+            Log.e(TAG, "Could not parse dynamic System Code, NFCID2 file, trashing.");
+            mDynamicSystemCodeNfcid2File.delete();
+        } finally {
+            if (fis != null) {
+                try {
+                    fis.close();
+                } catch (IOException e) {
+                }
+            }
+        }
+    }
+
+    private boolean writeDynamicSystemCodeNfcid2Locked() {
+        if (DBG) Log.d(TAG, "writeDynamicSystemCodeNfcid2Locked");
+        FileOutputStream fos = null;
+        try {
+            fos = mDynamicSystemCodeNfcid2File.startWrite();
+            XmlSerializer out = new FastXmlSerializer();
+            out.setOutput(fos, "utf-8");
+            out.startDocument(null, true);
+            out.setFeature(XML_INDENT_OUTPUT_FEATURE, true);
+            out.startTag(null, "services");
+            for (int i = 0; i < mUserServices.size(); i++) {
+                final UserServices userServices = mUserServices.valueAt(i);
+                for (Map.Entry<ComponentName, DynamicSystemCode> entry :
+                        userServices.dynamicSystemCode.entrySet()) {
+                    out.startTag(null, "service");
+                    out.attribute(null, "component", entry.getKey().flattenToString());
+                    out.attribute(null, "uid", Integer.toString(entry.getValue().uid));
+                    out.attribute(null, "system-code", entry.getValue().systemCode);
+                    if (userServices.dynamicNfcid2.containsKey(entry.getKey())) {
+                        out.attribute(null, "nfcid2",
+                                userServices.dynamicNfcid2.get(entry.getKey()).nfcid2);
+                    }
+                    out.endTag(null, "service");
+                }
+                for (Map.Entry<ComponentName, DynamicNfcid2> entry :
+                        userServices.dynamicNfcid2.entrySet()) {
+                    if (!userServices.dynamicSystemCode.containsKey(entry.getKey())) {
+                        out.startTag(null, "service");
+                        out.attribute(null, "component", entry.getKey().flattenToString());
+                        out.attribute(null, "uid", Integer.toString(entry.getValue().uid));
+                        out.attribute(null, "nfcid2", entry.getValue().nfcid2);
+                        out.endTag(null, "service");
+                    }
+                }
+            }
+            out.endTag(null, "services");
+            out.endDocument();
+            mDynamicSystemCodeNfcid2File.finishWrite(fos);
+            return true;
+        } catch (Exception e) {
+            Log.e(TAG, "Error writing dynamic System Code, NFCID2", e);
+            if (fos != null) {
+                mDynamicSystemCodeNfcid2File.failWrite(fos);
+            }
+            return false;
+        }
+    }
+
+    public boolean registerSystemCodeForService(int userId, int uid,
+            ComponentName componentName, String systemCode) {
+        if (DBG) Log.d(TAG, "registerSystemCodeForService");
+        ArrayList<NfcFServiceInfo> newServices = null;
+        boolean success;
+        synchronized (mLock) {
+            if (mActivated) {
+                Log.d(TAG, "failed to register System Code during activation");
+                return false;
+            }
+            UserServices userServices = findOrCreateUserLocked(userId);
+            // Check if we can find this service
+            NfcFServiceInfo service = getService(userId, componentName);
+            if (service == null) {
+                Log.e(TAG, "Service " + componentName + " does not exist.");
+                return false;
+            }
+            if (service.getUid() != uid) {
+                // This is probably a good indication something is wrong here.
+                // Either newer service installed with different uid (but then
+                // we should have known about it), or somebody calling us from
+                // a different uid.
+                Log.e(TAG, "UID mismatch.");
+                return false;
+            }
+            if (!systemCode.equalsIgnoreCase("NULL") &&
+                    !NfcFCardEmulation.isValidSystemCode(systemCode)) {
+                Log.e(TAG, "System Code " + systemCode + " is not a valid System Code");
+                return false;
+            }
+            // Apply dynamic System Code mappings
+            systemCode = systemCode.toUpperCase();
+            DynamicSystemCode oldDynamicSystemCode =
+                    userServices.dynamicSystemCode.get(componentName);
+            DynamicSystemCode dynamicSystemCode = new DynamicSystemCode(uid, systemCode);
+            userServices.dynamicSystemCode.put(componentName, dynamicSystemCode);
+            success = writeDynamicSystemCodeNfcid2Locked();
+            if (success) {
+                service.setOrReplaceDynamicSystemCode(systemCode);
+                newServices = new ArrayList<NfcFServiceInfo>(userServices.services.values());
+            } else {
+                Log.e(TAG, "Failed to persist System Code.");
+                // Undo registration
+                if (oldDynamicSystemCode == null) {
+                    userServices.dynamicSystemCode.remove(componentName);
+                } else {
+                    userServices.dynamicSystemCode.put(componentName, oldDynamicSystemCode);
+                }
+            }
+        }
+        if (success) {
+            // Make callback without the lock held
+            mCallback.onNfcFServicesUpdated(userId, newServices);
+        }
+        return success;
+    }
+
+    public String getSystemCodeForService(int userId, int uid, ComponentName componentName) {
+        if (DBG) Log.d(TAG, "getSystemCodeForService");
+        NfcFServiceInfo service = getService(userId, componentName);
+        if (service != null) {
+            if (service.getUid() != uid) {
+                Log.e(TAG, "UID mismatch");
+                return null;
+            }
+            return service.getSystemCode();
+        } else {
+            Log.e(TAG, "Could not find service " + componentName);
+            return null;
+        }
+    }
+
+    public boolean removeSystemCodeForService(int userId, int uid, ComponentName componentName) {
+        if (DBG) Log.d(TAG, "removeSystemCodeForService");
+        return registerSystemCodeForService(userId, uid, componentName, "NULL");
+    }
+
+    public boolean setNfcid2ForService(int userId, int uid,
+            ComponentName componentName, String nfcid2) {
+        if (DBG) Log.d(TAG, "setNfcid2ForService");
+        ArrayList<NfcFServiceInfo> newServices = null;
+        boolean success;
+        synchronized (mLock) {
+            if (mActivated) {
+                Log.d(TAG, "failed to set NFCID2 during activation");
+                return false;
+            }
+            UserServices userServices = findOrCreateUserLocked(userId);
+            // Check if we can find this service
+            NfcFServiceInfo service = getService(userId, componentName);
+            if (service == null) {
+                Log.e(TAG, "Service " + componentName + " does not exist.");
+                return false;
+            }
+            if (service.getUid() != uid) {
+                // This is probably a good indication something is wrong here.
+                // Either newer service installed with different uid (but then
+                // we should have known about it), or somebody calling us from
+                // a different uid.
+                Log.e(TAG, "UID mismatch.");
+                return false;
+            }
+            if (!NfcFCardEmulation.isValidNfcid2(nfcid2)) {
+                Log.e(TAG, "NFCID2 " + nfcid2 + " is not a valid NFCID2");
+                return false;
+            }
+            // Apply dynamic NFCID2 mappings
+            nfcid2 = nfcid2.toUpperCase();
+            DynamicNfcid2 oldDynamicNfcid2 = userServices.dynamicNfcid2.get(componentName);
+            DynamicNfcid2 dynamicNfcid2 = new DynamicNfcid2(uid, nfcid2);
+            userServices.dynamicNfcid2.put(componentName, dynamicNfcid2);
+            success = writeDynamicSystemCodeNfcid2Locked();
+            if (success) {
+                service.setOrReplaceDynamicNfcid2(nfcid2);
+                newServices = new ArrayList<NfcFServiceInfo>(userServices.services.values());
+            } else {
+                Log.e(TAG, "Failed to persist NFCID2.");
+                // Undo registration
+                if (oldDynamicNfcid2 == null) {
+                    userServices.dynamicNfcid2.remove(componentName);
+                } else {
+                    userServices.dynamicNfcid2.put(componentName, oldDynamicNfcid2);
+                }
+            }
+        }
+        if (success) {
+            // Make callback without the lock held
+            mCallback.onNfcFServicesUpdated(userId, newServices);
+        }
+        return success;
+    }
+
+    public String getNfcid2ForService(int userId, int uid, ComponentName componentName) {
+        if (DBG) Log.d(TAG, "getNfcid2ForService");
+        NfcFServiceInfo service = getService(userId, componentName);
+        if (service != null) {
+            if (service.getUid() != uid) {
+                Log.e(TAG, "UID mismatch");
+                return null;
+            }
+            return service.getNfcid2();
+        } else {
+            Log.e(TAG, "Could not find service " + componentName);
+            return null;
+        }
+    }
+
+    public void onHostEmulationActivated() {
+        if (DBG) Log.d(TAG, "onHostEmulationActivated");
+        synchronized (mLock) {
+            mActivated = true;
+        }
+    }
+
+    public void onHostEmulationDeactivated() {
+        if (DBG) Log.d(TAG, "onHostEmulationDeactivated");
+        synchronized (mLock) {
+            mActivated = false;
+        }
+    }
+
+    public void onNfcDisabled() {
+        synchronized (mLock) {
+            mActivated = false;
+        }
+    }
+
+    private String generateRandomNfcid2() {
+        long min = 0L;
+        long max = 0xFFFFFFFFFFFFL;
+
+        long randomNfcid2 = (long)Math.floor(Math.random() * (max-min+1)) + min;
+        return String.format("02FE%02X%02X%02X%02X%02X%02X",
+                (randomNfcid2 >>> 8 * 5) & 0xFF, (randomNfcid2 >>> 8 * 4) & 0xFF,
+                (randomNfcid2 >>> 8 * 3) & 0xFF, (randomNfcid2 >>> 8 * 2) & 0xFF,
+                (randomNfcid2 >>> 8 * 1) & 0xFF, (randomNfcid2 >>> 8 * 0) & 0xFF);
+    }
+
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("Registered HCE services for current user: ");
+        synchronized (mLock) {
+            UserServices userServices = findOrCreateUserLocked(ActivityManager.getCurrentUser());
+            for (NfcFServiceInfo service : userServices.services.values()) {
+                service.dump(fd, pw, args);
+                pw.println("");
+            }
+            pw.println("");
+        }
+    }
+
+}
diff --git a/src/com/android/nfc/cardemulation/RegisteredT3tIdentifiersCache.java b/src/com/android/nfc/cardemulation/RegisteredT3tIdentifiersCache.java
new file mode 100644
index 0000000..47408a8
--- /dev/null
+++ b/src/com/android/nfc/cardemulation/RegisteredT3tIdentifiersCache.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2015 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.cardemulation;
+
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.nfc.cardemulation.NfcFServiceInfo;
+import android.util.Log;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.HashMap;
+
+public class RegisteredT3tIdentifiersCache {
+    static final String TAG = "RegisteredT3tIdentifiersCache";
+
+    static final boolean DBG = false;
+
+    // All NFC-F services that have registered
+    List<NfcFServiceInfo> mServices = new ArrayList<NfcFServiceInfo>();
+
+    final HashMap<String, NfcFServiceInfo> mForegroundT3tIdentifiersCache =
+            new HashMap<String, NfcFServiceInfo>();
+
+    ComponentName mEnabledForegroundService;
+
+    final class T3tIdentifier {
+        public final String systemCode;
+        public final String nfcid2;
+
+        T3tIdentifier(String systemCode, String nfcid2) {
+            this.systemCode = systemCode;
+            this.nfcid2 = nfcid2;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+
+            T3tIdentifier that = (T3tIdentifier) o;
+            if (!systemCode.equalsIgnoreCase(that.systemCode)) return false;
+            if (!nfcid2.equalsIgnoreCase(that.nfcid2)) return false;
+
+            return true;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = systemCode.hashCode();
+            result = 31 * result + nfcid2.hashCode();
+            return result;
+        }
+    }
+
+    final Context mContext;
+    final SystemCodeRoutingManager mRoutingManager;
+
+    final Object mLock = new Object();
+
+    boolean mNfcEnabled = false;
+
+    public RegisteredT3tIdentifiersCache(Context context) {
+        Log.d(TAG, "RegisteredT3tIdentifiersCache");
+        mContext = context;
+        mRoutingManager = new SystemCodeRoutingManager();
+    }
+
+    public NfcFServiceInfo resolveNfcid2(String nfcid2) {
+        synchronized (mLock) {
+            if (DBG) Log.d(TAG, "resolveNfcid2: resolving NFCID " + nfcid2);
+            NfcFServiceInfo resolveInfo;
+            resolveInfo = mForegroundT3tIdentifiersCache.get(nfcid2);
+            Log.d(TAG,
+                    "Resolved to: " + (resolveInfo == null ? "null" : resolveInfo.toString()));
+            return resolveInfo;
+        }
+    }
+
+    void generateForegroundT3tIdentifiersCacheLocked() {
+        if (DBG) Log.d(TAG, "generateForegroundT3tIdentifiersCacheLocked");
+        mForegroundT3tIdentifiersCache.clear();
+        if (mEnabledForegroundService != null) {
+            for (NfcFServiceInfo service : mServices) {
+                if (mEnabledForegroundService.equals(service.getComponent())) {
+                    if (!service.getSystemCode().equalsIgnoreCase("NULL") &&
+                            !service.getNfcid2().equalsIgnoreCase("NULL")) {
+                        mForegroundT3tIdentifiersCache.put(service.getNfcid2(), service);
+                    }
+                    break;
+                }
+            }
+        }
+
+        if (DBG) {
+            Log.d(TAG, "mForegroundT3tIdentifiersCache: size=" +
+                    mForegroundT3tIdentifiersCache.size());
+            for (Map.Entry<String, NfcFServiceInfo> entry :
+                    mForegroundT3tIdentifiersCache.entrySet()) {
+                Log.d(TAG, "    " + entry.getKey() +
+                        "/" + entry.getValue().getComponent().toString());
+            }
+        }
+
+        updateRoutingLocked();
+    }
+
+    void updateRoutingLocked() {
+        if (DBG) Log.d(TAG, "updateRoutingLocked");
+        if (!mNfcEnabled) {
+            Log.d(TAG, "Not updating routing table because NFC is off.");
+            return;
+        }
+        List<T3tIdentifier> t3tIdentifiers = new ArrayList<T3tIdentifier>();
+        Iterator<Map.Entry<String, NfcFServiceInfo>> it;
+        // Register foreground service
+        it = mForegroundT3tIdentifiersCache.entrySet().iterator();
+        while (it.hasNext()) {
+            Map.Entry<String, NfcFServiceInfo> entry =
+                    (Map.Entry<String, NfcFServiceInfo>) it.next();
+            t3tIdentifiers.add(new T3tIdentifier(
+                    entry.getValue().getSystemCode(), entry.getValue().getNfcid2()));
+        }
+        mRoutingManager.configureRouting(t3tIdentifiers);
+    }
+
+    public void onServicesUpdated(int userId, List<NfcFServiceInfo> services) {
+        if (DBG) Log.d(TAG, "onServicesUpdated");
+        synchronized (mLock) {
+            if (ActivityManager.getCurrentUser() == userId) {
+                // Rebuild our internal data-structures
+                mServices = services;
+            } else {
+                Log.d(TAG, "Ignoring update because it's not for the current user.");
+            }
+        }
+    }
+
+    public void onEnabledForegroundNfcFServiceChanged(ComponentName component) {
+        if (DBG) Log.d(TAG, "Enabled foreground service changed.");
+        synchronized (mLock) {
+            if (component != null) {
+                if (mEnabledForegroundService != null) {
+                    return;
+                }
+                mEnabledForegroundService = component;
+            } else {
+                if (mEnabledForegroundService == null) {
+                    return;
+                }
+                mEnabledForegroundService = null;
+            }
+            generateForegroundT3tIdentifiersCacheLocked();
+        }
+    }
+
+    public void onNfcEnabled() {
+        synchronized (mLock) {
+            mNfcEnabled = true;
+        }
+    }
+
+    public void onNfcDisabled() {
+        synchronized (mLock) {
+            mNfcEnabled = false;
+            mForegroundT3tIdentifiersCache.clear();
+            mEnabledForegroundService = null;
+        }
+        mRoutingManager.onNfccRoutingTableCleared();
+    }
+
+    public void onUserSwitched() {
+        synchronized (mLock) {
+            mForegroundT3tIdentifiersCache.clear();
+            updateRoutingLocked();
+            mEnabledForegroundService = null;
+        }
+    }
+
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("    T3T Identifier cache entries: ");
+        for (Map.Entry<String, NfcFServiceInfo> entry : mForegroundT3tIdentifiersCache.entrySet()) {
+            pw.println("    NFCID2: " + entry.getKey());
+            pw.println("    NfcFServiceInfo: ");
+            entry.getValue().dump(fd, pw, args);
+        }
+        pw.println("");
+        mRoutingManager.dump(fd, pw, args);
+        pw.println("");
+    }
+}
diff --git a/src/com/android/nfc/cardemulation/SystemCodeRoutingManager.java b/src/com/android/nfc/cardemulation/SystemCodeRoutingManager.java
new file mode 100644
index 0000000..3067573
--- /dev/null
+++ b/src/com/android/nfc/cardemulation/SystemCodeRoutingManager.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2015 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.cardemulation;
+
+import android.util.Log;
+
+import com.android.nfc.NfcService;
+import com.android.nfc.cardemulation.RegisteredT3tIdentifiersCache.T3tIdentifier;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+public class SystemCodeRoutingManager {
+    static final String TAG = "SystemCodeRoutingManager";
+
+    static final boolean DBG = false;
+
+    final Object mLock = new Object();
+
+    List<T3tIdentifier> mConfiguredT3tIdentifiers =
+            new ArrayList<T3tIdentifier>();
+
+    public boolean configureRouting(List<T3tIdentifier> t3tIdentifiers) {
+        if (DBG) Log.d(TAG, "configureRouting");
+        List<T3tIdentifier> toBeAdded = new ArrayList<T3tIdentifier>();
+        List<T3tIdentifier> toBeRemoved = new ArrayList<T3tIdentifier>();
+        synchronized (mLock) {
+            for (T3tIdentifier t3tIdentifier : t3tIdentifiers) {
+                if (!mConfiguredT3tIdentifiers.contains(t3tIdentifier)) {
+                    toBeAdded.add(t3tIdentifier);
+                }
+            }
+            for (T3tIdentifier t3tIdentifier : mConfiguredT3tIdentifiers) {
+                if (!t3tIdentifiers.contains(t3tIdentifier)) {
+                    toBeRemoved.add(t3tIdentifier);
+                }
+            }
+            if (toBeAdded.size() <= 0 && toBeRemoved.size() <= 0) {
+                Log.d(TAG, "Routing table unchanged, not updating");
+                return false;
+            }
+            // Update internal structures
+            for (T3tIdentifier t3tIdentifier : toBeRemoved) {
+                if (DBG) Log.d(TAG, "deregisterNfcFSystemCodeonDh:");
+                NfcService.getInstance().deregisterT3tIdentifier(
+                        t3tIdentifier.systemCode, t3tIdentifier.nfcid2);
+            }
+            for (T3tIdentifier t3tIdentifier : toBeAdded) {
+                if (DBG) Log.d(TAG, "registerNfcFSystemCodeonDh:");
+                NfcService.getInstance().registerT3tIdentifier(
+                        t3tIdentifier.systemCode, t3tIdentifier.nfcid2);
+            }
+            if (DBG) {
+                Log.d(TAG, "(Before) mConfiguredT3tIdentifiers: size=" +
+                        mConfiguredT3tIdentifiers.size());
+                for (T3tIdentifier t3tIdentifier : mConfiguredT3tIdentifiers) {
+                    Log.d(TAG, "    " + t3tIdentifier.systemCode +
+                            "/" + t3tIdentifier.nfcid2);
+                }
+                Log.d(TAG, "(After) mConfiguredT3tIdentifiers: size=" +
+                        t3tIdentifiers.size());
+                for (T3tIdentifier t3tIdentifier : t3tIdentifiers) {
+                    Log.d(TAG, "    " + t3tIdentifier.systemCode +
+                            "/" + t3tIdentifier.nfcid2);
+                }
+            }
+            mConfiguredT3tIdentifiers = t3tIdentifiers;
+        }
+
+        // And finally commit the routing
+        NfcService.getInstance().commitRouting();
+
+        return true;
+    }
+
+    /**
+     * This notifies that the SystemCode routing table in the controller
+     * has been cleared (usually due to NFC being turned off).
+     */
+    public void onNfccRoutingTableCleared() {
+        // The routing table in the controller was cleared
+        // To stay in sync, clear our own tables.
+        synchronized (mLock) {
+            if (DBG) Log.d(TAG, "onNfccRoutingTableCleared");
+            NfcService.getInstance().clearT3tIdentifiersCache();
+            mConfiguredT3tIdentifiers.clear();
+        }
+    }
+
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("Routing table:");
+        synchronized (mLock) {
+            for (T3tIdentifier t3tIdentifier : mConfiguredT3tIdentifiers) {
+                pw.println("    " + t3tIdentifier.systemCode +
+                        "/" + t3tIdentifier.nfcid2);
+            }
+        }
+    }
+}
diff --git a/src/com/android/nfc/handover/BluetoothPeripheralHandover.java b/src/com/android/nfc/handover/BluetoothPeripheralHandover.java
index 10400be..b43961b 100644
--- a/src/com/android/nfc/handover/BluetoothPeripheralHandover.java
+++ b/src/com/android/nfc/handover/BluetoothPeripheralHandover.java
@@ -22,6 +22,7 @@
 import android.bluetooth.BluetoothHeadset;
 import android.bluetooth.BluetoothInputDevice;
 import android.bluetooth.BluetoothProfile;
+import android.bluetooth.OobData;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -94,6 +95,7 @@
     int mHfpResult;  // used only in STATE_CONNECTING and STATE_DISCONNETING
     int mA2dpResult; // used only in STATE_CONNECTING and STATE_DISCONNETING
     int mHidResult;
+    OobData mOobData;
 
     // protected by mLock
     BluetoothA2dp mA2dp;
@@ -105,12 +107,13 @@
     }
 
     public BluetoothPeripheralHandover(Context context, BluetoothDevice device, String name,
-                                       int transport, Callback callback) {
+                                       int transport, OobData oobData, Callback callback) {
         checkMainThread();  // mHandler must get get constructed on Main Thread for toasts to work
         mContext = context;
         mDevice = device;
         mName = name;
         mTransport = transport;
+        mOobData = oobData;
         mCallback = callback;
         mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
 
@@ -389,9 +392,14 @@
     void startBonding() {
         mState = STATE_BONDING;
         toast(getToastString(R.string.pairing_peripheral));
-        if (!mDevice.createBond(mTransport)) {
-            toast(getToastString(R.string.pairing_peripheral_failed));
-            complete(false);
+        if (mOobData != null) {
+            if (!mDevice.createBondOutOfBand(mTransport, mOobData)) {
+                toast(getToastString(R.string.pairing_peripheral_failed));
+                complete(false);
+            }
+        } else if (!mDevice.createBond(mTransport)) {
+                toast(getToastString(R.string.pairing_peripheral_failed));
+                complete(false);
         }
     }
 
diff --git a/src/com/android/nfc/handover/HandoverDataParser.java b/src/com/android/nfc/handover/HandoverDataParser.java
index 691488f..eb77aaa 100644
--- a/src/com/android/nfc/handover/HandoverDataParser.java
+++ b/src/com/android/nfc/handover/HandoverDataParser.java
@@ -26,6 +26,7 @@
 
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
+import android.bluetooth.OobData;
 import android.content.Context;
 import android.content.Intent;
 import android.nfc.FormatException;
@@ -59,8 +60,12 @@
     private static final int BT_HANDOVER_TYPE_LE_ROLE = 0x1C;
     private static final int BT_HANDOVER_TYPE_LONG_LOCAL_NAME = 0x09;
     private static final int BT_HANDOVER_TYPE_SHORT_LOCAL_NAME = 0x08;
+    private static final int BT_HANDOVER_TYPE_SECURITY_MANAGER_TK = 0x10;
+
     public static final int BT_HANDOVER_LE_ROLE_CENTRAL_ONLY = 0x01;
 
+    public static final int SECURITY_MANAGER_TK_SIZE = 16;
+
     private final BluetoothAdapter mBluetoothAdapter;
 
     private final Object mLock = new Object();
@@ -74,6 +79,7 @@
         public String name;
         public boolean carrierActivating = false;
         public int transport = BluetoothDevice.TRANSPORT_AUTO;
+        public OobData oobData;
     }
 
     public static class IncomingHandoverData {
@@ -408,7 +414,6 @@
         try {
 
             while (payload.remaining() > 0) {
-                byte[] nameBytes;
                 int len = payload.get();
                 int type = payload.get();
                 switch (type) {
@@ -427,10 +432,30 @@
                         }
                         break;
                     case BT_HANDOVER_TYPE_LONG_LOCAL_NAME:
-                        nameBytes = new byte[len - 1];
+                        byte[] nameBytes = new byte[len - 1];
                         payload.get(nameBytes);
                         result.name = new String(nameBytes, StandardCharsets.UTF_8);
                         break;
+                    case BT_HANDOVER_TYPE_SECURITY_MANAGER_TK:
+                        if (len-1 != SECURITY_MANAGER_TK_SIZE) {
+                            Log.i(TAG, "BT OOB: invalid size of SM TK, should be " +
+                                  SECURITY_MANAGER_TK_SIZE + " bytes.");
+                            break;
+                        }
+
+                        byte[] reversedTK = new byte[len - 1];
+                        payload.get(reversedTK);
+
+                        byte[] securityManagerTK = new byte[len - 1];
+
+                        //TK in AD is in reverse order
+                        for (int i = 0; i < reversedTK.length; i++) {
+                            securityManagerTK[i] = reversedTK[securityManagerTK.length - 1 - i];
+                        }
+
+                        result.oobData = new OobData();
+                        result.oobData.setSecurityManagerTk(securityManagerTK);
+                        break;
                     default:
                         payload.position(payload.position() + len - 1);
                         break;
diff --git a/src/com/android/nfc/handover/PeripheralHandoverService.java b/src/com/android/nfc/handover/PeripheralHandoverService.java
index 6c30244..95bf0d6 100644
--- a/src/com/android/nfc/handover/PeripheralHandoverService.java
+++ b/src/com/android/nfc/handover/PeripheralHandoverService.java
@@ -19,6 +19,7 @@
 import android.app.Service;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
+import android.bluetooth.OobData;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -46,6 +47,7 @@
     public static final String EXTRA_PERIPHERAL_DEVICE = "device";
     public static final String EXTRA_PERIPHERAL_NAME = "headsetname";
     public static final String EXTRA_PERIPHERAL_TRANSPORT = "transporttype";
+    public static final String EXTRA_PERIPHERAL_OOB_DATA = "oobdata";
 
     // Amount of time to pause polling when connecting to peripherals
     private static final int PAUSE_POLLING_TIMEOUT_MS = 35000;
@@ -157,9 +159,10 @@
         BluetoothDevice device = msgData.getParcelable(EXTRA_PERIPHERAL_DEVICE);
         String name = msgData.getString(EXTRA_PERIPHERAL_NAME);
         int transport = msgData.getInt(EXTRA_PERIPHERAL_TRANSPORT);
+        OobData oobData = msgData.getParcelable(EXTRA_PERIPHERAL_OOB_DATA);
 
         mBluetoothPeripheralHandover = new BluetoothPeripheralHandover(
-                this, device, name, transport, this);
+                this, device, name, transport, oobData, this);
 
         if (transport == BluetoothDevice.TRANSPORT_LE) {
             mHandler.sendMessageDelayed(