[automerger skipped] Import translations. DO NOT MERGE am: 3d0155c825 -s ours

am skip reason: subject contains skip directive

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Nfc/+/12089052

Change-Id: I84941d3b3983a54e57078c3b39da75bbe5be0f5e
diff --git a/Android.bp b/Android.bp
index b1f9e86..8c68554 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1,14 +1,23 @@
+genrule {
+    name: "statslog-Nfc-java-gen",
+    tools: ["stats-log-api-gen"],
+    cmd: "$(location stats-log-api-gen) --java $(out) --module nfc --javaPackage com.android.nfc"
+        + " --javaClass NfcStatsLog",
+    out: ["com/android/nfc/NfcStatsLog.java"],
+}
+
 // NCI Configuration
 android_app {
     name: "NfcNci",
     srcs: [
         "src/**/*.java",
         "nci/**/*.java",
+        ":statslog-Nfc-java-gen",
     ],
     platform_apis: true,
     certificate: "platform",
     jni_libs: ["libnfc_nci_jni"],
-    static_libs: ["androidx.legacy_legacy-support-v4"],
+    static_libs: ["androidx.appcompat_appcompat"],
     optimize: {
         enabled: false,
     },
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 17f68f2..f6a0716 100755
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -22,7 +22,6 @@
     <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
     <uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED" />
     <uses-permission android:name="android.permission.NFC" />
-    <uses-permission android:name="android.permission.NFC_UNLOCK" />
     <uses-permission android:name="android.permission.BIND_NFC_SERVICE" />
     <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
     <uses-permission android:name="android.permission.STATUS_BAR" />
@@ -44,10 +43,7 @@
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.MANAGE_USERS" />
     <uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW" />
-    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
-    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
     <uses-permission android:name="android.permission.SET_ACTIVITY_WATCHER" />
-    <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
     <uses-permission android:name="android.permission.NFC_HANDOVER_STATUS" />
     <uses-permission android:name="android.permission.LOCAL_MAC_ADDRESS" />
     <uses-permission android:name="com.android.permission.WHITELIST_BLUETOOTH_DEVICE" />
@@ -60,6 +56,8 @@
     <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
     <uses-permission android:name="android.permission.START_ACTIVITIES_FROM_BACKGROUND" />
     <uses-permission android:name="android.permission.NETWORK_SETTINGS" />
+    <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
+    <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
 
     <application android:name=".NfcApplication"
                  android:icon="@drawable/icon"
@@ -117,15 +115,6 @@
             android:noHistory="true"
             android:configChanges="orientation|keyboardHidden|screenSize"
         />
-
-        <activity android:name=".NfcBlockedNotification"
-            android:finishOnCloseSystemDialogs="true"
-            android:excludeFromRecents="true"
-            android:theme="@android:style/Theme.Translucent.NoTitleBar"
-            android:noHistory="true"
-            android:configChanges="orientation|keyboardHidden|screenSize"
-        />
-
         <activity android:name=".BeamShareActivity"
             android:finishOnCloseSystemDialogs="true"
             android:theme="@android:style/Theme.Translucent"
diff --git a/OWNERS b/OWNERS
index 0aa310b..45e7662 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,4 +1,4 @@
 zachoverflow@google.com
-rmojumder@google.com
 jackcwyu@google.com
 georgekgchang@google.com
+alisher@google.com
diff --git a/nci/jni/Android.bp b/nci/jni/Android.bp
index cfbc8a6..65d065d 100644
--- a/nci/jni/Android.bp
+++ b/nci/jni/Android.bp
@@ -45,4 +45,8 @@
             cflags: ["-DDCHECK_ALWAYS_ON"],
         },
     },
+    sanitize: {
+        integer_overflow: true,
+        misc_undefined: ["bounds"],
+    },
 }
diff --git a/nci/jni/IntervalTimer.cpp b/nci/jni/IntervalTimer.cpp
index 47bbc52..c361417 100644
--- a/nci/jni/IntervalTimer.cpp
+++ b/nci/jni/IntervalTimer.cpp
@@ -76,6 +76,7 @@
   se.sigev_value.sival_ptr = &mTimerId;
   se.sigev_notify_function = cb;
   se.sigev_notify_attributes = NULL;
+  se.sigev_signo = 0;
   mCb = cb;
   stat = timer_create(CLOCK_MONOTONIC, &se, &mTimerId);
   if (stat == -1) LOG(ERROR) << StringPrintf("fail create timer");
diff --git a/nci/jni/NativeNfcManager.cpp b/nci/jni/NativeNfcManager.cpp
index 65f006f..5048262 100644
--- a/nci/jni/NativeNfcManager.cpp
+++ b/nci/jni/NativeNfcManager.cpp
@@ -64,6 +64,7 @@
 extern void nativeNfcTag_resetPresenceCheck();
 extern void nativeNfcTag_doReadCompleted(tNFA_STATUS status);
 extern void nativeNfcTag_setRfInterface(tNFA_INTF_TYPE rfInterface);
+extern void nativeNfcTag_setActivatedRfProtocol(tNFA_INTF_TYPE rfProtocol);
 extern void nativeNfcTag_abortWaits();
 extern void nativeLlcpConnectionlessSocket_abortWait();
 extern void nativeNfcTag_registerNdefTypeHandler();
@@ -190,7 +191,8 @@
 }
 void initializeMfcReaderOption() {
   legacy_mfc_reader =
-      (NfcConfig::getUnsigned(NAME_LEGACY_MIFARE_READER, 1) != 0) ? true : false;
+      (NfcConfig::getUnsigned(NAME_LEGACY_MIFARE_READER, 0) != 0) ? true
+                                                                  : false;
 
   DLOG_IF(INFO, nfc_debug_enabled)
       << __func__ <<": mifare reader option=" << legacy_mfc_reader;
@@ -226,18 +228,31 @@
 **
 *******************************************************************************/
 static void handleRfDiscoveryEvent(tNFC_RESULT_DEVT* discoveredDevice) {
+  NfcTag& natTag = NfcTag::getInstance();
+  natTag.setNumDiscNtf(natTag.getNumDiscNtf() + 1);
   if (discoveredDevice->more == NCI_DISCOVER_NTF_MORE) {
     // there is more discovery notification coming
     return;
   }
 
-  bool isP2p = NfcTag::getInstance().isP2pDiscovered();
-  if (!sReaderModeEnabled && isP2p) {
+  bool isP2p = natTag.isP2pDiscovered();
+
+  if (natTag.getNumDiscNtf() > 1) {
+    natTag.setMultiProtocolTagSupport(true);
+    if (isP2p) {
+      // Remove NFC_DEP NTF count
+      // Skip NFC_DEP protocol in MultiProtocolTag select.
+      natTag.setNumDiscNtf(natTag.getNumDiscNtf() - 1);
+    }
+  }
+
+  if (sP2pEnabled && !sReaderModeEnabled && isP2p) {
     // select the peer that supports P2P
-    NfcTag::getInstance().selectP2p();
+    natTag.selectP2p();
   } else {
+    natTag.setNumDiscNtf(natTag.getNumDiscNtf() - 1);
     // select the first of multiple tags that is discovered
-    NfcTag::getInstance().selectFirstTag();
+    natTag.selectFirstTag();
   }
 }
 
@@ -294,6 +309,8 @@
           << StringPrintf("%s: NFA_RF_DISCOVERY_STOPPED_EVT: status = %u",
                           __func__, eventData->status);
 
+      gActivated = false;
+
       SyncEventGuard guard(sNfaEnableDisablePollingEvent);
       sNfaEnableDisablePollingEvent.notifyOne();
     } break;
@@ -303,6 +320,7 @@
       DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
           "%s: NFA_DISC_RESULT_EVT: status = %d", __func__, status);
       if (status != NFA_STATUS_OK) {
+        NfcTag::getInstance().setNumDiscNtf(0);
         LOG(ERROR) << StringPrintf("%s: NFA_DISC_RESULT_EVT error: status = %d",
                                    __func__, status);
       } else {
@@ -339,14 +357,23 @@
       break;
 
     case NFA_ACTIVATED_EVT:  // NFC link/protocol activated
+    {
       DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
           "%s: NFA_ACTIVATED_EVT: gIsSelectingRfInterface=%d, sIsDisabling=%d",
           __func__, gIsSelectingRfInterface, sIsDisabling);
+      uint8_t activatedProtocol =
+          (tNFA_INTF_TYPE)eventData->activated.activate_ntf.protocol;
+      if (NFC_PROTOCOL_T5T == activatedProtocol &&
+          NfcTag::getInstance().getNumDiscNtf()) {
+        /* T5T doesn't support multiproto detection logic */
+        NfcTag::getInstance().setNumDiscNtf(0);
+      }
       if ((eventData->activated.activate_ntf.protocol !=
            NFA_PROTOCOL_NFC_DEP) &&
           (!isListenMode(eventData->activated))) {
         nativeNfcTag_setRfInterface(
             (tNFA_INTF_TYPE)eventData->activated.activate_ntf.intf_param.type);
+        nativeNfcTag_setActivatedRfProtocol(activatedProtocol);
       }
       if (EXTNS_GetConnectFlag() == TRUE) {
         NfcTag::getInstance().setActivationState();
@@ -397,6 +424,12 @@
         }
       } else {
         NfcTag::getInstance().connectionEventHandler(connEvent, eventData);
+        if (NfcTag::getInstance().getNumDiscNtf()) {
+          /*If its multiprotocol tag, deactivate tag with current selected
+          protocol to sleep . Select tag with next supported protocol after
+          deactivation event is received*/
+          NFA_Deactivate(true);
+        }
 
         // We know it is not activating for P2P.  If it activated in
         // listen mode then it is likely for an SE transaction.
@@ -405,13 +438,13 @@
           sSeRfActive = true;
         }
       }
-      break;
-
+    } break;
     case NFA_DEACTIVATED_EVT:  // NFC link/protocol deactivated
       DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
           "%s: NFA_DEACTIVATED_EVT   Type: %u, gIsTagDeactivating: %d",
           __func__, eventData->deactivated.type, gIsTagDeactivating);
       NfcTag::getInstance().setDeactivationState(eventData->deactivated);
+      NfcTag::getInstance().selectNextTagIfExists();
       if (eventData->deactivated.type != NFA_DEACTIVATE_TYPE_SLEEP) {
         {
           SyncEventGuard g(gDeactivatedEvent);
@@ -1164,19 +1197,20 @@
 }
 
 static void nfcManager_configNfccConfigControl(bool flag) {
-  // configure NFCC_CONFIG_CONTROL- NFCC allowed to manage RF configuration.
-  if (NFC_GetNCIVersion() != NCI_VERSION_1_0) {
-    uint8_t nfa_set_config[] = {0x00};
+    // configure NFCC_CONFIG_CONTROL- NFCC allowed to manage RF configuration.
+    if (NFC_GetNCIVersion() != NCI_VERSION_1_0) {
+        uint8_t nfa_set_config[] = { 0x00 };
 
-    nfa_set_config[0] = (flag == true ? 1 : 0);
+        nfa_set_config[0] = (flag == true ? 1 : 0);
 
-    tNFA_STATUS status =
-        NFA_SetConfig(NCI_PARAM_ID_NFCC_CONFIG_CONTROL, sizeof(nfa_set_config),
-                      &nfa_set_config[0]);
-    if (status != NFA_STATUS_OK) {
-      LOG(ERROR) << __func__ << ": Failed to configure NFCC_CONFIG_CONTROL";
+        tNFA_STATUS status = NFA_SetConfig(NCI_PARAM_ID_NFCC_CONFIG_CONTROL,
+                                           sizeof(nfa_set_config),
+                                           &nfa_set_config[0]);
+        if (status != NFA_STATUS_OK) {
+            LOG(ERROR) << __func__
+            << ": Failed to configure NFCC_CONFIG_CONTROL";
+        }
     }
-  }
 }
 
 /*******************************************************************************
@@ -1246,8 +1280,7 @@
         sReaderModeEnabled = true;
         NFA_DisableListening();
 
-        // configure NFCC_CONFIG_CONTROL- NFCC not allowed to manage RF
-        // configuration.
+        // configure NFCC_CONFIG_CONTROL- NFCC not allowed to manage RF configuration.
         nfcManager_configNfccConfigControl(false);
 
         NFA_SetRfDiscoveryDuration(READER_MODE_DISCOVERY_DURATION);
@@ -1256,8 +1289,7 @@
         sReaderModeEnabled = false;
         NFA_EnableListening();
 
-        // configure NFCC_CONFIG_CONTROL- NFCC allowed to manage RF
-        // configuration.
+        // configure NFCC_CONFIG_CONTROL- NFCC allowed to manage RF configuration.
         nfcManager_configNfccConfigControl(true);
 
         NFA_SetRfDiscoveryDuration(nat->discovery_duration);
@@ -1959,6 +1991,11 @@
   }
   return true;
 }
+
+static jstring nfcManager_doGetNfaStorageDir(JNIEnv* e, jobject o) {
+  string nfaStorageDir = NfcConfig::getString(NAME_NFA_STORAGE, "/data/nfc");
+  return e->NewStringUTF(nfaStorageDir.c_str());
+}
 /*****************************************************************************
 **
 ** JNI functions for android-4.0.1_r1
@@ -2046,6 +2083,9 @@
     {"getAidTableSize", "()I", (void*)nfcManager_getAidTableSize},
 
     {"doSetNfcSecure", "(Z)Z", (void*)nfcManager_doSetNfcSecure},
+
+    {"getNfaStorageDir", "()Ljava/lang/String;",
+     (void*)nfcManager_doGetNfaStorageDir},
 };
 
 /*******************************************************************************
diff --git a/nci/jni/NativeNfcTag.cpp b/nci/jni/NativeNfcTag.cpp
index cd9f232..ddae94e 100644
--- a/nci/jni/NativeNfcTag.cpp
+++ b/nci/jni/NativeNfcTag.cpp
@@ -87,6 +87,7 @@
 static bool sCheckNdefCapable = false;  // whether tag has NDEF capability
 static tNFA_HANDLE sNdefTypeHandlerHandle = NFA_HANDLE_INVALID;
 static tNFA_INTF_TYPE sCurrentRfInterface = NFA_INTERFACE_ISO_DEP;
+static tNFA_INTF_TYPE sCurrentActivatedProtocl = NFA_INTERFACE_ISO_DEP;
 static std::basic_string<uint8_t> sRxDataBuffer;
 static tNFA_STATUS sRxDataStatus = NFA_STATUS_OK;
 static bool sWaitingForTransceive = false;
@@ -121,6 +122,7 @@
 static jboolean sMakeReadonlyWaitingForComplete = JNI_FALSE;
 static int sCurrentConnectedTargetType = TARGET_TYPE_UNKNOWN;
 static int sCurrentConnectedTargetProtocol = NFC_PROTOCOL_UNKNOWN;
+static int sCurrentConnectedHandle = 0;
 static int reSelect(tNFA_INTF_TYPE rfInterface, bool fSwitchIfNeeded);
 static bool switchRfInterface(tNFA_INTF_TYPE rfInterface);
 
@@ -157,6 +159,7 @@
   }
   sem_post(&sMakeReadonlySem);
   sCurrentRfInterface = NFA_INTERFACE_ISO_DEP;
+  sCurrentActivatedProtocl = NFA_INTERFACE_ISO_DEP;
   sCurrentConnectedTargetType = TARGET_TYPE_UNKNOWN;
   sCurrentConnectedTargetProtocol = NFC_PROTOCOL_UNKNOWN;
 }
@@ -203,6 +206,19 @@
 }
 
 /*******************************************************************************
+ **
+ ** Function:        nativeNfcTag_setActivatedRfProtocol
+ **
+ ** Description:     Set rf Activated Protocol.
+ **
+ ** Returns:         void
+ **
+ *******************************************************************************/
+void nativeNfcTag_setActivatedRfProtocol(tNFA_INTF_TYPE rfProtocol) {
+  sCurrentActivatedProtocl = rfProtocol;
+}
+
+/*******************************************************************************
 **
 ** Function:        ndefHandlerCallback
 **
@@ -407,6 +423,12 @@
         }
       } else {
         status = NFA_RwFormatTag();
+        if (status != NFA_STATUS_OK) {
+          LOG(ERROR) << StringPrintf("%s: can't format mifare classic tag",
+                                     __func__);
+          sem_destroy(&sFormatSem);
+          goto TheEnd;
+        }
       }
       sem_wait(&sFormatSem);
       sem_destroy(&sFormatSem);
@@ -546,11 +568,13 @@
 
   sCurrentConnectedTargetType = natTag.mTechList[i];
   sCurrentConnectedTargetProtocol = natTag.mTechLibNfcTypes[i];
+  sCurrentConnectedHandle = targetHandle;
 
-  if (sCurrentConnectedTargetProtocol != NFC_PROTOCOL_ISO_DEP) {
-    DLOG_IF(INFO, nfc_debug_enabled)
-        << StringPrintf("%s() Nfc type = %d, do nothing for non ISO_DEP",
-                        __func__, sCurrentConnectedTargetProtocol);
+  if (sCurrentConnectedTargetProtocol != NFC_PROTOCOL_ISO_DEP &&
+      sCurrentConnectedTargetProtocol != NFC_PROTOCOL_MIFARE) {
+    DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
+        "%s() Nfc type = %d, do nothing for non ISO_DEP and non Mifare ",
+        __func__, sCurrentConnectedTargetProtocol);
     retCode = NFCSTATUS_SUCCESS;
     goto TheEnd;
   }
@@ -562,6 +586,9 @@
         sCurrentConnectedTargetType);
     retCode = switchRfInterface(NFA_INTERFACE_FRAME) ? NFA_STATUS_OK
                                                      : NFA_STATUS_FAILED;
+  } else if (sCurrentConnectedTargetType == TARGET_TYPE_MIFARE_CLASSIC) {
+    retCode = switchRfInterface(NFA_INTERFACE_MIFARE) ? NFA_STATUS_OK
+                                                      : NFA_STATUS_FAILED;
   } else {
     retCode = switchRfInterface(NFA_INTERFACE_ISO_DEP) ? NFA_STATUS_OK
                                                        : NFA_STATUS_FAILED;
@@ -614,11 +641,11 @@
         (NFC_GetNCIVersion() >= NCI_VERSION_2_0)) {
       {
         SyncEventGuard g3(sReconnectEvent);
-        if (sCurrentConnectedTargetProtocol == NFA_PROTOCOL_T2T) {
+        if (sCurrentActivatedProtocl == NFA_PROTOCOL_T2T) {
           status = NFA_SendRawFrame(RW_TAG_SLP_REQ, sizeof(RW_TAG_SLP_REQ), 0);
-        } else if (sCurrentConnectedTargetProtocol == NFA_PROTOCOL_ISO_DEP) {
-          status =
-              NFA_SendRawFrame(RW_DESELECT_REQ, sizeof(RW_DESELECT_REQ), 0);
+        } else if (sCurrentActivatedProtocl == NFA_PROTOCOL_ISO_DEP) {
+          status = NFA_SendRawFrame(RW_DESELECT_REQ,
+                                    sizeof(RW_DESELECT_REQ), 0);
         }
         sReconnectEvent.wait(4);
         if (status != NFA_STATUS_OK) {
@@ -670,21 +697,39 @@
           << StringPrintf("%s: select interface %u", __func__, rfInterface);
       gIsSelectingRfInterface = true;
       if (NFA_STATUS_OK !=
-          (status = NFA_Select(natTag.mTechHandles[0],
-                               natTag.mTechLibNfcTypes[0], rfInterface))) {
+          (status = NFA_Select(natTag.mTechHandles[sCurrentConnectedHandle],
+                               natTag.mTechLibNfcTypes[sCurrentConnectedHandle],
+                               rfInterface))) {
         LOG(ERROR) << StringPrintf("%s: NFA_Select failed, status = %d",
                                    __func__, status);
         break;
       }
 
       sConnectOk = false;
-      if (sReconnectEvent.wait(1000) == false)  // if timeout occured
+      if (sReconnectEvent.wait(1000) == false)  // if timeout occurred
       {
         LOG(ERROR) << StringPrintf("%s: timeout waiting for select", __func__);
         break;
       }
     }
 
+    /*Retry logic in case of core Generic error while selecting a tag*/
+    if (sConnectOk == false) {
+      LOG(ERROR) << StringPrintf("%s: waiting for Card to be activated",
+                                 __func__);
+      int retry = 0;
+      sConnectWaitingForComplete = JNI_TRUE;
+      do {
+        SyncEventGuard reselectEvent(sReconnectEvent);
+        if (sReconnectEvent.wait(500) == false) {  // if timeout occurred
+          LOG(ERROR) << StringPrintf("%s: timeout ", __func__);
+        }
+        retry++;
+        LOG(ERROR) << StringPrintf("%s: waiting for Card to be activated %x %x",
+                                   __func__, retry, sConnectOk);
+      } while (sConnectOk == false && retry < 3);
+    }
+
     DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
         "%s: select completed; sConnectOk=%d", __func__, sConnectOk);
     if (NfcTag::getInstance().getActivationState() != NfcTag::Active) {
@@ -723,10 +768,11 @@
 static bool switchRfInterface(tNFA_INTF_TYPE rfInterface) {
   NfcTag& natTag = NfcTag::getInstance();
 
-  if (sCurrentConnectedTargetProtocol != NFC_PROTOCOL_ISO_DEP) {
-    DLOG_IF(INFO, nfc_debug_enabled)
-        << StringPrintf("%s: protocol: %d not ISO_DEP, do nothing", __func__,
-                        natTag.mTechLibNfcTypes[0]);
+  if (sCurrentConnectedTargetProtocol != NFC_PROTOCOL_ISO_DEP &&
+      sCurrentConnectedTargetProtocol != NFC_PROTOCOL_MIFARE) {
+    DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
+        "%s: protocol: %d not ISO_DEP and not Mifare, do nothing", __func__,
+        natTag.mTechLibNfcTypes[0]);
     return true;
   }
 
diff --git a/nci/jni/NfcTag.cpp b/nci/jni/NfcTag.cpp
old mode 100644
new mode 100755
index 7194d8c..b9d8d55
--- a/nci/jni/NfcTag.cpp
+++ b/nci/jni/NfcTag.cpp
@@ -34,6 +34,9 @@
 using android::base::StringPrintf;
 
 extern bool nfc_debug_enabled;
+static void deleteglobaldata(JNIEnv* e);
+static jobjectArray sTechPollBytes;
+static int sLastSelectedTagId = 0;
 
 /*******************************************************************************
 **
@@ -57,12 +60,17 @@
       mNdefDetectionTimedOut(false),
       mIsDynamicTagId(false),
       mPresenceCheckAlgorithm(NFA_RW_PRES_CHK_DEFAULT),
-      mIsFelicaLite(false) {
+      mIsFelicaLite(false),
+      mNumDiscNtf(0),
+      mNumDiscTechList(0),
+      mTechListTail(0),
+      mIsMultiProtocolTag(false) {
   memset(mTechList, 0, sizeof(mTechList));
   memset(mTechHandles, 0, sizeof(mTechHandles));
   memset(mTechLibNfcTypes, 0, sizeof(mTechLibNfcTypes));
   memset(mTechParams, 0, sizeof(mTechParams));
   memset(mLastKovioUid, 0, NFC_KOVIO_MAX_LEN);
+  memset(&mLastKovioTime, 0, sizeof(timespec));
 }
 
 /*******************************************************************************
@@ -94,7 +102,6 @@
   mIsActivated = false;
   mActivationState = Idle;
   mProtocol = NFC_PROTOCOL_UNKNOWN;
-  mNumTechList = 0;
   mtT1tMaxMessageSize = 0;
   mReadCompletedStatus = NFA_STATUS_OK;
   resetTechnologies();
@@ -280,7 +287,9 @@
   DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("%s: enter", fn);
   tNFC_ACTIVATE_DEVT& rfDetail = activationData.activate_ntf;
 
-  mNumTechList = 0;
+  if (mTechListTail < (MAX_NUM_TECHNOLOGY - 1)) {
+    mNumTechList = mTechListTail;
+  }
   mTechHandles[mNumTechList] = rfDetail.rf_disc_id;
   mTechLibNfcTypes[mNumTechList] = rfDetail.protocol;
 
@@ -335,6 +344,17 @@
     // type-4 tag uses technology ISO-DEP and technology A or B
     mTechList[mNumTechList] =
         TARGET_TYPE_ISO14443_4;  // is TagTechnology.ISO_DEP by Java API
+    if ((NFC_DISCOVERY_TYPE_POLL_A == rfDetail.rf_tech_param.mode) ||
+        (NFC_DISCOVERY_TYPE_POLL_A_ACTIVE == rfDetail.rf_tech_param.mode)) {
+      uint8_t fwi = rfDetail.intf_param.intf_param.pa_iso.fwi;
+      if (fwi >= MIN_FWI && fwi <= MAX_FWI) {
+        //2^MIN_FWI * 256 * 16 * 1000 / 13560000 is approximately 618
+        int fwt = (1 << (fwi - MIN_FWI)) * 618;
+        DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
+            "Setting the transceive timeout = %d, fwi = %0#x", fwt, fwi);
+        setTransceiveTimeout(mTechList[mNumTechList], fwt);
+      }
+    }
     if ((rfDetail.rf_tech_param.mode == NFC_DISCOVERY_TYPE_POLL_A) ||
         (rfDetail.rf_tech_param.mode == NFC_DISCOVERY_TYPE_POLL_A_ACTIVE) ||
         (rfDetail.rf_tech_param.mode == NFC_DISCOVERY_TYPE_LISTEN_A) ||
@@ -411,111 +431,29 @@
 void NfcTag::discoverTechnologies(tNFA_DISC_RESULT& discoveryData) {
   static const char fn[] = "NfcTag::discoverTechnologies (discovery)";
   tNFC_RESULT_DEVT& discovery_ntf = discoveryData.discovery_ntf;
+  uint8_t index = mNumDiscNtf;
 
   DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
       "%s: enter: rf disc. id=%u; protocol=%u, mNumTechList=%u", fn,
       discovery_ntf.rf_disc_id, discovery_ntf.protocol, mNumTechList);
-  if (mNumTechList >= MAX_NUM_TECHNOLOGY) {
+  if (index >= MAX_NUM_TECHNOLOGY) {
     LOG(ERROR) << StringPrintf("%s: exceed max=%d", fn, MAX_NUM_TECHNOLOGY);
     goto TheEnd;
   }
-  mTechHandles[mNumTechList] = discovery_ntf.rf_disc_id;
-  mTechLibNfcTypes[mNumTechList] = discovery_ntf.protocol;
-
-  // save the stack's data structure for interpretation later
-  memcpy(&(mTechParams[mNumTechList]), &(discovery_ntf.rf_tech_param),
-         sizeof(discovery_ntf.rf_tech_param));
-
-  if (NFC_PROTOCOL_T1T == discovery_ntf.protocol) {
-    mTechList[mNumTechList] =
-        TARGET_TYPE_ISO14443_3A;  // is TagTechnology.NFC_A by Java API
-  } else if (NFC_PROTOCOL_T2T == discovery_ntf.protocol) {
-    mTechList[mNumTechList] =
-        TARGET_TYPE_ISO14443_3A;  // is TagTechnology.NFC_A by Java API
-    // type-2 tags are identical to Mifare Ultralight, so Ultralight is also
-    // discovered
-    if ((discovery_ntf.rf_tech_param.param.pa.sel_rsp == 0) &&
-        (mNumTechList < (MAX_NUM_TECHNOLOGY - 1))) {
-      // Mifare Ultralight
-      mNumTechList++;
-      mTechHandles[mNumTechList] = discovery_ntf.rf_disc_id;
-      mTechLibNfcTypes[mNumTechList] = discovery_ntf.protocol;
-      mTechList[mNumTechList] =
-          TARGET_TYPE_MIFARE_UL;  // is TagTechnology.MIFARE_ULTRALIGHT by Java
-                                  // API
-    }
-
-    // save the stack's data structure for interpretation later
-    memcpy(&(mTechParams[mNumTechList]), &(discovery_ntf.rf_tech_param),
-           sizeof(discovery_ntf.rf_tech_param));
-  } else if (NFC_PROTOCOL_T3T == discovery_ntf.protocol) {
-    mTechList[mNumTechList] = TARGET_TYPE_FELICA;
-  } else if (NFC_PROTOCOL_ISO_DEP == discovery_ntf.protocol) {
-    // type-4 tag uses technology ISO-DEP and technology A or B
-    mTechList[mNumTechList] =
-        TARGET_TYPE_ISO14443_4;  // is TagTechnology.ISO_DEP by Java API
-    if ((discovery_ntf.rf_tech_param.mode == NFC_DISCOVERY_TYPE_POLL_A) ||
-        (discovery_ntf.rf_tech_param.mode ==
-         NFC_DISCOVERY_TYPE_POLL_A_ACTIVE) ||
-        (discovery_ntf.rf_tech_param.mode == NFC_DISCOVERY_TYPE_LISTEN_A) ||
-        (discovery_ntf.rf_tech_param.mode ==
-         NFC_DISCOVERY_TYPE_LISTEN_A_ACTIVE)) {
-      if (mNumTechList < (MAX_NUM_TECHNOLOGY - 1)) {
-        mNumTechList++;
-        mTechHandles[mNumTechList] = discovery_ntf.rf_disc_id;
-        mTechLibNfcTypes[mNumTechList] = discovery_ntf.protocol;
-        mTechList[mNumTechList] =
-            TARGET_TYPE_ISO14443_3A;  // is TagTechnology.NFC_A by Java API
-        // save the stack's data structure for interpretation later
-        memcpy(&(mTechParams[mNumTechList]), &(discovery_ntf.rf_tech_param),
-               sizeof(discovery_ntf.rf_tech_param));
-      }
-    } else if ((discovery_ntf.rf_tech_param.mode ==
-                NFC_DISCOVERY_TYPE_POLL_B) ||
-               (discovery_ntf.rf_tech_param.mode ==
-                NFC_DISCOVERY_TYPE_POLL_B_PRIME) ||
-               (discovery_ntf.rf_tech_param.mode ==
-                NFC_DISCOVERY_TYPE_LISTEN_B) ||
-               (discovery_ntf.rf_tech_param.mode ==
-                NFC_DISCOVERY_TYPE_LISTEN_B_PRIME)) {
-      if (mNumTechList < (MAX_NUM_TECHNOLOGY - 1)) {
-        mNumTechList++;
-        mTechHandles[mNumTechList] = discovery_ntf.rf_disc_id;
-        mTechLibNfcTypes[mNumTechList] = discovery_ntf.protocol;
-        mTechList[mNumTechList] =
-            TARGET_TYPE_ISO14443_3B;  // is TagTechnology.NFC_B by Java API
-        // save the stack's data structure for interpretation later
-        memcpy(&(mTechParams[mNumTechList]), &(discovery_ntf.rf_tech_param),
-               sizeof(discovery_ntf.rf_tech_param));
-      }
-    }
-  } else if (NFC_PROTOCOL_T5T == discovery_ntf.protocol) {
-    // is TagTechnology.NFC_V by Java API
-    mTechList[mNumTechList] = TARGET_TYPE_V;
-  } else if (NFC_PROTOCOL_MIFARE == discovery_ntf.protocol) {
-    mTechList[mNumTechList] = TARGET_TYPE_MIFARE_CLASSIC;
-    if (mNumTechList < (MAX_NUM_TECHNOLOGY - 1)) {
-      mNumTechList++;
-      mTechHandles[mNumTechList] = discovery_ntf.rf_disc_id;
-      mTechLibNfcTypes[mNumTechList] = discovery_ntf.protocol;
-      mTechList[mNumTechList] = TARGET_TYPE_ISO14443_3A;
-      // save the stack's data structure for interpretation later
-      memcpy(&(mTechParams[mNumTechList]), &(discovery_ntf.rf_tech_param),
-             sizeof(discovery_ntf.rf_tech_param));
-    }
-  } else {
-    LOG(ERROR) << StringPrintf("%s: unknown protocol ????", fn);
-    mTechList[mNumTechList] = TARGET_TYPE_UNKNOWN;
+  mTechHandlesDiscData[index] = discovery_ntf.rf_disc_id;
+  mTechLibNfcTypesDiscData[index] = discovery_ntf.protocol;
+  if (mNumDiscTechList < MAX_NUM_TECHNOLOGY) {
+    mNumDiscTechList++;
   }
-
-  mNumTechList++;
   if (discovery_ntf.more != NCI_DISCOVER_NTF_MORE) {
-    for (int i = 0; i < mNumTechList; i++) {
-      DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
-          "%s: index=%d; tech=%d; handle=%d; nfc type=%d", fn, i, mTechList[i],
-          mTechHandles[i], mTechLibNfcTypes[i]);
+    for (int i = 0; i < mNumDiscTechList; i++) {
+      DLOG_IF(INFO, nfc_debug_enabled)
+          << StringPrintf("%s: index=%d; handle=%d; nfc type=%d", fn, i,
+                          mTechHandlesDiscData[i], mTechLibNfcTypesDiscData[i]);
     }
   }
+  DLOG_IF(INFO, nfc_debug_enabled)
+      << StringPrintf("%s; mNumDiscTechList=%x", fn, mNumDiscTechList);
 
 TheEnd:
   DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("%s: exit", fn);
@@ -576,15 +514,24 @@
   }
   mNativeData->tag = e->NewGlobalRef(tag.get());
 
-  // notify NFC service about this new tag
   DLOG_IF(INFO, nfc_debug_enabled)
-      << StringPrintf("%s: try notify nfc service", fn);
-  e->CallVoidMethod(mNativeData->manager,
-                    android::gCachedNfcManagerNotifyNdefMessageListeners,
-                    tag.get());
-  if (e->ExceptionCheck()) {
-    e->ExceptionClear();
-    LOG(ERROR) << StringPrintf("%s: fail notify nfc service", fn);
+      << StringPrintf("%s; mNumDiscNtf=%x", fn, mNumDiscNtf);
+
+  if (!mNumDiscNtf) {
+    // notify NFC service about this new tag
+    DLOG_IF(INFO, nfc_debug_enabled)
+        << StringPrintf("%s: try notify nfc service", fn);
+    e->CallVoidMethod(mNativeData->manager,
+                      android::gCachedNfcManagerNotifyNdefMessageListeners,
+                      tag.get());
+    if (e->ExceptionCheck()) {
+      e->ExceptionClear();
+      LOG(ERROR) << StringPrintf("%s: fail notify nfc service", fn);
+    }
+    deleteglobaldata(e);
+  } else {
+    DLOG_IF(INFO, nfc_debug_enabled)
+        << StringPrintf("%s: Selecting next tag", fn);
   }
 
   DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("%s: exit", fn);
@@ -592,6 +539,25 @@
 
 /*******************************************************************************
 **
+** Function:        deleteglobaldata
+**
+** Description:     Deletes the global data reference after notifying to service
+**                  e: JVM environment.
+**
+** Returns:         None
+**
+*******************************************************************************/
+static void deleteglobaldata(JNIEnv* e) {
+  static const char fn[] = "deleteglobaldata";
+  DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("%s: enter", fn);
+  if (sTechPollBytes != NULL) {
+    e->DeleteGlobalRef(sTechPollBytes);
+  }
+  DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("%s: exit", fn);
+}
+
+/*******************************************************************************
+**
 ** Function:        fillNativeNfcTagMembers1
 **
 ** Description:     Fill NativeNfcTag's members: mProtocols, mTechList,
@@ -686,8 +652,20 @@
   ScopedLocalRef<jobjectArray> techPollBytes(
       e, e->NewObjectArray(mNumTechList, byteArrayClass.get(), 0));
   int len = 0;
+  if (mTechListTail == 0) {
+    sTechPollBytes =
+        reinterpret_cast<jobjectArray>(e->NewGlobalRef(techPollBytes.get()));
+  } else {
+    /* Add previously activated tag's tech poll bytes also in the
+    list for multiprotocol tag*/
+    jobject techPollBytesObject;
+    for (int j = 0; j < mTechListTail; j++) {
+      techPollBytesObject = e->GetObjectArrayElement(sTechPollBytes, j);
+      e->SetObjectArrayElement(techPollBytes.get(), j, techPollBytesObject);
+    }
+  }
 
-  for (int i = 0; i < mNumTechList; i++) {
+  for (int i = mTechListTail; i < mNumTechList; i++) {
     DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
         "%s: index=%d; rf tech params mode=%u", fn, i, mTechParams[i].mode);
     if (NFC_DISCOVERY_TYPE_POLL_A == mTechParams[i].mode ||
@@ -780,6 +758,13 @@
     }  // switch: every type of technology
     e->SetObjectArrayElement(techPollBytes.get(), i, pollBytes.get());
   }  // for: every technology in the array
+  if (sTechPollBytes != NULL && mTechListTail != 0) {
+    /* Save tech poll bytes of all activated tags of a multiprotocol tag in
+     * sTechPollBytes*/
+    e->DeleteGlobalRef(sTechPollBytes);
+    sTechPollBytes =
+        reinterpret_cast<jobjectArray>(e->NewGlobalRef(techPollBytes.get()));
+  }
   jfieldID f = e->GetFieldID(tag_cls, "mTechPollBytes", "[[B");
   e->SetObjectField(tag, f, techPollBytes.get());
 }
@@ -808,7 +793,24 @@
   ScopedLocalRef<jobjectArray> techActBytes(
       e, e->NewObjectArray(mNumTechList, byteArrayClass.get(), 0));
 
-  for (int i = 0; i < mNumTechList; i++) {
+  // merging sak for combi tag
+  if (activationData.activate_ntf.protocol &
+      (NFC_PROTOCOL_T1T | NFC_PROTOCOL_T2T | NFC_PROTOCOL_MIFARE |
+       NFC_PROTOCOL_ISO_DEP)) {
+    uint8_t merge_sak = 0;
+    for (int i = 0; i < mNumTechList; i++) {
+      merge_sak = (merge_sak | mTechParams[i].param.pa.sel_rsp);
+    }
+    for (int i = 0; i < mNumTechList; i++) {
+      mTechParams[i].param.pa.sel_rsp = merge_sak;
+      actBytes.reset(e->NewByteArray(1));
+      e->SetByteArrayRegion(actBytes.get(), 0, 1,
+                            (jbyte*)&mTechParams[i].param.pa.sel_rsp);
+      e->SetObjectArrayElement(techActBytes.get(), i, actBytes.get());
+    }
+  }
+
+  for (int i = mTechListTail; i < mNumTechList; i++) {
     DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("%s: index=%d", fn, i);
     if (NFC_PROTOCOL_T1T == mTechLibNfcTypes[i] ||
         NFC_PROTOCOL_T2T == mTechLibNfcTypes[i]) {
@@ -914,7 +916,7 @@
       actBytes.reset(e->NewByteArray(0));
     }
     e->SetObjectArrayElement(techActBytes.get(), i, actBytes.get());
-  }  // for: every technology in the array
+  }  // for: every technology in the array of current selected tag
   jfieldID f = e->GetFieldID(tag_cls, "mTechActBytes", "[[B");
   e->SetObjectField(tag, f, techActBytes.get());
 }
@@ -992,6 +994,10 @@
   }
   jfieldID f = e->GetFieldID(tag_cls, "mUid", "[B");
   e->SetObjectField(tag, f, uid.get());
+  mTechListTail = mNumTechList;
+  if (mNumDiscNtf == 0) mTechListTail = 0;
+  DLOG_IF(INFO, nfc_debug_enabled)
+      << StringPrintf("%s;mTechListTail=%x", fn, mTechListTail);
 }
 
 /*******************************************************************************
@@ -1007,8 +1013,8 @@
   static const char fn[] = "NfcTag::isP2pDiscovered";
   bool retval = false;
 
-  for (int i = 0; i < mNumTechList; i++) {
-    if (mTechLibNfcTypes[i] == NFA_PROTOCOL_NFC_DEP) {
+  for (int i = 0; i < mNumDiscTechList; i++) {
+    if (mTechLibNfcTypesDiscData[i] == NFA_PROTOCOL_NFC_DEP) {
       // if remote device supports P2P
       DLOG_IF(INFO, nfc_debug_enabled)
           << StringPrintf("%s: discovered P2P", fn);
@@ -1076,6 +1082,10 @@
   static const char fn[] = "NfcTag::resetTechnologies";
   DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("%s", fn);
   mNumTechList = 0;
+  mNumDiscNtf = 0;
+  mNumDiscTechList = 0;
+  mTechListTail = 0;
+  mIsMultiProtocolTag = false;
   memset(mTechList, 0, sizeof(mTechList));
   memset(mTechHandles, 0, sizeof(mTechHandles));
   memset(mTechLibNfcTypes, 0, sizeof(mTechLibNfcTypes));
@@ -1100,26 +1110,27 @@
   int foundIdx = -1;
   tNFA_INTF_TYPE rf_intf = NFA_INTERFACE_FRAME;
 
-  for (int i = 0; i < mNumTechList; i++) {
+  for (int i = 0; i < mNumDiscTechList; i++) {
     DLOG_IF(INFO, nfc_debug_enabled)
         << StringPrintf("%s: nfa target idx=%d h=0x%X; protocol=0x%X", fn, i,
-                        mTechHandles[i], mTechLibNfcTypes[i]);
-    if (mTechLibNfcTypes[i] != NFA_PROTOCOL_NFC_DEP) {
+                        mTechHandlesDiscData[i], mTechLibNfcTypesDiscData[i]);
+    if (mTechLibNfcTypesDiscData[i] != NFA_PROTOCOL_NFC_DEP) {
+      sLastSelectedTagId = i;
       foundIdx = i;
       break;
     }
   }
 
   if (foundIdx != -1) {
-    if (mTechLibNfcTypes[foundIdx] == NFA_PROTOCOL_ISO_DEP) {
+    if (mTechLibNfcTypesDiscData[foundIdx] == NFA_PROTOCOL_ISO_DEP) {
       rf_intf = NFA_INTERFACE_ISO_DEP;
-    } else if (mTechLibNfcTypes[foundIdx] == NFC_PROTOCOL_MIFARE) {
+    } else if (mTechLibNfcTypesDiscData[foundIdx] == NFC_PROTOCOL_MIFARE) {
       rf_intf = NFA_INTERFACE_MIFARE;
     } else
       rf_intf = NFA_INTERFACE_FRAME;
 
-    tNFA_STATUS stat =
-        NFA_Select(mTechHandles[foundIdx], mTechLibNfcTypes[foundIdx], rf_intf);
+    tNFA_STATUS stat = NFA_Select(mTechHandlesDiscData[foundIdx],
+                                  mTechLibNfcTypesDiscData[foundIdx], rf_intf);
     if (stat != NFA_STATUS_OK)
       LOG(ERROR) << StringPrintf("%s: fail select; error=0x%X", fn, stat);
   } else
@@ -1128,6 +1139,67 @@
 
 /*******************************************************************************
 **
+** Function:        selectNextTagIfExists
+**
+** Description:     When multiple tags are discovered, selects the next tag to
+**                  activate.
+**
+** Returns:         None
+**
+*******************************************************************************/
+void NfcTag::selectNextTagIfExists() {
+  static const char fn[] = "NfcTag::selectNextTagIfExists";
+  int foundIdx = -1;
+  tNFA_INTF_TYPE rf_intf = NFA_INTERFACE_FRAME;
+  tNFA_STATUS stat = NFA_STATUS_FAILED;
+
+  if (mNumDiscNtf == 0) {
+    return;
+  }
+  mNumDiscNtf--;
+  DLOG_IF(INFO, nfc_debug_enabled)
+      << StringPrintf("%s: enter, mNumDiscTechList=%x", fn, mNumDiscTechList);
+  for (int i = 0; i < mNumDiscTechList; i++) {
+    DLOG_IF(INFO, nfc_debug_enabled)
+        << StringPrintf("%s: nfa target idx=%dh=0x%X; protocol=0x%X", fn, i,
+                        mTechHandlesDiscData[i], mTechLibNfcTypesDiscData[i]);
+    if (((mTechHandlesDiscData[sLastSelectedTagId] !=
+          mTechHandlesDiscData[i]) ||
+         (mTechLibNfcTypesDiscData[sLastSelectedTagId] !=
+          mTechLibNfcTypesDiscData[i])) &&
+        (mTechLibNfcTypesDiscData[i] != NFA_PROTOCOL_NFC_DEP)) {
+      sLastSelectedTagId = i;
+      foundIdx = i;
+      break;
+    }
+  }
+
+  if (foundIdx != -1) {
+    if (mTechLibNfcTypesDiscData[foundIdx] == NFA_PROTOCOL_ISO_DEP) {
+      rf_intf = NFA_INTERFACE_ISO_DEP;
+    } else if (mTechLibNfcTypesDiscData[foundIdx] == NFC_PROTOCOL_MIFARE) {
+      rf_intf = NFA_INTERFACE_MIFARE;
+    } else {
+      rf_intf = NFA_INTERFACE_FRAME;
+    }
+
+    stat = NFA_Select(mTechHandlesDiscData[foundIdx],
+                      mTechLibNfcTypesDiscData[foundIdx], rf_intf);
+    if (stat == NFA_STATUS_OK) {
+      DLOG_IF(ERROR, nfc_debug_enabled)
+          << StringPrintf("%s: Select Success, wait for activated ntf", fn);
+    } else {
+      DLOG_IF(ERROR, nfc_debug_enabled)
+          << StringPrintf("%s: fail select; error=0x%X", fn, stat);
+    }
+  } else {
+    DLOG_IF(ERROR, nfc_debug_enabled)
+        << StringPrintf("%s: only found NFC-DEP technology.", fn);
+  }
+}
+
+/*******************************************************************************
+**
 ** Function:        getT1tMaxMessageSize
 **
 ** Description:     Get the maximum size (octet) that a T1T can store.
@@ -1512,3 +1584,44 @@
   DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("%s: return=%u", fn, retval);
   return retval;
 }
+
+/*******************************************************************************
+**
+** Function:        setMultiProtocolTagSupport
+**
+** Description:     Update mIsMultiProtocolTag
+**
+** Returns:         None
+**
+*******************************************************************************/
+
+void NfcTag::setMultiProtocolTagSupport(bool isMultiProtocolSupported) {
+  mIsMultiProtocolTag = isMultiProtocolSupported;
+}
+
+/*******************************************************************************
+**
+** Function:        setNumDiscNtf
+**
+** Description:     Update number of Discovery NTF received
+**
+** Returns:         None
+**
+*******************************************************************************/
+void NfcTag::setNumDiscNtf(int numDiscNtfValue) {
+  if (numDiscNtfValue < MAX_NUM_TECHNOLOGY) {
+    mNumDiscNtf = numDiscNtfValue;
+  }
+}
+
+/*******************************************************************************
+**
+** Function:        getNumDiscNtf
+**
+** Description:     number of discovery notifications received from NFCC after
+**                  last RF DISCOVERY state
+**
+** Returns:         number of discovery notifications received from NFCC
+**
+*******************************************************************************/
+int NfcTag::getNumDiscNtf() { return mNumDiscNtf; }
\ No newline at end of file
diff --git a/nci/jni/NfcTag.h b/nci/jni/NfcTag.h
index bda0aed..4769df1 100644
--- a/nci/jni/NfcTag.h
+++ b/nci/jni/NfcTag.h
@@ -25,6 +25,9 @@
 
 #include "nfa_rw_api.h"
 
+#define MIN_FWI (11)
+#define MAX_FWI (14)
+
 class NfcTag {
  public:
   enum ActivationState { Idle, Sleep, Active };
@@ -32,10 +35,13 @@
       11;  // max number of technologies supported by one or more tags
   int mTechList[MAX_NUM_TECHNOLOGY];  // array of NFC technologies according to
                                       // NFC service
-  int mTechHandles[MAX_NUM_TECHNOLOGY];  // array of tag handles according to
-                                         // NFC service
-  int mTechLibNfcTypes[MAX_NUM_TECHNOLOGY];  // array of detailed tag types
-                                             // according to NFC service
+  int mTechHandles[MAX_NUM_TECHNOLOGY];  // array of tag handles (RF DISC ID)
+                                         // according to NFC service received
+                                         // from RF_INTF_ACTIVATED NTF
+  int mTechLibNfcTypes[MAX_NUM_TECHNOLOGY];  // array of detailed tag types (RF
+                                             // Protocol) according to NFC
+                                             // service received from
+                                             // RF_INTF_ACTIVATED NTF
   int mNumTechList;  // current number of NFC technologies in the list
 
   /*******************************************************************************
@@ -187,6 +193,18 @@
 
   /*******************************************************************************
   **
+  ** Function:        selectNextTagIfExists
+  **
+  ** Description:     When multiple tags are discovered, selects the Next one to
+  **                  activate.
+  **
+  ** Returns:         None
+  **
+  *******************************************************************************/
+  void selectNextTagIfExists();
+
+  /*******************************************************************************
+  **
   ** Function:        getT1tMaxMessageSize
   **
   ** Description:     Get the maximum size (octet) that a T1T can store.
@@ -309,7 +327,7 @@
   **
   ** Description:     Get the timeout value for one technology.
   **                  techId: one of the values in TARGET_TYPE_* defined in
-  *NfcJniUtil.h
+  **                  NfcJniUtil.h
   **
   ** Returns:         Timeout value in millisecond.
   **
@@ -364,6 +382,40 @@
   *******************************************************************************/
   bool isKovioType2Tag();
 
+  /*******************************************************************************
+  **
+  ** Function:        setMultiProtocolTagSupport
+  **
+  ** Description:     Update mIsMultiProtocolTag
+  **
+  ** Returns:         None
+  **
+  *******************************************************************************/
+  void setMultiProtocolTagSupport(bool isMultiProtocolSupported);
+
+  /*******************************************************************************
+  **
+  ** Function:        setNumDiscNtf
+  **
+  ** Description:     Update mNumDiscNtf
+  **
+  ** Returns:         None
+  **
+  *******************************************************************************/
+  void setNumDiscNtf(int numDiscNtfValue);
+
+  /*******************************************************************************
+  **
+  ** Function:        getNumDiscNtf
+  **
+  ** Description:     number of discovery notifications received from NFCC after
+  **                  last RF DISCOVERY state
+  **
+  ** Returns:         number of discovery notifications received from NFCC
+  **
+  *******************************************************************************/
+  int getNumDiscNtf();
+
  private:
   std::vector<int> mTechnologyTimeoutsTable;
   std::vector<int> mTechnologyDefaultTimeoutsTable;
@@ -383,6 +435,17 @@
   bool mIsDynamicTagId;  // whether the tag has dynamic tag ID
   tNFA_RW_PRES_CHK_OPTION mPresenceCheckAlgorithm;
   bool mIsFelicaLite;
+  int mTechHandlesDiscData[MAX_NUM_TECHNOLOGY];      // array of tag handles (RF
+                                                     // DISC ID) received from
+                                                     // RF_DISC_NTF
+  int mTechLibNfcTypesDiscData[MAX_NUM_TECHNOLOGY];  // array of detailed tag
+                                                     // types ( RF Protocol)
+                                                     // received from
+                                                     // RF_DISC_NTF
+  int mNumDiscNtf;
+  int mNumDiscTechList;
+  int mTechListTail;  // Index of Last added entry in mTechList
+  bool mIsMultiProtocolTag;
 
   /*******************************************************************************
   **
diff --git a/nci/jni/RoutingManager.cpp b/nci/jni/RoutingManager.cpp
index 69ace9f..93ba9e0 100755
--- a/nci/jni/RoutingManager.cpp
+++ b/nci/jni/RoutingManager.cpp
@@ -61,7 +61,10 @@
 
 static const uint8_t AID_ROUTE_QUAL_PREFIX = 0x10;
 
-RoutingManager::RoutingManager() : mAidRoutingConfigured(false) {
+RoutingManager::RoutingManager()
+    : mSecureNfcEnabled(false),
+      mNativeData(NULL),
+      mAidRoutingConfigured(false) {
   static const char fn[] = "RoutingManager::RoutingManager()";
 
   mDefaultOffHostRoute =
@@ -107,6 +110,10 @@
 
   mDefaultIsoDepRoute = NfcConfig::getUnsigned(NAME_DEFAULT_ISODEP_ROUTE, 0x0);
 
+  mHostListenTechMask =
+      NfcConfig::getUnsigned(NAME_HOST_LISTEN_TECH_MASK,
+                             NFA_TECHNOLOGY_MASK_A | NFA_TECHNOLOGY_MASK_F);
+
   memset(&mEeInfo, 0, sizeof(mEeInfo));
   mReceivedEeInfo = false;
   mSeTechMask = 0x00;
@@ -147,8 +154,10 @@
   }
   mSeTechMask = updateEeTechRouteSetting();
 
-  // Tell the host-routing to only listen on Nfc-A
-  tNFA_STATUS nfaStat = NFA_CeSetIsoDepListenTech(NFA_TECHNOLOGY_MASK_A);
+  // Set the host-routing Tech
+  tNFA_STATUS nfaStat = NFA_CeSetIsoDepListenTech(
+      mHostListenTechMask & (NFA_TECHNOLOGY_MASK_A | NFA_TECHNOLOGY_MASK_B));
+
   if (nfaStat != NFA_STATUS_OK)
     LOG(ERROR) << StringPrintf("Failed to configure CE IsoDep technologies");
 
@@ -196,7 +205,8 @@
 
   // Route Nfc-A to host if we don't have a SE
   tNFA_TECHNOLOGY_MASK techMask = NFA_TECHNOLOGY_MASK_A;
-  if ((mSeTechMask & NFA_TECHNOLOGY_MASK_A) == 0) {
+  if ((mHostListenTechMask & NFA_TECHNOLOGY_MASK_A) &&
+      (mSeTechMask & NFA_TECHNOLOGY_MASK_A) == 0) {
     nfaStat = NFA_EeSetDefaultTechRouting(
         NFC_DH_ID, techMask, 0, 0, mSecureNfcEnabled ? 0 : techMask,
         mSecureNfcEnabled ? 0 : techMask, mSecureNfcEnabled ? 0 : techMask);
@@ -206,9 +216,23 @@
       LOG(ERROR) << fn << "Fail to set default tech routing for Nfc-A";
   }
 
+  // Route Nfc-B to host if we don't have a SE
+  techMask = NFA_TECHNOLOGY_MASK_B;
+  if ((mHostListenTechMask & NFA_TECHNOLOGY_MASK_B) &&
+      (mSeTechMask & NFA_TECHNOLOGY_MASK_B) == 0) {
+    nfaStat = NFA_EeSetDefaultTechRouting(
+        NFC_DH_ID, techMask, 0, 0, mSecureNfcEnabled ? 0 : techMask,
+        mSecureNfcEnabled ? 0 : techMask, mSecureNfcEnabled ? 0 : techMask);
+    if (nfaStat == NFA_STATUS_OK)
+      mRoutingEvent.wait();
+    else
+      LOG(ERROR) << fn << "Fail to set default tech routing for Nfc-B";
+  }
+
   // Route Nfc-F to host if we don't have a SE
   techMask = NFA_TECHNOLOGY_MASK_F;
-  if ((mSeTechMask & NFA_TECHNOLOGY_MASK_F) == 0) {
+  if ((mHostListenTechMask & NFA_TECHNOLOGY_MASK_F) &&
+      (mSeTechMask & NFA_TECHNOLOGY_MASK_F) == 0) {
     nfaStat = NFA_EeSetDefaultTechRouting(
         NFC_DH_ID, techMask, 0, 0, mSecureNfcEnabled ? 0 : techMask,
         mSecureNfcEnabled ? 0 : techMask, mSecureNfcEnabled ? 0 : techMask);
@@ -224,41 +248,53 @@
   tNFA_STATUS nfaStat;
   SyncEventGuard guard(mRoutingEvent);
 
-  // Default routing for IsoDep protocol
+  // Clear default routing for IsoDep protocol
   if (mDefaultIsoDepRoute == NFC_DH_ID) {
     nfaStat =
         NFA_EeClearDefaultProtoRouting(NFC_DH_ID, NFA_PROTOCOL_MASK_ISO_DEP);
     if (nfaStat == NFA_STATUS_OK)
       mRoutingEvent.wait();
     else
-      LOG(ERROR) << fn << "Fail to set default proto routing for IsoDep";
+      LOG(ERROR) << fn << "Fail to clear default proto routing for IsoDep";
   }
 
-  // Default routing for Nfc-A technology if we don't have a SE
-  if ((mSeTechMask & NFA_TECHNOLOGY_MASK_A) == 0) {
+  // Clear default routing for Nfc-A technology if we don't have a SE
+  if ((mHostListenTechMask & NFA_TECHNOLOGY_MASK_A) &&
+      (mSeTechMask & NFA_TECHNOLOGY_MASK_A) == 0) {
     nfaStat = NFA_EeClearDefaultTechRouting(NFC_DH_ID, NFA_TECHNOLOGY_MASK_A);
     if (nfaStat == NFA_STATUS_OK)
       mRoutingEvent.wait();
     else
-      LOG(ERROR) << fn << "Fail to set default tech routing for Nfc-A";
+      LOG(ERROR) << fn << "Fail to clear default tech routing for Nfc-A";
   }
 
-  // Default routing for Nfc-F technology if we don't have a SE
-  if ((mSeTechMask & NFA_TECHNOLOGY_MASK_F) == 0) {
+  // Clear default routing for Nfc-B technology if we don't have a SE
+  if ((mHostListenTechMask & NFA_TECHNOLOGY_MASK_B) &&
+      (mSeTechMask & NFA_TECHNOLOGY_MASK_B) == 0) {
+    nfaStat = NFA_EeClearDefaultTechRouting(NFC_DH_ID, NFA_TECHNOLOGY_MASK_B);
+    if (nfaStat == NFA_STATUS_OK)
+      mRoutingEvent.wait();
+    else
+      LOG(ERROR) << fn << "Fail to clear default tech routing for Nfc-B";
+  }
+
+  // Clear default routing for Nfc-F technology if we don't have a SE
+  if ((mHostListenTechMask & NFA_TECHNOLOGY_MASK_F) &&
+      (mSeTechMask & NFA_TECHNOLOGY_MASK_F) == 0) {
     nfaStat = NFA_EeClearDefaultTechRouting(NFC_DH_ID, NFA_TECHNOLOGY_MASK_F);
     if (nfaStat == NFA_STATUS_OK)
       mRoutingEvent.wait();
     else
-      LOG(ERROR) << fn << "Fail to set default tech routing for Nfc-F";
+      LOG(ERROR) << fn << "Fail to clear default tech routing for Nfc-F";
   }
 
-  // Default routing for T3T protocol
+  // Clear default routing for T3T protocol
   if (!mIsScbrSupported && mDefaultEe == NFC_DH_ID) {
     nfaStat = NFA_EeClearDefaultProtoRouting(NFC_DH_ID, NFA_PROTOCOL_MASK_T3T);
     if (nfaStat == NFA_STATUS_OK)
       mRoutingEvent.wait();
     else
-      LOG(ERROR) << fn << "Fail to set default proto routing for T3T";
+      LOG(ERROR) << fn << "Fail to clear default proto routing for T3T";
   }
 }
 
@@ -661,14 +697,24 @@
   }
 
   // Clear DH technology route on NFC-A
-  if ((allSeTechMask & NFA_TECHNOLOGY_MASK_A) != 0) {
+  if ((mHostListenTechMask & NFA_TECHNOLOGY_MASK_A) &&
+      (allSeTechMask & NFA_TECHNOLOGY_MASK_A) != 0) {
     nfaStat = NFA_EeClearDefaultTechRouting(NFC_DH_ID, NFA_TECHNOLOGY_MASK_A);
     if (nfaStat != NFA_STATUS_OK)
       LOG(ERROR) << "Failed to clear DH technology routing on NFC-A.";
   }
 
+  // Clear DH technology route on NFC-B
+  if ((mHostListenTechMask & NFA_TECHNOLOGY_MASK_B) &&
+      (allSeTechMask & NFA_TECHNOLOGY_MASK_B) != 0) {
+    nfaStat = NFA_EeClearDefaultTechRouting(NFC_DH_ID, NFA_TECHNOLOGY_MASK_B);
+    if (nfaStat != NFA_STATUS_OK)
+      LOG(ERROR) << "Failed to clear DH technology routing on NFC-B.";
+  }
+
   // Clear DH technology route on NFC-F
-  if ((allSeTechMask & NFA_TECHNOLOGY_MASK_F) != 0) {
+  if ((mHostListenTechMask & NFA_TECHNOLOGY_MASK_F) &&
+      (allSeTechMask & NFA_TECHNOLOGY_MASK_F) != 0) {
     nfaStat = NFA_EeClearDefaultTechRouting(NFC_DH_ID, NFA_TECHNOLOGY_MASK_F);
     if (nfaStat != NFA_STATUS_OK)
       LOG(ERROR) << "Failed to clear DH technology routing on NFC-F.";
@@ -692,8 +738,11 @@
   static const char fn[] = "RoutingManager::nfaEeCallback";
 
   RoutingManager& routingManager = RoutingManager::getInstance();
-  if (eventData) routingManager.mCbEventData = *eventData;
-
+  if (!eventData) {
+    LOG(ERROR) << "eventData is null";
+    return;
+  }
+  routingManager.mCbEventData = *eventData;
   switch (event) {
     case NFA_EE_REGISTER_EVT: {
       SyncEventGuard guard(routingManager.mEeRegisterEvent);
diff --git a/nci/jni/RoutingManager.h b/nci/jni/RoutingManager.h
index 4bfc886..51ef4f4 100755
--- a/nci/jni/RoutingManager.h
+++ b/nci/jni/RoutingManager.h
@@ -107,6 +107,7 @@
   uint16_t mDefaultSysCodeRoute;
   uint8_t mDefaultSysCodePowerstate;
   uint8_t mOffHostAidRoutingPowerState;
+  uint8_t mHostListenTechMask;
   bool mDeinitializing;
   bool mEeInfoChanged;
   bool mReceivedEeInfo;
diff --git a/nci/jni/extns/pn54x/inc/phNxpExtns.h b/nci/jni/extns/pn54x/inc/phNxpExtns.h
index 8984aed..17ee6ae 100644
--- a/nci/jni/extns/pn54x/inc/phNxpExtns.h
+++ b/nci/jni/extns/pn54x/inc/phNxpExtns.h
@@ -24,7 +24,7 @@
 
 NFCSTATUS EXTNS_Init(tNFA_DM_CBACK* p_dm_cback, tNFA_CONN_CBACK* p_conn_cback);
 void EXTNS_Close(void);
-NFCSTATUS EXTNS_MfcInit(tNFA_ACTIVATED activationData);
+NFCSTATUS EXTNS_MfcInit(tNFA_ACTIVATED& activationData);
 NFCSTATUS EXTNS_MfcCheckNDef(void);
 NFCSTATUS EXTNS_MfcReadNDef(void);
 NFCSTATUS EXTNS_MfcPresenceCheck(void);
diff --git a/nci/jni/extns/pn54x/src/mifare/phFriNfc_MifareStdMap.cpp b/nci/jni/extns/pn54x/src/mifare/phFriNfc_MifareStdMap.cpp
index 78d0ed9..5fcf693 100644
--- a/nci/jni/extns/pn54x/src/mifare/phFriNfc_MifareStdMap.cpp
+++ b/nci/jni/extns/pn54x/src/mifare/phFriNfc_MifareStdMap.cpp
@@ -1379,8 +1379,6 @@
     if (NdefMap->CardType == PH_FRINFC_NDEFMAP_MIFARE_STD_1K_CARD) {
       /* if Sector Id > 15 No Sectors to write */
       if (SectorID > 15) {
-        SectorID =
-            phFriNfc_MifStd_H_GetSect(NdefMap->StdMifareContainer.currentBlock);
         /*Error: No Ndef Compliant Sectors present */
         Result = PHNFCSTVAL(CID_FRI_NFC_NDEF_MAP, NFCSTATUS_INVALID_PARAMETER);
         callbreak = 1;
@@ -1413,8 +1411,6 @@
         } else {
           phFriNfc_MifStd1k_H_BlkChk(NdefMap, SectorID, &callbreak);
         }
-      } else {
-        phFriNfc_MifStd1k_H_BlkChk(NdefMap, SectorID, &callbreak);
       }
     } /* End of if*/ /* End of Mifare 2k check*/
     else             /* Mifare 4k check starts here */
@@ -2676,18 +2672,9 @@
 
   if (*TL4bytesFlag == PH_FRINFC_MIFARESTD_FLAG0) {
     (*TempLength) += (NdefMap->SendRecvBuf[TempLen] + PH_FRINFC_MIFARESTD_VAL1);
-
-    if (NdefMap->TLVStruct.NdefTLVFoundFlag == PH_FRINFC_MIFARESTD_FLAG0) {
-      LengthRemaining =
-          (((*TempLength) < PH_FRINFC_MIFARESTD_BYTES_READ)
-               ? PH_FRINFC_MIFARESTD_VAL0
-               : (NdefMap->SendRecvBuf[TempLen] - LengthRemaining));
-    } else {
-      LengthRemaining =
-          (((*TempLength) < PH_FRINFC_MIFARESTD_BYTES_READ)
-               ? PH_FRINFC_MIFARESTD_VAL0
-               : (NdefMap->SendRecvBuf[TempLen] - LengthRemaining));
-    }
+    LengthRemaining = (((*TempLength) < PH_FRINFC_MIFARESTD_BYTES_READ)
+                           ? PH_FRINFC_MIFARESTD_VAL0
+                           : (NdefMap->SendRecvBuf[TempLen] - LengthRemaining));
   } else {
     *TL4bytesFlag = PH_FRINFC_MIFARESTD_FLAG0;
     if (NdefMap->TLVStruct.NoLbytesinTLV == PH_FRINFC_MIFARESTD_VAL1) {
@@ -3524,8 +3511,8 @@
   uint8_t TempLength = PH_FRINFC_MIFARESTD_VAL0;
 
   if (*NdefMap->SendRecvLength == PH_FRINFC_MIFARESTD_BYTES_READ) {
-    memcpy(&NdefMap->SendRecvBuf[PH_FRINFC_MIFARESTD_VAL1],
-           NdefMap->SendRecvBuf, PH_FRINFC_MIFARESTD_BLOCK_BYTES);
+    memmove(&NdefMap->SendRecvBuf[PH_FRINFC_MIFARESTD_VAL1],
+            NdefMap->SendRecvBuf, PH_FRINFC_MIFARESTD_BLOCK_BYTES);
 
     /* Write to Ndef TLV Block */
     NdefMap->SendRecvBuf[PH_FRINFC_MIFARESTD_VAL0] =
diff --git a/nci/jni/extns/pn54x/src/mifare/phNxpExtns_MifareStd.cpp b/nci/jni/extns/pn54x/src/mifare/phNxpExtns_MifareStd.cpp
index 77a0dc1..81ac416 100644
--- a/nci/jni/extns/pn54x/src/mifare/phNxpExtns_MifareStd.cpp
+++ b/nci/jni/extns/pn54x/src/mifare/phNxpExtns_MifareStd.cpp
@@ -35,7 +35,7 @@
 #if (NFC_NXP_NOT_OPEN_INCLUDED == TRUE)
 pthread_mutex_t SharedDataMutex = PTHREAD_MUTEX_INITIALIZER;
 #endif
-uint8_t current_key[6] = {0};
+uint8_t current_key[PHLIBNFC_MFC_AUTHKEYLEN] = {0};
 phNci_mfc_auth_cmd_t gAuthCmdBuf;
 static NFCSTATUS phNciNfc_SendMfReq(phNciNfc_TransceiveInfo_t tTranscvInfo,
                                     uint8_t* buff, uint16_t* buffSz);
@@ -637,9 +637,7 @@
     /* Set Completion Routine for ReadNdef */
     NdefMap->CompletionRoutine[1].CompletionRoutine =
         Mfc_ReadNdef_Completion_Routine;
-    NdefInfo.NdefContinueRead = (uint8_t)((phLibNfc_Ndef_EBegin == Offset)
-                                              ? PH_FRINFC_NDEFMAP_SEEK_BEGIN
-                                              : PH_FRINFC_NDEFMAP_SEEK_CUR);
+    NdefInfo.NdefContinueRead = (uint8_t)(PH_FRINFC_NDEFMAP_SEEK_BEGIN);
   }
 
   PacketData = NdefInfo.psUpperNdefMsg->buffer;
@@ -854,13 +852,15 @@
 *******************************************************************************/
 NFCSTATUS Mfc_FormatNdef(uint8_t* secretkey, uint8_t len) {
   NFCSTATUS status = NFCSTATUS_FAILED;
-  uint8_t mif_std_key[6] = {0};
+  uint8_t mif_std_key[PHLIBNFC_MFC_AUTHKEYLEN] = {0};
   //    static uint8_t   Index;
   //    /*commented to eliminate unused variable warning*/
   uint8_t sak = 0;
 
   EXTNS_SetCallBackFlag(false);
 
+  if (len != PHLIBNFC_MFC_AUTHKEYLEN) return NFCSTATUS_FAILED;
+
   memcpy(mif_std_key, secretkey, len);
   memcpy(current_key, secretkey, len);
 
@@ -1525,12 +1525,6 @@
   int32_t sdwStat = 0X00;
   NFCSTATUS wStatus = NFCSTATUS_INVALID_PARAMETER;
 
-  /*Key Configuration
-    uint8_t NdefKey[PHLIBNFC_MFC_AUTHKEYLEN] = {0xD3,0XF7,0xD3,0XF7,0xD3,0XF7};
-    uint8_t RawKey[PHLIBNFC_MFC_AUTHKEYLEN] = {0xFF,0XFF,0xFF,0XFF,0xFF,0XFF};
-    uint8_t MadKey[PHLIBNFC_MFC_AUTHKEYLEN] = {0xA0,0XA1,0xA2,0XA3,0xA4,0XA5};
-    uint8_t Key[PHLIBNFC_MFC_AUTHKEYLEN] = {0x00,0x00,0x00,0x00,0x00,0x00}; */ /*Key used during ndef format*/
-
   uint8_t bIndex = 0x00;
   uint8_t bNoOfKeys = 0x00;
 
diff --git a/nci/jni/extns/pn54x/src/mifare/phNxpExtns_MifareStd.h b/nci/jni/extns/pn54x/src/mifare/phNxpExtns_MifareStd.h
index fc40157..465ff9c 100644
--- a/nci/jni/extns/pn54x/src/mifare/phNxpExtns_MifareStd.h
+++ b/nci/jni/extns/pn54x/src/mifare/phNxpExtns_MifareStd.h
@@ -68,16 +68,14 @@
 
 #define NDEF_SENDRCV_BUF_LEN 252U /* Send receive buffer length */
 
-#define NXP_NUMBER_OF_MFC_KEYS (0x04U)
+#define NXP_NUMBER_OF_MFC_KEYS (0x03U)
 #define NXP_MFC_KEY_SIZE (0x06U)
 
 #define NXP_MFC_KEYS                            \
   {                                             \
     {0xA0, 0XA1, 0xA2, 0XA3, 0xA4, 0XA5},       \
-        {0xD3, 0XF7, 0xD3, 0XF7, 0xD3, 0XF7},   \
-        {0xFF, 0XFF, 0xFF, 0XFF, 0xFF, 0XFF}, { \
-      0x00, 0x00, 0x00, 0x00, 0x00, 0x00        \
-    }                                           \
+    {0xD3, 0XF7, 0xD3, 0XF7, 0xD3, 0XF7},       \
+    {0xFF, 0XFF, 0xFF, 0XFF, 0xFF, 0XFF}        \
   } /* Key used during NDEF format */
 
 #ifndef NCI_MAX_DATA_LEN
diff --git a/nci/jni/extns/pn54x/src/phNxpExtns.cpp b/nci/jni/extns/pn54x/src/phNxpExtns.cpp
index 0c4d0ff..99f03c5 100644
--- a/nci/jni/extns/pn54x/src/phNxpExtns.cpp
+++ b/nci/jni/extns/pn54x/src/phNxpExtns.cpp
@@ -414,7 +414,7 @@
 ** Returns          NFCSTATUS_SUCCESS
 **
 *******************************************************************************/
-NFCSTATUS EXTNS_MfcInit(tNFA_ACTIVATED activationData) {
+NFCSTATUS EXTNS_MfcInit(tNFA_ACTIVATED& activationData) {
   tNFC_ACTIVATE_DEVT rfDetail = activationData.activate_ntf;
 
   NdefMap->psRemoteDevInfo->RemoteDevInfo.Iso14443A_Info.Sak =
diff --git a/nci/src/com/android/nfc/dhimpl/NativeNfcManager.java b/nci/src/com/android/nfc/dhimpl/NativeNfcManager.java
index 5a57988..027f4ff 100755
--- a/nci/src/com/android/nfc/dhimpl/NativeNfcManager.java
+++ b/nci/src/com/android/nfc/dhimpl/NativeNfcManager.java
@@ -389,6 +389,9 @@
         return doSetNfcSecure(enable);
     }
 
+    @Override
+    public native String getNfaStorageDir();
+
     /**
      * Notifies Ndef Message (TODO: rename into notifyTargetDiscovered)
      */
diff --git a/res/values-night/styles.xml b/res/values-night/styles.xml
new file mode 100644
index 0000000..64c4afc
--- /dev/null
+++ b/res/values-night/styles.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+
+<resources>
+    <style name="DialogAlertDayNight" parent="@android:style/Theme.DeviceDefault.Dialog.Alert"/>
+</resources>
diff --git a/res/values/config.xml b/res/values/config.xml
index 50ad038..2186cb8 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -5,8 +5,10 @@
     <bool name="enable_auto_play">true</bool>
     <bool name="enable_notify_dispatch_failed">false</bool>
     <bool name="enable_antenna_blocked_alert">false</bool>
+    <bool name="polling_disable_allowed">false</bool>
     <integer name="max_antenna_blocked_failure_count">10</integer>
-    <integer name="unknown_tag_polling_delay">-1</integer>
+    <integer name="toast_debounce_time_ms">3000</integer>
+    <integer name="unknown_tag_polling_delay">2000</integer>
 
     <!-- List of SKUs where Secure NFC functionality is supported -->
     <string-array name="config_skuSupportsSecureNfc" translatable="false" />
diff --git a/res/values/styles.xml b/res/values/styles.xml
new file mode 100644
index 0000000..c98dccc
--- /dev/null
+++ b/res/values/styles.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+
+<resources>
+    <style name="DialogAlertDayNight" parent="@android:style/Theme.DeviceDefault.Light.Dialog.Alert"/>
+</resources>
diff --git a/src/com/android/nfc/BeamShareActivity.java b/src/com/android/nfc/BeamShareActivity.java
index 446e499..37e8d1d 100644
--- a/src/com/android/nfc/BeamShareActivity.java
+++ b/src/com/android/nfc/BeamShareActivity.java
@@ -98,7 +98,7 @@
         registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, null);
 
         AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this,
-                AlertDialog.THEME_DEVICE_DEFAULT_LIGHT);
+                com.android.nfc.R.style.DialogAlertDayNight);
         dialogBuilder.setMessage(msgId);
         dialogBuilder.setOnCancelListener(new DialogInterface.OnCancelListener() {
             @Override
diff --git a/src/com/android/nfc/ConfirmConnectToWifiNetworkActivity.java b/src/com/android/nfc/ConfirmConnectToWifiNetworkActivity.java
index eb26198..8752dbb 100644
--- a/src/com/android/nfc/ConfirmConnectToWifiNetworkActivity.java
+++ b/src/com/android/nfc/ConfirmConnectToWifiNetworkActivity.java
@@ -33,7 +33,7 @@
                 intent.getParcelableExtra(NfcWifiProtectedSetup.EXTRA_WIFI_CONFIG);
 
         String printableSsid = mCurrentWifiConfiguration.getPrintableSsid();
-        mAlertDialog = new AlertDialog.Builder(this,  AlertDialog.THEME_DEVICE_DEFAULT_LIGHT)
+        mAlertDialog = new AlertDialog.Builder(this, R.style.DialogAlertDayNight)
                 .setTitle(R.string.title_connect_to_network)
                 .setMessage(
                         String.format(getResources().getString(R.string.prompt_connect_to_network),
diff --git a/src/com/android/nfc/DeviceHost.java b/src/com/android/nfc/DeviceHost.java
index 6d3bb12..43722ab 100644
--- a/src/com/android/nfc/DeviceHost.java
+++ b/src/com/android/nfc/DeviceHost.java
@@ -258,4 +258,6 @@
     public void shutdown();
 
     public boolean setNfcSecure(boolean enable);
+
+    public String getNfaStorageDir();
 }
diff --git a/src/com/android/nfc/ForegroundUtils.java b/src/com/android/nfc/ForegroundUtils.java
index d718537..48d7b88 100644
--- a/src/com/android/nfc/ForegroundUtils.java
+++ b/src/com/android/nfc/ForegroundUtils.java
@@ -112,7 +112,17 @@
     }
 
     private boolean isInForegroundLocked(int uid) {
-        return mForegroundUidPids.get(uid) != null;
+        if (mForegroundUidPids.get(uid) != null)
+            return true;
+        if (DBG) Log.d(TAG, "Checking UID:" + Integer.toString(uid));
+        try {
+            // If the onForegroundActivitiesChanged() has not yet been called,
+            // check whether the UID is in an active state to use the NFC.
+            return mIActivityManager.isUidActive(uid, NfcApplication.NFC_PROCESS);
+        } catch (RemoteException e) {
+            Log.e(TAG, "ForegroundUtils: could not get isUidActive");
+        }
+        return false;
     }
 
     private void handleUidToBackground(int uid) {
diff --git a/src/com/android/nfc/NfcBlockedNotification.java b/src/com/android/nfc/NfcBlockedNotification.java
index 34421cc..9db74f6 100644
--- a/src/com/android/nfc/NfcBlockedNotification.java
+++ b/src/com/android/nfc/NfcBlockedNotification.java
@@ -16,49 +16,61 @@
 
 package com.android.nfc;
 
-import android.app.Activity;
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.content.Context;
-import android.content.DialogInterface;
 import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.res.Resources;
 import android.net.Uri;
-import android.os.Bundle;
 import android.text.TextUtils;
+
 import com.android.nfc.R;
 
-public class NfcBlockedNotification extends Activity {
+/**
+ * This class handles the Notification Manager for the antenna blocked notification
+ */
+
+public class NfcBlockedNotification {
     private static final String NFC_NOTIFICATION_CHANNEL = "nfc_notification_channel";
     private NotificationChannel mNotificationChannel;
     public static final int NOTIFICATION_ID_NFC = -1000001;
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
+    Context mContext;
+
+    /**
+     * Constructor
+     *
+     * @param ctx The context to use to obtain access to the resources
+     */
+    public NfcBlockedNotification(Context ctx) {
+        mContext = ctx;
+    }
+
+    /**
+     * Start the notification.
+     */
+    public void startNotification() {
         Intent infoIntent;
-        if (TextUtils.isEmpty(getString(R.string.antenna_blocked_alert_link))) {
+        if (TextUtils.isEmpty(mContext.getString(R.string.antenna_blocked_alert_link))) {
             // Do nothing after user click the notification if antenna_blocked_alert_link is empty
             infoIntent = new Intent();
         } else {
             // Open the link after user click the notification
             infoIntent = new Intent(Intent.ACTION_VIEW);
-            infoIntent.setData(Uri.parse(getString(R.string.antenna_blocked_alert_link)));
+            infoIntent.setData(Uri.parse(mContext.getString(R.string.antenna_blocked_alert_link)));
         }
-        Notification.Builder builder = new Notification.Builder(this, NFC_NOTIFICATION_CHANNEL);
-        builder.setContentTitle(getString(R.string.nfc_blocking_alert_title))
-               .setContentText(getString(R.string.nfc_blocking_alert_message))
-               .setSmallIcon(android.R.drawable.stat_sys_warning)
-               .setPriority(NotificationManager.IMPORTANCE_DEFAULT)
-               .setAutoCancel(true)
-               .setContentIntent(PendingIntent.getActivity(getApplicationContext(), 0, infoIntent,
-                       PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE));
+        Notification.Builder builder = new Notification.Builder(mContext, NFC_NOTIFICATION_CHANNEL);
+        builder.setContentTitle(mContext.getString(R.string.nfc_blocking_alert_title))
+                .setContentText(mContext.getString(R.string.nfc_blocking_alert_message))
+                .setSmallIcon(android.R.drawable.stat_sys_warning)
+                .setPriority(NotificationManager.IMPORTANCE_DEFAULT)
+                .setAutoCancel(true)
+                .setContentIntent(PendingIntent.getActivity(mContext, 0, infoIntent,
+                      PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE));
         mNotificationChannel = new NotificationChannel(NFC_NOTIFICATION_CHANNEL,
-            getString(R.string.nfcUserLabel), NotificationManager.IMPORTANCE_DEFAULT);
+                mContext.getString(R.string.nfcUserLabel), NotificationManager.IMPORTANCE_DEFAULT);
         NotificationManager notificationManager =
-            getApplicationContext().getSystemService(NotificationManager.class);
+                mContext.getSystemService(NotificationManager.class);
         notificationManager.createNotificationChannel(mNotificationChannel);
         notificationManager.notify(NOTIFICATION_ID_NFC, builder.build());
     }
diff --git a/src/com/android/nfc/NfcDiscoveryParameters.java b/src/com/android/nfc/NfcDiscoveryParameters.java
index 1149836..3226aa4 100644
--- a/src/com/android/nfc/NfcDiscoveryParameters.java
+++ b/src/com/android/nfc/NfcDiscoveryParameters.java
@@ -16,6 +16,8 @@
 
 package com.android.nfc;
 
+import android.util.proto.ProtoOutputStream;
+
 /**
  * Parameters for enabling NFC tag discovery and polling,
  * and host card emulation.
@@ -137,6 +139,15 @@
         return sb.toString();
     }
 
+    /** Dumps DiscoveryParamsProto for debugging. */
+    void dumpDebug(ProtoOutputStream proto) {
+        proto.write(DiscoveryParamsProto.TECH_MASK, mTechMask);
+        proto.write(DiscoveryParamsProto.ENABLE_LPD, mEnableLowPowerDiscovery);
+        proto.write(DiscoveryParamsProto.ENABLE_READER, mEnableReaderMode);
+        proto.write(DiscoveryParamsProto.ENABLE_HOST_ROUTING, mEnableHostRouting);
+        proto.write(DiscoveryParamsProto.ENABLE_P2P, mEnableP2p);
+    }
+
     public static NfcDiscoveryParameters.Builder newBuilder() {
         return new Builder();
     }
diff --git a/src/com/android/nfc/NfcDispatcher.java b/src/com/android/nfc/NfcDispatcher.java
index a41cd2e..0f46087 100644
--- a/src/com/android/nfc/NfcDispatcher.java
+++ b/src/com/android/nfc/NfcDispatcher.java
@@ -55,6 +55,7 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.util.Log;
+import android.util.proto.ProtoOutputStream;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.WindowManager;
@@ -65,12 +66,12 @@
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.StringJoiner;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Locale;
 
-import android.util.StatsLog;
 /**
  * Dispatch of NFC events to start activities
  */
@@ -241,7 +242,8 @@
                     ActivityManager.getCurrentUser());
             if (activities.size() > 0) {
                 context.startActivityAsUser(rootIntent, UserHandle.CURRENT);
-                StatsLog.write(StatsLog.NFC_TAG_OCCURRED, StatsLog.NFC_TAG_OCCURRED__TYPE__APP_LAUNCH);
+                NfcStatsLog.write(NfcStatsLog.NFC_TAG_OCCURRED,
+                        NfcStatsLog.NFC_TAG_OCCURRED__TYPE__APP_LAUNCH);
                 return true;
             }
             return false;
@@ -253,7 +255,8 @@
             if (activities.size() > 0) {
                 rootIntent.putExtra(NfcRootActivity.EXTRA_LAUNCH_INTENT, intentToStart);
                 context.startActivityAsUser(rootIntent, UserHandle.CURRENT);
-                StatsLog.write(StatsLog.NFC_TAG_OCCURRED, StatsLog.NFC_TAG_OCCURRED__TYPE__APP_LAUNCH);
+                NfcStatsLog.write(NfcStatsLog.NFC_TAG_OCCURRED,
+                        NfcStatsLog.NFC_TAG_OCCURRED__TYPE__APP_LAUNCH);
                 return true;
             }
             return false;
@@ -309,24 +312,28 @@
 
         if (tryOverrides(dispatch, tag, message, overrideIntent, overrideFilters,
                 overrideTechLists)) {
-            StatsLog.write(StatsLog.NFC_TAG_OCCURRED, StatsLog.NFC_TAG_OCCURRED__TYPE__APP_LAUNCH);
+            NfcStatsLog.write(
+                    NfcStatsLog.NFC_TAG_OCCURRED, NfcStatsLog.NFC_TAG_OCCURRED__TYPE__APP_LAUNCH);
             return screenUnlocked ? DISPATCH_UNLOCK : DISPATCH_SUCCESS;
         }
 
         if (tryPeripheralHandover(message)) {
             if (DBG) Log.i(TAG, "matched BT HANDOVER");
-            StatsLog.write(StatsLog.NFC_TAG_OCCURRED, StatsLog.NFC_TAG_OCCURRED__TYPE__BT_PAIRING);
+            NfcStatsLog.write(
+                    NfcStatsLog.NFC_TAG_OCCURRED, NfcStatsLog.NFC_TAG_OCCURRED__TYPE__BT_PAIRING);
             return screenUnlocked ? DISPATCH_UNLOCK : DISPATCH_SUCCESS;
         }
 
         if (NfcWifiProtectedSetup.tryNfcWifiSetup(ndef, mContext)) {
             if (DBG) Log.i(TAG, "matched NFC WPS TOKEN");
-            StatsLog.write(StatsLog.NFC_TAG_OCCURRED, StatsLog.NFC_TAG_OCCURRED__TYPE__WIFI_CONNECT);
+            NfcStatsLog.write(
+                    NfcStatsLog.NFC_TAG_OCCURRED, NfcStatsLog.NFC_TAG_OCCURRED__TYPE__WIFI_CONNECT);
             return screenUnlocked ? DISPATCH_UNLOCK : DISPATCH_SUCCESS;
         }
 
         if (provisioningOnly) {
-            StatsLog.write(StatsLog.NFC_TAG_OCCURRED, StatsLog.NFC_TAG_OCCURRED__TYPE__PROVISION);
+            NfcStatsLog.write(
+                    NfcStatsLog.NFC_TAG_OCCURRED, NfcStatsLog.NFC_TAG_OCCURRED__TYPE__PROVISION);
             if (message == null) {
                 // We only allow NDEF-message dispatch in provisioning mode
                 return DISPATCH_FAIL;
@@ -361,7 +368,7 @@
         }
 
         if (DBG) Log.i(TAG, "no match");
-        StatsLog.write(StatsLog.NFC_TAG_OCCURRED, StatsLog.NFC_TAG_OCCURRED__TYPE__OTHERS);
+        NfcStatsLog.write(NfcStatsLog.NFC_TAG_OCCURRED, NfcStatsLog.NFC_TAG_OCCURRED__TYPE__OTHERS);
         return DISPATCH_FAIL;
     }
 
@@ -562,7 +569,8 @@
         if (dispatch.isWebIntent() && dispatch.hasIntentReceiver()) {
             if (DBG) Log.i(TAG, "matched Web link - prompting user");
             showWebLinkConfirmation(dispatch);
-            StatsLog.write(StatsLog.NFC_TAG_OCCURRED, StatsLog.NFC_TAG_OCCURRED__TYPE__URL);
+            NfcStatsLog.write(
+                    NfcStatsLog.NFC_TAG_OCCURRED, NfcStatsLog.NFC_TAG_OCCURRED__TYPE__URL);
             return true;
         }
 
@@ -756,7 +764,7 @@
         }
         AlertDialog.Builder builder = new AlertDialog.Builder(
                 mContext.getApplicationContext(),
-                android.R.style.Theme_DeviceDefault_Light_Dialog_Alert);
+                R.style.DialogAlertDayNight);
         builder.setTitle(R.string.title_confirm_url_open);
         LayoutInflater inflater = LayoutInflater.from(mContext);
         View view = inflater.inflate(R.layout.url_open_confirmation, null);
@@ -784,6 +792,30 @@
         }
     }
 
+    void dumpDebug(ProtoOutputStream proto) {
+        proto.write(NfcDispatcherProto.DEVICE_SUPPORTS_BLUETOOTH, mDeviceSupportsBluetooth);
+        proto.write(NfcDispatcherProto.BLUETOOTH_ENABLED_BY_NFC, mBluetoothEnabledByNfc.get());
+
+        synchronized (this) {
+            proto.write(NfcDispatcherProto.PROVISIONING_ONLY, mProvisioningOnly);
+            if (mOverrideTechLists != null) {
+                StringJoiner techListsJoiner = new StringJoiner(System.lineSeparator());
+                for (String[] list : mOverrideTechLists) {
+                    techListsJoiner.add(Arrays.toString(list));
+                }
+                proto.write(NfcDispatcherProto.OVERRIDE_TECH_LISTS, techListsJoiner.toString());
+            }
+            if (mOverrideIntent != null) {
+                mOverrideIntent.dumpDebug(proto, NfcDispatcherProto.OVERRIDE_INTENT);
+            }
+            if (mOverrideFilters != null) {
+                for (IntentFilter filter : mOverrideFilters) {
+                    filter.dumpDebug(proto, NfcDispatcherProto.OVERRIDE_FILTERS);
+                }
+            }
+        }
+    }
+
     private class MessageHandler extends Handler {
         @Override
         public void handleMessage(Message msg) {
diff --git a/src/com/android/nfc/NfcPermissions.java b/src/com/android/nfc/NfcPermissions.java
index 50adf23..c4528a9 100644
--- a/src/com/android/nfc/NfcPermissions.java
+++ b/src/com/android/nfc/NfcPermissions.java
@@ -18,6 +18,14 @@
     static final String NFC_PERMISSION = android.Manifest.permission.NFC;
     private static final String NFC_PERM_ERROR = "NFC permission required";
 
+    /**
+     * NFC PREFERRED PAYMENT INFO permission
+     */
+    static final String NFC_PREFERRED_PAYMENT_INFO_PERMISSION =
+            android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO;
+    private static final String NFC_PREFERRED_PAYMENT_INFO_PERM_ERROR =
+            "NFC_PREFERRED_PAYMENT_INFO permission required";
+
     public static void validateUserId(int userId) {
         if (userId != UserHandle.getCallingUserId()) {
             throw new SecurityException("userId passed in is not the calling user.");
@@ -32,4 +40,9 @@
     public static void enforceUserPermissions(Context context) {
         context.enforceCallingOrSelfPermission(NFC_PERMISSION, NFC_PERM_ERROR);
     }
+
+    public static void enforcePreferredPaymentInfoPermissions(Context context) {
+        context.enforceCallingOrSelfPermission(NFC_PREFERRED_PAYMENT_INFO_PERMISSION,
+                NFC_PREFERRED_PAYMENT_INFO_PERM_ERROR);
+    }
 }
diff --git a/src/com/android/nfc/NfcService.java b/src/com/android/nfc/NfcService.java
index f1ba3be..2a52842 100644
--- a/src/com/android/nfc/NfcService.java
+++ b/src/com/android/nfc/NfcService.java
@@ -18,11 +18,11 @@
 
 import android.app.ActivityManager;
 import android.app.Application;
-import android.app.backup.BackupManager;
+import android.app.BroadcastOptions;
 import android.app.KeyguardManager;
 import android.app.PendingIntent;
 import android.app.admin.DevicePolicyManager;
-import android.app.BroadcastOptions;
+import android.app.backup.BackupManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -37,7 +37,6 @@
 import android.content.pm.UserInfo;
 import android.content.res.Resources.NotFoundException;
 import android.media.AudioAttributes;
-import android.media.AudioManager;
 import android.media.SoundPool;
 import android.net.Uri;
 import android.nfc.BeamShareData;
@@ -82,6 +81,7 @@
 import android.service.vr.IVrStateCallbacks;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.proto.ProtoOutputStream;
 import android.widget.Toast;
 
 import com.android.internal.logging.MetricsLogger;
@@ -103,16 +103,16 @@
 import java.io.PrintWriter;
 import java.io.UnsupportedEncodingException;
 import java.nio.ByteBuffer;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.Arrays;
+import java.nio.file.Files;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.Scanner;
-
-import android.util.StatsLog;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Collectors;
 
 public class NfcService implements DeviceHostListener {
     static final boolean DBG = false;
@@ -138,7 +138,8 @@
     static final String TRON_NFC_P2P = "nfc_p2p";
     static final String TRON_NFC_TAG = "nfc_tag";
 
-    static final String NATIVE_LOG_FILE_NAME = "native_logs";
+    static final String NATIVE_LOG_FILE_NAME = "native_crash_logs";
+    static final int NATIVE_CRASH_FILE_SIZE = 1024 * 1024;
 
     static final int MSG_NDEF_TAG = 0;
     static final int MSG_LLCP_LINK_ACTIVATION = 1;
@@ -158,6 +159,8 @@
     static final int MSG_UPDATE_STATS = 15;
     static final int MSG_APPLY_SCREEN_STATE = 16;
     static final int MSG_TRANSACTION_EVENT = 17;
+    static final int MSG_PREFERRED_PAYMENT_CHANGED = 18;
+    static final int MSG_TOAST_DEBOUNCE_EVENT = 19;
 
     // Negative value for NO polling delay
     static final int NO_POLL_DELAY = -1;
@@ -166,6 +169,8 @@
     static final long STATS_UPDATE_INTERVAL_MS = 4 * 60 * 60 * 1000;
     static final long MAX_POLLING_PAUSE_TIMEOUT = 40000;
 
+    static final int MAX_TOAST_DEBOUNCE_TIME = 10000;
+
     static final int TASK_ENABLE = 1;
     static final int TASK_DISABLE = 2;
     static final int TASK_BOOT = 3;
@@ -228,6 +233,9 @@
     private static int nci_version = NCI_VERSION_1_0;
     // NFC Execution Environment
     // fields below are protected by this
+    private final boolean mPollingDisableAllowed;
+    private HashMap<Integer, ReaderModeDeathRecipient> mPollingDisableDeathRecipients =
+            new HashMap<Integer, ReaderModeDeathRecipient>();
     private final ReaderModeDeathRecipient mReaderModeDeathRecipient =
             new ReaderModeDeathRecipient();
     private final NfcUnlockManager mNfcUnlockManager;
@@ -237,6 +245,10 @@
     // cached version of installed packages requesting Android.permission.NFC_TRANSACTION_EVENTS
     List<String> mNfcEventInstalledPackages = new ArrayList<String>();
 
+    // cached version of installed packages requesting
+    // Android.permission.NFC_PREFERRED_PAYMENT_INFO
+    List<String> mNfcPreferredPaymentChangedInstalledPackages = new ArrayList<String>();
+
     // fields below are used in multiple threads and protected by synchronized(this)
     final HashMap<Integer, Object> mObjectMap = new HashMap<Integer, Object>();
     int mScreenState;
@@ -312,7 +324,8 @@
     private ForegroundUtils mForegroundUtils;
 
     private static NfcService sService;
-    private static Toast mToast;
+    private static boolean sToast_debounce = false;
+    private static int sToast_debounce_time_ms = 3000;
     public  static boolean sIsDtaMode = false;
 
     private IVrManager vrManager;
@@ -395,7 +408,8 @@
     public void onNfcTransactionEvent(byte[] aid, byte[] data, String seName) {
         byte[][] dataObj = {aid, data, seName.getBytes()};
         sendMessage(NfcService.MSG_TRANSACTION_EVENT, dataObj);
-        StatsLog.write(StatsLog.NFC_CARDEMULATION_OCCURRED, StatsLog.NFC_CARDEMULATION_OCCURRED__CATEGORY__OFFHOST, seName);
+        NfcStatsLog.write(NfcStatsLog.NFC_CARDEMULATION_OCCURRED,
+                NfcStatsLog.NFC_CARDEMULATION_OCCURRED__CATEGORY__OFFHOST, seName);
     }
 
     @Override
@@ -519,6 +533,11 @@
             mIsSecureNfcCapable;
         mDeviceHost.setNfcSecure(mIsSecureNfcEnabled);
 
+        sToast_debounce_time_ms =
+                mContext.getResources().getInteger(R.integer.toast_debounce_time_ms);
+        if(sToast_debounce_time_ms > MAX_TOAST_DEBOUNCE_TIME) {
+            sToast_debounce_time_ms = MAX_TOAST_DEBOUNCE_TIME;
+        }
 
         // Notification message variables
         mDispatchFailedCount = 0;
@@ -535,6 +554,8 @@
         mPollDelay = mContext.getResources().getInteger(R.integer.unknown_tag_polling_delay);
         mNotifyDispatchFailed = mContext.getResources().getBoolean(R.bool.enable_notify_dispatch_failed);
 
+        mPollingDisableAllowed = mContext.getResources().getBoolean(R.bool.polling_disable_allowed);
+
         // Make sure this is only called when object construction is complete.
         ServiceManager.addService(SERVICE_NAME, mNfcAdapter);
 
@@ -596,11 +617,19 @@
         List<PackageInfo> packagesNfcEvents = pm.getPackagesHoldingPermissions(
                 new String[] {android.Manifest.permission.NFC_TRANSACTION_EVENT},
                 PackageManager.GET_ACTIVITIES);
+        List<PackageInfo> packagesNfcPreferredPaymentChanged = pm.getPackagesHoldingPermissions(
+                new String[] {android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO},
+                PackageManager.GET_ACTIVITIES);
         synchronized (this) {
             mNfcEventInstalledPackages.clear();
             for (int i = 0; i < packagesNfcEvents.size(); i++) {
                 mNfcEventInstalledPackages.add(packagesNfcEvents.get(i).packageName);
             }
+            mNfcPreferredPaymentChangedInstalledPackages.clear();
+            for (int i = 0; i < packagesNfcPreferredPaymentChanged.size(); i++) {
+                mNfcPreferredPaymentChangedInstalledPackages.add(
+                        packagesNfcPreferredPaymentChanged.get(i).packageName);
+            }
         }
     }
 
@@ -696,7 +725,8 @@
                 return true;
             }
             Log.i(TAG, "Enabling NFC");
-            StatsLog.write(StatsLog.NFC_STATE_CHANGED, StatsLog.NFC_STATE_CHANGED__STATE__ON);
+            NfcStatsLog.write(
+                    NfcStatsLog.NFC_STATE_CHANGED, NfcStatsLog.NFC_STATE_CHANGED__STATE__ON);
             updateState(NfcAdapter.STATE_TURNING_ON);
 
             WatchDogThread watchDog = new WatchDogThread("enableInternal", INIT_WATCHDOG_MS);
@@ -730,6 +760,8 @@
                     mP2pLinkManager.enableDisable(mIsNdefPushEnabled, true);
                 }
                 updateState(NfcAdapter.STATE_ON);
+
+                onPreferredPaymentChanged(NfcAdapter.PREFERRED_PAYMENT_LOADED);
             }
 
             initSoundPool();
@@ -743,6 +775,8 @@
 
             mDeviceHost.doSetScreenState(screen_state_mask);
 
+            sToast_debounce = false;
+
             /* Start polling loop */
 
             applyRouting(true);
@@ -758,7 +792,8 @@
                 return true;
             }
             Log.i(TAG, "Disabling NFC");
-            StatsLog.write(StatsLog.NFC_STATE_CHANGED, StatsLog.NFC_STATE_CHANGED__STATE__OFF);
+            NfcStatsLog.write(
+                    NfcStatsLog.NFC_STATE_CHANGED, NfcStatsLog.NFC_STATE_CHANGED__STATE__OFF);
             updateState(NfcAdapter.STATE_TURNING_OFF);
 
             /* Sometimes mDeviceHost.deinitialize() hangs, use a watch-dog.
@@ -994,9 +1029,11 @@
                 mCardEmulationManager.onSecureNfcToggled();
             }
             if (enable)
-                StatsLog.write(StatsLog.NFC_STATE_CHANGED, StatsLog.NFC_STATE_CHANGED__STATE__ON_LOCKED);
+                NfcStatsLog.write(NfcStatsLog.NFC_STATE_CHANGED,
+                        NfcStatsLog.NFC_STATE_CHANGED__STATE__ON_LOCKED);
             else
-                StatsLog.write(StatsLog.NFC_STATE_CHANGED, StatsLog.NFC_STATE_CHANGED__STATE__ON);
+                NfcStatsLog.write(
+                        NfcStatsLog.NFC_STATE_CHANGED, NfcStatsLog.NFC_STATE_CHANGED__STATE__ON);
             return true;
         }
 
@@ -1213,39 +1250,68 @@
         public void setReaderMode(IBinder binder, IAppCallback callback, int flags, Bundle extras)
                 throws RemoteException {
             int callingUid = Binder.getCallingUid();
-            if (callingUid != Process.SYSTEM_UID && !mForegroundUtils.isInForeground(callingUid)) {
+            int callingPid = Binder.getCallingPid();
+            // Allow non-foreground callers with system uid or systemui
+            boolean privilegedCaller = (callingUid == Process.SYSTEM_UID
+                    || getPackageNameFromUid(callingUid).equals("com.android.systemui"));
+            if (!privilegedCaller && !mForegroundUtils.isInForeground(callingUid)) {
                 Log.e(TAG, "setReaderMode: Caller is not in foreground and is not system process.");
                 return;
             }
+            boolean disablePolling = flags != 0 && getReaderModeTechMask(flags) == 0;
+            // Only allow to disable polling for specific callers
+            if (disablePolling && !(privilegedCaller && mPollingDisableAllowed)) {
+                Log.e(TAG, "setReaderMode() called with invalid flag parameter.");
+                return;
+            }
             synchronized (NfcService.this) {
-                if (!isNfcEnabled()) {
+                if (!isNfcEnabled() && !privilegedCaller) {
                     Log.e(TAG, "setReaderMode() called while NFC is not enabled.");
                     return;
                 }
                 if (flags != 0) {
                     try {
-                        mReaderModeParams = new ReaderModeParams();
-                        mReaderModeParams.callback = callback;
-                        mReaderModeParams.flags = flags;
-                        mReaderModeParams.presenceCheckDelay = extras != null
-                                ? (extras.getInt(NfcAdapter.EXTRA_READER_PRESENCE_CHECK_DELAY,
-                                        DEFAULT_PRESENCE_CHECK_DELAY))
-                                : DEFAULT_PRESENCE_CHECK_DELAY;
-                        binder.linkToDeath(mReaderModeDeathRecipient, 0);
+                        if (disablePolling) {
+                            ReaderModeDeathRecipient pollingDisableDeathRecipient =
+                                    new ReaderModeDeathRecipient();
+                            binder.linkToDeath(pollingDisableDeathRecipient, 0);
+                            mPollingDisableDeathRecipients.put(
+                                    callingPid, pollingDisableDeathRecipient);
+                        } else {
+                            if (mPollingDisableDeathRecipients.size() != 0) {
+                                Log.e(TAG, "active polling is forced to disable now.");
+                                return;
+                            }
+                            binder.linkToDeath(mReaderModeDeathRecipient, 0);
+                        }
+                        updateReaderModeParams(callback, flags, extras);
                     } catch (RemoteException e) {
                         Log.e(TAG, "Remote binder has already died.");
                         return;
                     }
                 } else {
                     try {
-                        mReaderModeParams = null;
-                        StopPresenceChecking();
-                        binder.unlinkToDeath(mReaderModeDeathRecipient, 0);
+                        ReaderModeDeathRecipient pollingDisableDeathRecipient =
+                                mPollingDisableDeathRecipients.get(callingPid);
+                        mPollingDisableDeathRecipients.remove(callingPid);
+
+                        if (mPollingDisableDeathRecipients.size() == 0) {
+                            mReaderModeParams = null;
+                            StopPresenceChecking();
+                        }
+
+                        if (pollingDisableDeathRecipient != null) {
+                            binder.unlinkToDeath(pollingDisableDeathRecipient, 0);
+                        } else {
+                            binder.unlinkToDeath(mReaderModeDeathRecipient, 0);
+                        }
                     } catch (NoSuchElementException e) {
                         Log.e(TAG, "Reader mode Binder was never registered.");
                     }
                 }
-                applyRouting(false);
+                if (isNfcEnabled()) {
+                    applyRouting(false);
+                }
             }
         }
 
@@ -1316,6 +1382,50 @@
 
             return mask;
         }
+
+        private int getReaderModeTechMask(int flags) {
+            int techMask = 0;
+            if ((flags & NfcAdapter.FLAG_READER_NFC_A) != 0) {
+                techMask |= NFC_POLL_A;
+            }
+            if ((flags & NfcAdapter.FLAG_READER_NFC_B) != 0) {
+                techMask |= NFC_POLL_B;
+            }
+            if ((flags & NfcAdapter.FLAG_READER_NFC_F) != 0) {
+                techMask |= NFC_POLL_F;
+            }
+            if ((flags & NfcAdapter.FLAG_READER_NFC_V) != 0) {
+                techMask |= NFC_POLL_V;
+            }
+            if ((flags & NfcAdapter.FLAG_READER_NFC_BARCODE) != 0) {
+                techMask |= NFC_POLL_KOVIO;
+            }
+
+            return techMask;
+        }
+
+        private String getPackageNameFromUid(int uid) {
+            PackageManager packageManager = mContext.getPackageManager();
+            if (packageManager != null) {
+                String[] packageName = packageManager.getPackagesForUid(uid);
+                if (packageName != null && packageName.length > 0) {
+                    return packageName[0];
+                }
+            }
+            return null;
+        }
+
+        private void updateReaderModeParams(IAppCallback callback, int flags, Bundle extras) {
+            synchronized (NfcService.this) {
+                mReaderModeParams = new ReaderModeParams();
+                mReaderModeParams.callback = callback;
+                mReaderModeParams.flags = flags;
+                mReaderModeParams.presenceCheckDelay = extras != null
+                        ? (extras.getInt(NfcAdapter.EXTRA_READER_PRESENCE_CHECK_DELAY,
+                                DEFAULT_PRESENCE_CHECK_DELAY))
+                        : DEFAULT_PRESENCE_CHECK_DELAY;
+            }
+        }
     }
 
     final class ReaderModeDeathRecipient implements IBinder.DeathRecipient {
@@ -1323,8 +1433,11 @@
         public void binderDied() {
             synchronized (NfcService.this) {
                 if (mReaderModeParams != null) {
-                    mReaderModeParams = null;
-                    applyRouting(false);
+                    mPollingDisableDeathRecipients.values().remove(this);
+                    if (mPollingDisableDeathRecipients.size() == 0) {
+                        mReaderModeParams = null;
+                        applyRouting(false);
+                    }
                 }
             }
         }
@@ -1776,7 +1889,8 @@
                 mRoutingWakeLock.release();
             }
             Log.e(TAG, "Watchdog triggered, aborting.");
-            StatsLog.write(StatsLog.NFC_STATE_CHANGED, StatsLog.NFC_STATE_CHANGED__STATE__CRASH_RESTART);
+            NfcStatsLog.write(NfcStatsLog.NFC_STATE_CHANGED,
+                    NfcStatsLog.NFC_STATE_CHANGED__STATE__CRASH_RESTART);
             storeNativeCrashLogs();
             mDeviceHost.doAbort(getName());
         }
@@ -1873,6 +1987,9 @@
 
                 paramsBuilder.setTechMask(techMask);
                 paramsBuilder.setEnableReaderMode(true);
+                if (mReaderModeParams.flags != 0 && techMask == 0) {
+                    paramsBuilder.setEnableHostRouting(true);
+                }
             } else {
                 paramsBuilder.setTechMask(NfcDiscoveryParameters.NFC_POLL_DEFAULT);
                 paramsBuilder.setEnableP2p(mIsBeamCapable);
@@ -2082,6 +2199,10 @@
         return mDeviceHost.sendRawFrame(data);
     }
 
+    public void onPreferredPaymentChanged(int reason) {
+        sendMessage(MSG_PREFERRED_PAYMENT_CHANGED, reason);
+    }
+
     void sendMessage(int what, Object obj) {
         Message msg = mHandler.obtainMessage();
         msg.what = what;
@@ -2224,12 +2345,13 @@
                         if (!tag.reconnect()) {
                             tag.disconnect();
                             if (mScreenState == ScreenStateHelper.SCREEN_STATE_ON_UNLOCKED) {
-                                if (mToast != null) {
-                                    if (mToast.getView().isShown()) mToast.cancel();
+                                if (!sToast_debounce) {
+                                    Toast.makeText(mContext, R.string.tag_read_error,
+                                                   Toast.LENGTH_SHORT).show();
+                                    sToast_debounce = true;
+                                    mHandler.sendEmptyMessageDelayed(MSG_TOAST_DEBOUNCE_EVENT,
+                                                                     sToast_debounce_time_ms);
                                 }
-                                mToast = Toast.makeText(mContext, R.string.tag_read_error,
-                                                        Toast.LENGTH_SHORT);
-                                mToast.show();
                             }
                             break;
                         }
@@ -2381,6 +2503,18 @@
                     sendOffHostTransactionEvent(data[0], data[1], data[2]);
                     break;
 
+                case MSG_PREFERRED_PAYMENT_CHANGED:
+                    Intent preferredPaymentChangedIntent =
+                            new Intent(NfcAdapter.ACTION_PREFERRED_PAYMENT_CHANGED);
+                    preferredPaymentChangedIntent.putExtra(
+                            NfcAdapter.EXTRA_PREFERRED_PAYMENT_CHANGED_REASON, (int)msg.obj);
+                    sendPreferredPaymentChangedEvent(preferredPaymentChangedIntent);
+                    break;
+
+                case MSG_TOAST_DEBOUNCE_EVENT:
+                    sToast_debounce = false;
+                    break;
+
                 default:
                     Log.e(TAG, "Unknown message received");
                     break;
@@ -2510,6 +2644,94 @@
             }
         }
 
+        /* Returns the list of packages request for nfc preferred payment service changed and
+         * have access to NFC Events on any SE */
+        private ArrayList<String> getNfcPreferredPaymentChangedSEAccessAllowedPackages() {
+            if (!isSEServiceAvailable() || mNfcPreferredPaymentChangedInstalledPackages.isEmpty()) {
+                return null;
+            }
+            String[] readers = null;
+            try {
+                readers = mSEService.getReaders();
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error in getReaders() " + e);
+                return null;
+            }
+
+            if (readers == null || readers.length == 0) {
+                return null;
+            }
+            boolean[] nfcAccessFinal = null;
+            String[] installedPackages =
+                    new String[mNfcPreferredPaymentChangedInstalledPackages.size()];
+            for (String reader : readers) {
+                try {
+                    boolean[] accessList = mSEService.isNFCEventAllowed(reader, null,
+                            mNfcPreferredPaymentChangedInstalledPackages.toArray(installedPackages)
+                            );
+                    if (accessList == null) {
+                        continue;
+                    }
+                    if (nfcAccessFinal == null) {
+                        nfcAccessFinal = accessList;
+                    }
+                    for (int i = 0; i < accessList.length; i++) {
+                        if (accessList[i]) {
+                            nfcAccessFinal[i] = true;
+                        }
+                    }
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Error in isNFCEventAllowed() " + e);
+                }
+            }
+            if (nfcAccessFinal == null) {
+                return null;
+            }
+            ArrayList<String> packages = new ArrayList<String>();
+            for (int i = 0; i < nfcAccessFinal.length; i++) {
+                if (nfcAccessFinal[i]) {
+                    packages.add(mNfcPreferredPaymentChangedInstalledPackages.get(i));
+                }
+            }
+            return packages;
+        }
+
+        private void sendPreferredPaymentChangedEvent(Intent intent) {
+            intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
+            // Resume app switches so the receivers can start activities without delay
+            mNfcDispatcher.resumeAppSwitches();
+            synchronized (this) {
+                ArrayList<String> SEPackages =
+                        getNfcPreferredPaymentChangedSEAccessAllowedPackages();
+                if (SEPackages!= null && !SEPackages.isEmpty()) {
+                    for (String packageName : SEPackages) {
+                        intent.setPackage(packageName);
+                        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+                        mContext.sendBroadcast(intent);
+                    }
+                }
+                PackageManager pm = mContext.getPackageManager();
+                for (String packageName : mNfcPreferredPaymentChangedInstalledPackages) {
+                    try {
+                        PackageInfo info = pm.getPackageInfo(packageName, 0);
+                        if (SEPackages != null && SEPackages.contains(packageName)) {
+                            continue;
+                        }
+                        if (info.applicationInfo != null &&
+                                ((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0 ||
+                                (info.applicationInfo.privateFlags &
+                                ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0)) {
+                            intent.setPackage(packageName);
+                            intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+                            mContext.sendBroadcast(intent);
+                        }
+                    } catch (Exception e) {
+                        Log.e(TAG, "Exception in getPackageInfo " + e);
+                    }
+                }
+            }
+        }
+
         private boolean llcpActivated(NfcDepEndpoint device) {
             Log.d(TAG, "LLCP Activation message");
 
@@ -2601,21 +2823,18 @@
                     } else {
                         Log.e(TAG, "Keep presence checking.");
                     }
-                    if (mScreenState == ScreenStateHelper.SCREEN_STATE_ON_UNLOCKED &&
-                        mContext.getResources().getBoolean(R.bool.enable_notify_dispatch_failed)) {
-                        if (mToast != null) {
-                            if (mToast.getView().isShown()) mToast.cancel();
+                    if (mScreenState == ScreenStateHelper.SCREEN_STATE_ON_UNLOCKED && mNotifyDispatchFailed) {
+                        if (!sToast_debounce) {
+                            Toast.makeText(mContext, R.string.tag_dispatch_failed,
+                                           Toast.LENGTH_SHORT).show();
+                            sToast_debounce = true;
+                            mHandler.sendEmptyMessageDelayed(MSG_TOAST_DEBOUNCE_EVENT,
+                                                             sToast_debounce_time_ms);
                         }
-                        mToast = Toast.makeText(mContext, R.string.tag_dispatch_failed,
-                                                Toast.LENGTH_SHORT);
-                        mToast.show();
                         playSound(SOUND_ERROR);
                     }
                     if (!mAntennaBlockedMessageShown && mDispatchFailedCount++ > mDispatchFailedMax) {
-                        Intent dialogIntent = new Intent(mContext, NfcBlockedNotification.class);
-                        dialogIntent.setFlags(
-                            Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
-                        mContext.startActivity(dialogIntent);
+                        new NfcBlockedNotification(mContext).startNotification();
                         mPrefsEditor.putBoolean(PREF_ANTENNA_BLOCKED_MESSAGE_SHOWN, true);
                         mPrefsEditor.apply();
                         mBackupManager.dataChanged();
@@ -2686,6 +2905,7 @@
                 sendMessage(NfcService.MSG_APPLY_SCREEN_STATE, screenState);
             } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
                 int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
+                mUserId = userId;
                 if (mIsBeamCapable) {
                     int beamSetting =
                         PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
@@ -2700,7 +2920,6 @@
                         Log.e(TAG, "Error int getComponentEnabledSetting for BeamShareActivity");
                     }
                     synchronized (this) {
-                        mUserId = userId;
                         if (beamSetting == PackageManager.COMPONENT_ENABLED_STATE_DISABLED) {
                            mIsNdefPushEnabled = false;
                         } else {
@@ -2785,9 +3004,28 @@
         }
     }
 
+    static int stateToProtoEnum(int state) {
+        switch (state) {
+            case NfcAdapter.STATE_OFF:
+                return NfcServiceDumpProto.STATE_OFF;
+            case NfcAdapter.STATE_TURNING_ON:
+                return NfcServiceDumpProto.STATE_TURNING_ON;
+            case NfcAdapter.STATE_ON:
+                return NfcServiceDumpProto.STATE_ON;
+            case NfcAdapter.STATE_TURNING_OFF:
+                return NfcServiceDumpProto.STATE_TURNING_OFF;
+            default:
+                return NfcServiceDumpProto.STATE_UNKNOWN;
+        }
+    }
+
+    public String getNfaStorageDir() {
+        return mDeviceHost.getNfaStorageDir();
+    }
+
     private void copyNativeCrashLogsIfAny(PrintWriter pw) {
       try {
-          File file = new File(mContext.getFilesDir(), NATIVE_LOG_FILE_NAME);
+          File file = new File(getNfaStorageDir(), NATIVE_LOG_FILE_NAME);
           if (!file.exists()) {
             return;
           }
@@ -2806,12 +3044,12 @@
 
     private void storeNativeCrashLogs() {
       try {
-          File file = new File(mContext.getFilesDir(), NATIVE_LOG_FILE_NAME);
-          if (!file.exists()) {
+          File file = new File(getNfaStorageDir(), NATIVE_LOG_FILE_NAME);
+          if (file.length() >= NATIVE_CRASH_FILE_SIZE) {
               file.createNewFile();
           }
 
-          FileOutputStream fos = new FileOutputStream(file);
+          FileOutputStream fos = new FileOutputStream(file, true);
           mDeviceHost.dump(fos.getFD());
           fos.flush();
           fos.close();
@@ -2829,13 +3067,26 @@
             return;
         }
 
+        for (String arg : args) {
+            if ("--proto".equals(arg)) {
+                ProtoOutputStream proto = new ProtoOutputStream(new FileOutputStream(fd));
+                synchronized (this) {
+                    dumpDebug(proto);
+                }
+                proto.flush();
+                return;
+            }
+        }
+
         synchronized (this) {
             pw.println("mState=" + stateToString(mState));
             pw.println("mIsZeroClickRequested=" + mIsNdefPushEnabled);
             pw.println("mScreenState=" + ScreenStateHelper.screenStateToString(mScreenState));
+            pw.println("mIsSecureNfcEnabled=" + mIsSecureNfcEnabled);
             pw.println(mCurrentDiscoveryParameters);
-            if (mIsBeamCapable)
+            if (mIsBeamCapable) {
                 mP2pLinkManager.dump(fd, pw, args);
+            }
             if (mIsHceCapable) {
                 mCardEmulationManager.dump(fd, pw, args);
             }
@@ -2845,4 +3096,63 @@
             mDeviceHost.dump(fd);
         }
     }
+
+    /**
+     * Dump debugging information as a NfcServiceDumpProto
+     *
+     * Note:
+     * See proto definition in frameworks/base/core/proto/android/nfc/nfc_service.proto
+     * When writing a nested message, must call {@link ProtoOutputStream#start(long)} before and
+     * {@link ProtoOutputStream#end(long)} after.
+     * Never reuse a proto field number. When removing a field, mark it as reserved.
+     */
+    private void dumpDebug(ProtoOutputStream proto) {
+        proto.write(NfcServiceDumpProto.STATE, stateToProtoEnum(mState));
+        proto.write(NfcServiceDumpProto.IN_PROVISION_MODE, mInProvisionMode);
+        proto.write(NfcServiceDumpProto.NDEF_PUSH_ENABLED, mIsNdefPushEnabled);
+        proto.write(NfcServiceDumpProto.SCREEN_STATE,
+                ScreenStateHelper.screenStateToProtoEnum(mScreenState));
+        proto.write(NfcServiceDumpProto.SECURE_NFC_ENABLED, mIsSecureNfcEnabled);
+        proto.write(NfcServiceDumpProto.POLLING_PAUSED, mPollingPaused);
+        proto.write(NfcServiceDumpProto.NUM_TAGS_DETECTED, mNumTagsDetected.get());
+        proto.write(NfcServiceDumpProto.NUM_P2P_DETECTED, mNumP2pDetected.get());
+        proto.write(NfcServiceDumpProto.NUM_HCE_DETECTED, mNumHceDetected.get());
+        proto.write(NfcServiceDumpProto.HCE_CAPABLE, mIsHceCapable);
+        proto.write(NfcServiceDumpProto.HCE_F_CAPABLE, mIsHceFCapable);
+        proto.write(NfcServiceDumpProto.BEAM_CAPABLE, mIsBeamCapable);
+        proto.write(NfcServiceDumpProto.SECURE_NFC_CAPABLE, mIsSecureNfcCapable);
+        proto.write(NfcServiceDumpProto.VR_MODE_ENABLED, mIsVrModeEnabled);
+
+        long token = proto.start(NfcServiceDumpProto.DISCOVERY_PARAMS);
+        mCurrentDiscoveryParameters.dumpDebug(proto);
+        proto.end(token);
+
+        if (mIsBeamCapable) {
+            token = proto.start(NfcServiceDumpProto.P2P_LINK_MANAGER);
+            mP2pLinkManager.dumpDebug(proto);
+            proto.end(token);
+        }
+
+        if (mIsHceCapable) {
+            token = proto.start(NfcServiceDumpProto.CARD_EMULATION_MANAGER);
+            mCardEmulationManager.dumpDebug(proto);
+            proto.end(token);
+        }
+
+        token = proto.start(NfcServiceDumpProto.NFC_DISPATCHER);
+        mNfcDispatcher.dumpDebug(proto);
+        proto.end(token);
+
+        // Dump native crash logs if any
+        File file = new File(getNfaStorageDir(), NATIVE_LOG_FILE_NAME);
+        if (!file.exists()) {
+            return;
+        }
+        try {
+            String logs = Files.lines(file.toPath()).collect(Collectors.joining("\n"));
+            proto.write(NfcServiceDumpProto.NATIVE_CRASH_LOGS, logs);
+        } catch (IOException e) {
+            Log.e(TAG, "IOException in dumpDebug(ProtoOutputStream): " + e);
+        }
+    }
 }
diff --git a/src/com/android/nfc/P2pLinkManager.java b/src/com/android/nfc/P2pLinkManager.java
index ae818a3..89ac067 100755
--- a/src/com/android/nfc/P2pLinkManager.java
+++ b/src/com/android/nfc/P2pLinkManager.java
@@ -61,7 +61,8 @@
 import java.util.List;
 import java.io.UnsupportedEncodingException;
 
-import android.util.StatsLog;
+import android.util.proto.ProtoOutputStream;
+
 /**
  * Interface to listen for P2P events.
  * All callbacks are made from the UI thread.
@@ -1108,7 +1109,8 @@
                     mSendState = SEND_STATE_NOTHING_TO_SEND;
                     if (DBG) Log.d(TAG, "onP2pReceiveComplete()");
                     mEventListener.onP2pReceiveComplete(false);
-                    StatsLog.write(StatsLog.NFC_BEAM_OCCURRED, StatsLog.NFC_BEAM_OCCURRED__OPERATION__RECEIVE);
+                    NfcStatsLog.write(NfcStatsLog.NFC_BEAM_OCCURRED,
+                            NfcStatsLog.NFC_BEAM_OCCURRED__OPERATION__RECEIVE);
                 }
                 break;
             case MSG_RECEIVE_COMPLETE:
@@ -1124,7 +1126,8 @@
                     if (DBG) Log.d(TAG, "onP2pReceiveComplete()");
                     mEventListener.onP2pReceiveComplete(true);
                     NfcService.getInstance().sendMockNdefTag(m);
-                    StatsLog.write(StatsLog.NFC_BEAM_OCCURRED, StatsLog.NFC_BEAM_OCCURRED__OPERATION__RECEIVE);
+                    NfcStatsLog.write(NfcStatsLog.NFC_BEAM_OCCURRED,
+                            NfcStatsLog.NFC_BEAM_OCCURRED__OPERATION__RECEIVE);
                 }
                 break;
             case MSG_HANDOVER_NOT_SUPPORTED:
@@ -1157,7 +1160,8 @@
                             Log.e(TAG, "Failed NDEF completed callback: " + e.getMessage());
                         }
                     }
-                    StatsLog.write(StatsLog.NFC_BEAM_OCCURRED, StatsLog.NFC_BEAM_OCCURRED__OPERATION__SEND);
+                    NfcStatsLog.write(NfcStatsLog.NFC_BEAM_OCCURRED,
+                            NfcStatsLog.NFC_BEAM_OCCURRED__OPERATION__SEND);
                 }
                 break;
             case MSG_HANDOVER_BUSY:
@@ -1242,6 +1246,23 @@
         }
     }
 
+    static int sendStateToProtoEnum(int state) {
+        switch (state) {
+            case SEND_STATE_NOTHING_TO_SEND:
+                return P2pLinkManagerProto.SEND_STATE_NOTHING_TO_SEND;
+            case SEND_STATE_NEED_CONFIRMATION:
+                return P2pLinkManagerProto.SEND_STATE_NEED_CONFIRMATION;
+            case SEND_STATE_SENDING:
+                return P2pLinkManagerProto.SEND_STATE_SENDING;
+            case SEND_STATE_COMPLETE:
+                return P2pLinkManagerProto.SEND_STATE_COMPLETE;
+            case SEND_STATE_CANCELED:
+                return P2pLinkManagerProto.SEND_STATE_CANCELED;
+            default:
+                return P2pLinkManagerProto.SEND_STATE_UNKNOWN;
+        }
+    }
+
     static String linkStateToString(int state) {
         switch (state) {
             case LINK_STATE_DOWN:
@@ -1255,6 +1276,19 @@
         }
     }
 
+    static int linkStateToProtoEnum(int state) {
+        switch (state) {
+            case LINK_STATE_DOWN:
+                return P2pLinkManagerProto.LINK_STATE_DOWN;
+            case LINK_STATE_DEBOUNCE:
+                return P2pLinkManagerProto.LINK_STATE_DEBOUNCE;
+            case LINK_STATE_UP:
+                return P2pLinkManagerProto.LINK_STATE_UP;
+            default:
+                return P2pLinkManagerProto.LINK_STATE_UNKNOWN;
+        }
+    }
+
     void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         synchronized (this) {
             pw.println("mIsSendEnabled=" + mIsSendEnabled);
@@ -1267,4 +1301,34 @@
             pw.println("mUrisToSend=" + mUrisToSend);
         }
     }
+
+    /**
+     * Dump debugging information as a P2pLinkManagerProto
+     *
+     * Note:
+     * See proto definition in frameworks/base/core/proto/android/nfc/nfc_service.proto
+     * When writing a nested message, must call {@link ProtoOutputStream#start(long)} before and
+     * {@link ProtoOutputStream#end(long)} after.
+     * Never reuse a proto field number. When removing a field, mark it as reserved.
+     */
+    void dumpDebug(ProtoOutputStream proto) {
+        proto.write(P2pLinkManagerProto.DEFAULT_MIU, mDefaultMiu);
+        proto.write(P2pLinkManagerProto.DEFAULT_RW_SIZE, mDefaultRwSize);
+        proto.write(P2pLinkManagerProto.LINK_STATE, linkStateToProtoEnum(mLinkState));
+        proto.write(P2pLinkManagerProto.SEND_STATE, sendStateToProtoEnum(mSendState));
+        proto.write(P2pLinkManagerProto.SEND_FLAGS, mSendFlags);
+        proto.write(P2pLinkManagerProto.SEND_ENABLED, mIsSendEnabled);
+        proto.write(P2pLinkManagerProto.RECEIVE_ENABLED, mIsReceiveEnabled);
+        proto.write(P2pLinkManagerProto.CALLBACK_NDEF, String.valueOf(mCallbackNdef));
+        if (mMessageToSend != null) {
+            long token = proto.start(P2pLinkManagerProto.MESSAGE_TO_SEND);
+            mMessageToSend.dumpDebug(proto);
+            proto.end(token);
+        }
+        if (mUrisToSend != null && mUrisToSend.length > 0) {
+            for (Uri uri : mUrisToSend) {
+                proto.write(P2pLinkManagerProto.URIS_TO_SEND, uri.toString());
+            }
+        }
+    }
 }
diff --git a/src/com/android/nfc/ScreenStateHelper.java b/src/com/android/nfc/ScreenStateHelper.java
index 92ba168..22ff13d 100644
--- a/src/com/android/nfc/ScreenStateHelper.java
+++ b/src/com/android/nfc/ScreenStateHelper.java
@@ -61,4 +61,19 @@
                 return "UNKNOWN";
         }
     }
+
+    static int screenStateToProtoEnum(int screenState) {
+        switch (screenState) {
+            case SCREEN_STATE_OFF_LOCKED:
+                return NfcServiceDumpProto.SCREEN_STATE_OFF_LOCKED;
+            case SCREEN_STATE_ON_LOCKED:
+                return NfcServiceDumpProto.SCREEN_STATE_ON_LOCKED;
+            case SCREEN_STATE_ON_UNLOCKED:
+                return NfcServiceDumpProto.SCREEN_STATE_ON_UNLOCKED;
+            case SCREEN_STATE_OFF_UNLOCKED:
+                return NfcServiceDumpProto.SCREEN_STATE_OFF_UNLOCKED;
+            default:
+                return NfcServiceDumpProto.SCREEN_STATE_UNKNOWN;
+        }
+    }
 }
diff --git a/src/com/android/nfc/beam/SendUi.java b/src/com/android/nfc/beam/SendUi.java
index ab35467..49f893c 100644
--- a/src/com/android/nfc/beam/SendUi.java
+++ b/src/com/android/nfc/beam/SendUi.java
@@ -241,7 +241,7 @@
                 | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
                 PixelFormat.OPAQUE);
         mWindowLayoutParams.privateFlags |=
-                WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+                WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
         mWindowLayoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
         mWindowLayoutParams.token = new Binder();
 
@@ -542,7 +542,7 @@
         mContext.unregisterReceiver(mReceiver);
         if (mToastString != null) {
             Toast toast = Toast.makeText(mContext, mToastString, Toast.LENGTH_LONG);
-            toast.getWindowParams().privateFlags |= LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+            toast.getWindowParams().privateFlags |= LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
             toast.show();
         }
         mToastString = null;
diff --git a/src/com/android/nfc/cardemulation/AidRoutingManager.java b/src/com/android/nfc/cardemulation/AidRoutingManager.java
index d5c81e3..b1e6713 100644
--- a/src/com/android/nfc/cardemulation/AidRoutingManager.java
+++ b/src/com/android/nfc/cardemulation/AidRoutingManager.java
@@ -17,20 +17,19 @@
 
 import android.util.Log;
 import android.util.SparseArray;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.nfc.NfcService;
-
+import com.android.nfc.NfcStatsLog;
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.util.Arrays;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
 
-import android.util.StatsLog;
-
 public class AidRoutingManager {
 
     static final String TAG = "AidRoutingManager";
@@ -201,7 +200,10 @@
         boolean aidRouteResolved = false;
         HashMap<String, AidEntry> aidRoutingTableCache = new HashMap<String, AidEntry>(aidMap.size());
         ArrayList<Integer> seList = new ArrayList<Integer>();
-        seList.add(ROUTE_HOST);
+        seList.add(mDefaultRoute);
+        if (mDefaultRoute != ROUTE_HOST) {
+            seList.add(ROUTE_HOST);
+        }
 
         SparseArray<Set<String>> aidRoutingTable = new SparseArray<Set<String>>(aidMap.size());
         HashMap<String, Integer> routeForAid = new HashMap<String, Integer>(aidMap.size());
@@ -357,7 +359,8 @@
           if(aidRouteResolved == true) {
               commit(aidRoutingTableCache);
           } else {
-              StatsLog.write(StatsLog.NFC_ERROR_OCCURRED, StatsLog.NFC_ERROR_OCCURRED__TYPE__AID_OVERFLOW, 0, 0);
+              NfcStatsLog.write(NfcStatsLog.NFC_ERROR_OCCURRED,
+                      NfcStatsLog.NFC_ERROR_OCCURRED__TYPE__AID_OVERFLOW, 0, 0);
               Log.e(TAG, "RoutingTable unchanged because it's full, not updating");
           }
         }
@@ -408,4 +411,27 @@
             }
         }
     }
+
+    /**
+     * Dump debugging information as a AidRoutingManagerProto
+     *
+     * Note:
+     * See proto definition in frameworks/base/core/proto/android/nfc/card_emulation.proto
+     * When writing a nested message, must call {@link ProtoOutputStream#start(long)} before and
+     * {@link ProtoOutputStream#end(long)} after.
+     * Never reuse a proto field number. When removing a field, mark it as reserved.
+     */
+    void dumpDebug(ProtoOutputStream proto) {
+        proto.write(AidRoutingManagerProto.DEFAULT_ROUTE, mDefaultRoute);
+        synchronized (mLock) {
+            for (int i = 0; i < mAidRoutingTable.size(); i++) {
+                long token = proto.start(AidRoutingManagerProto.ROUTES);
+                proto.write(AidRoutingManagerProto.Route.ID, mAidRoutingTable.keyAt(i));
+                mAidRoutingTable.valueAt(i).forEach(aid -> {
+                    proto.write(AidRoutingManagerProto.Route.AIDS, aid);
+                });
+                proto.end(token);
+            }
+        }
+    }
 }
diff --git a/src/com/android/nfc/cardemulation/AppChooserActivity.java b/src/com/android/nfc/cardemulation/AppChooserActivity.java
index 5ed96c0..b5fb515 100644
--- a/src/com/android/nfc/cardemulation/AppChooserActivity.java
+++ b/src/com/android/nfc/cardemulation/AppChooserActivity.java
@@ -80,7 +80,7 @@
     protected void onCreate(Bundle savedInstanceState, String category,
             ArrayList<ApduServiceInfo> options, ComponentName failedComponent) {
         super.onCreate(savedInstanceState);
-        setTheme(R.style.Theme_DeviceDefault_Light_Dialog_Alert);
+        setTheme(com.android.nfc.R.style.DialogAlertDayNight);
 
         IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
         registerReceiver(mReceiver, filter);
diff --git a/src/com/android/nfc/cardemulation/CardEmulationManager.java b/src/com/android/nfc/cardemulation/CardEmulationManager.java
index 7f0ed55..489196e 100644
--- a/src/com/android/nfc/cardemulation/CardEmulationManager.java
+++ b/src/com/android/nfc/cardemulation/CardEmulationManager.java
@@ -19,11 +19,13 @@
 import java.io.PrintWriter;
 import java.util.List;
 
+import android.app.ActivityManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.nfc.INfcCardEmulation;
 import android.nfc.INfcFCardEmulation;
+import android.nfc.NfcAdapter;
 import android.nfc.cardemulation.AidGroup;
 import android.nfc.cardemulation.ApduServiceInfo;
 import android.nfc.cardemulation.NfcFServiceInfo;
@@ -36,6 +38,7 @@
 import android.os.SystemClock;
 import android.provider.Settings;
 import android.util.Log;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.nfc.NfcPermissions;
 import com.android.nfc.NfcService;
@@ -191,6 +194,49 @@
         mHostNfcFEmulationManager.dump(fd, pw, args);
     }
 
+    /**
+     * Dump debugging information as a CardEmulationManagerProto
+     *
+     * Note:
+     * See proto definition in frameworks/base/core/proto/android/nfc/card_emulation.proto
+     * When writing a nested message, must call {@link ProtoOutputStream#start(long)} before and
+     * {@link ProtoOutputStream#end(long)} after.
+     * Never reuse a proto field number. When removing a field, mark it as reserved.
+     */
+    public void dumpDebug(ProtoOutputStream proto) {
+        long token = proto.start(CardEmulationManagerProto.REGISTERED_SERVICES_CACHE);
+        mServiceCache.dumpDebug(proto);
+        proto.end(token);
+
+        token = proto.start(CardEmulationManagerProto.REGISTERED_NFC_F_SERVICES_CACHE);
+        mNfcFServicesCache.dumpDebug(proto);
+        proto.end(token);
+
+        token = proto.start(CardEmulationManagerProto.PREFERRED_SERVICES);
+        mPreferredServices.dumpDebug(proto);
+        proto.end(token);
+
+        token = proto.start(CardEmulationManagerProto.ENABLED_NFC_F_SERVICES);
+        mEnabledNfcFServices.dumpDebug(proto);
+        proto.end(token);
+
+        token = proto.start(CardEmulationManagerProto.AID_CACHE);
+        mAidCache.dumpDebug(proto);
+        proto.end(token);
+
+        token = proto.start(CardEmulationManagerProto.T3T_IDENTIFIERS_CACHE);
+        mT3tIdentifiersCache.dumpDebug(proto);
+        proto.end(token);
+
+        token = proto.start(CardEmulationManagerProto.HOST_EMULATION_MANAGER);
+        mHostEmulationManager.dumpDebug(proto);
+        proto.end(token);
+
+        token = proto.start(CardEmulationManagerProto.HOST_NFC_F_EMULATION_MANAGER);
+        mHostNfcFEmulationManager.dumpDebug(proto);
+        proto.end(token);
+    }
+
     @Override
     public void onServicesUpdated(int userId, List<ApduServiceInfo> services) {
         // Verify defaults are still sane
@@ -199,6 +245,8 @@
         mAidCache.onServicesUpdated(userId, services);
         // Update the preferred services list
         mPreferredServices.onServicesUpdated();
+
+        NfcService.getInstance().onPreferredPaymentChanged(NfcAdapter.PREFERRED_PAYMENT_UPDATED);
     }
 
     @Override
@@ -211,7 +259,7 @@
 
     void verifyDefaults(int userId, List<ApduServiceInfo> services) {
         ComponentName defaultPaymentService =
-                getDefaultServiceForCategory(userId, CardEmulation.CATEGORY_PAYMENT, false);
+                getDefaultServiceForCategory(userId, CardEmulation.CATEGORY_PAYMENT, true);
         if (DBG) Log.d(TAG, "Current default: " + defaultPaymentService);
         if (defaultPaymentService == null) {
             // A payment service may have been removed, leaving only one;
@@ -376,8 +424,13 @@
             if (!isServiceRegistered(userId, service)) {
                 return false;
             }
-            return mServiceCache.registerAidGroupForService(userId, Binder.getCallingUid(), service,
-                    aidGroup);
+            if (!mServiceCache.registerAidGroupForService(userId, Binder.getCallingUid(), service,
+                    aidGroup)) {
+                return false;
+            }
+            NfcService.getInstance().onPreferredPaymentChanged(
+                    NfcAdapter.PREFERRED_PAYMENT_UPDATED);
+            return true;
         }
 
         @Override
@@ -387,8 +440,13 @@
             if (!isServiceRegistered(userId, service)) {
                 return false;
             }
-            return mServiceCache.setOffHostSecureElement(userId, Binder.getCallingUid(), service,
-                    offHostSE);
+            if (!mServiceCache.setOffHostSecureElement(userId, Binder.getCallingUid(), service,
+                    offHostSE)) {
+                return false;
+            }
+            NfcService.getInstance().onPreferredPaymentChanged(
+                    NfcAdapter.PREFERRED_PAYMENT_UPDATED);
+            return true;
         }
 
         @Override
@@ -398,7 +456,12 @@
             if (!isServiceRegistered(userId, service)) {
                 return false;
             }
-            return mServiceCache.unsetOffHostSecureElement(userId, Binder.getCallingUid(), service);
+            if (!mServiceCache.unsetOffHostSecureElement(userId, Binder.getCallingUid(), service)) {
+                return false;
+            }
+            NfcService.getInstance().onPreferredPaymentChanged(
+                    NfcAdapter.PREFERRED_PAYMENT_UPDATED);
+            return true;
         }
 
         @Override
@@ -421,8 +484,13 @@
             if (!isServiceRegistered(userId, service)) {
                 return false;
             }
-            return mServiceCache.removeAidGroupForService(userId, Binder.getCallingUid(), service,
-                    category);
+            if (!mServiceCache.removeAidGroupForService(userId, Binder.getCallingUid(), service,
+                    category)) {
+                return false;
+            }
+            NfcService.getInstance().onPreferredPaymentChanged(
+                    NfcAdapter.PREFERRED_PAYMENT_UPDATED);
+            return true;
         }
 
         @Override
@@ -456,6 +524,14 @@
         public boolean supportsAidPrefixRegistration() throws RemoteException {
             return mAidCache.supportsAidPrefixRegistration();
         }
+
+        @Override
+        public ApduServiceInfo getPreferredPaymentService(int userId) throws RemoteException {
+            NfcPermissions.validateUserId(userId);
+            NfcPermissions.enforceUserPermissions(mContext);
+            NfcPermissions.enforcePreferredPaymentInfoPermissions(mContext);
+            return mServiceCache.getService(userId, mAidCache.getPreferredService());
+        }
     }
 
     /**
@@ -562,12 +638,18 @@
     public void onPreferredPaymentServiceChanged(ComponentName service) {
         mAidCache.onPreferredPaymentServiceChanged(service);
         mHostEmulationManager.onPreferredPaymentServiceChanged(service);
+
+        NfcService.getInstance().onPreferredPaymentChanged(
+                NfcAdapter.PREFERRED_PAYMENT_CHANGED);
     }
 
     @Override
     public void onPreferredForegroundServiceChanged(ComponentName service) {
         mAidCache.onPreferredForegroundServiceChanged(service);
         mHostEmulationManager.onPreferredForegroundServiceChanged(service);
+
+        NfcService.getInstance().onPreferredPaymentChanged(
+                NfcAdapter.PREFERRED_PAYMENT_CHANGED);
     }
 
     @Override
diff --git a/src/com/android/nfc/cardemulation/EnabledNfcFServices.java b/src/com/android/nfc/cardemulation/EnabledNfcFServices.java
index c06ca26..0e7edbe 100644
--- a/src/com/android/nfc/cardemulation/EnabledNfcFServices.java
+++ b/src/com/android/nfc/cardemulation/EnabledNfcFServices.java
@@ -27,6 +27,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.util.Log;
+import android.util.proto.ProtoOutputStream;
 
 public class EnabledNfcFServices implements com.android.nfc.ForegroundUtils.Callback {
     static final String TAG = "EnabledNfcFCardEmulationServices";
@@ -217,4 +218,25 @@
 
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
     }
+
+    /**
+     * Dump debugging information as a EnabledNfcFServicesProto
+     *
+     * Note:
+     * See proto definition in frameworks/base/core/proto/android/nfc/card_emulation.proto
+     * When writing a nested message, must call {@link ProtoOutputStream#start(long)} before and
+     * {@link ProtoOutputStream#end(long)} after.
+     * Never reuse a proto field number. When removing a field, mark it as reserved.
+     */
+    void dumpDebug(ProtoOutputStream proto) {
+        if (mForegroundComponent != null) {
+            mForegroundComponent.dumpDebug(proto, EnabledNfcFServicesProto.FOREGROUND_COMPONENT);
+        }
+        if (mForegroundRequested != null) {
+            mForegroundRequested.dumpDebug(proto, EnabledNfcFServicesProto.FOREGROUND_REQUESTED);
+        }
+        proto.write(EnabledNfcFServicesProto.ACTIVATED, mActivated);
+        proto.write(EnabledNfcFServicesProto.COMPUTE_FG_REQUESTED, mComputeFgRequested);
+        proto.write(EnabledNfcFServicesProto.FOREGROUND_UID, mForegroundUid);
+    }
 }
diff --git a/src/com/android/nfc/cardemulation/HostEmulationManager.java b/src/com/android/nfc/cardemulation/HostEmulationManager.java
index df701f2..2d763f9 100644
--- a/src/com/android/nfc/cardemulation/HostEmulationManager.java
+++ b/src/com/android/nfc/cardemulation/HostEmulationManager.java
@@ -34,16 +34,15 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.util.Log;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.nfc.NfcService;
+import com.android.nfc.NfcStatsLog;
 import com.android.nfc.cardemulation.RegisteredAidCache.AidResolveInfo;
-
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 
-import android.util.StatsLog;
-
 public class HostEmulationManager {
     static final String TAG = "HostEmulationManager";
     static final boolean DBG = false;
@@ -225,12 +224,12 @@
                         mState = STATE_W4_SERVICE;
                     }
                     if(CardEmulation.CATEGORY_PAYMENT.equals(resolveInfo.category))
-                      StatsLog.write(StatsLog.NFC_CARDEMULATION_OCCURRED,
-                                     StatsLog.NFC_CARDEMULATION_OCCURRED__CATEGORY__HCE_PAYMENT,
+                      NfcStatsLog.write(NfcStatsLog.NFC_CARDEMULATION_OCCURRED,
+                                     NfcStatsLog.NFC_CARDEMULATION_OCCURRED__CATEGORY__HCE_PAYMENT,
                                      "HCE");
                     else
-                      StatsLog.write(StatsLog.NFC_CARDEMULATION_OCCURRED,
-                                     StatsLog.NFC_CARDEMULATION_OCCURRED__CATEGORY__HCE_OTHER,
+                      NfcStatsLog.write(NfcStatsLog.NFC_CARDEMULATION_OCCURRED,
+                                     NfcStatsLog.NFC_CARDEMULATION_OCCURRED__CATEGORY__HCE_OTHER,
                                      "HCE");
 
                 } else {
@@ -551,4 +550,22 @@
             pw.println("    other: " + mServiceName);
         }
     }
+
+    /**
+     * Dump debugging information as a HostEmulationManagerProto
+     *
+     * Note:
+     * See proto definition in frameworks/base/core/proto/android/nfc/card_emulation.proto
+     * When writing a nested message, must call {@link ProtoOutputStream#start(long)} before and
+     * {@link ProtoOutputStream#end(long)} after.
+     * Never reuse a proto field number. When removing a field, mark it as reserved.
+     */
+    void dumpDebug(ProtoOutputStream proto) {
+        if (mPaymentServiceBound) {
+            mPaymentServiceName.dumpDebug(proto, HostEmulationManagerProto.PAYMENT_SERVICE_NAME);
+        }
+        if (mServiceBound) {
+            mServiceName.dumpDebug(proto, HostEmulationManagerProto.SERVICE_NAME);
+        }
+    }
 }
diff --git a/src/com/android/nfc/cardemulation/HostNfcFEmulationManager.java b/src/com/android/nfc/cardemulation/HostNfcFEmulationManager.java
index 1880a7a..8b9e7de 100644
--- a/src/com/android/nfc/cardemulation/HostNfcFEmulationManager.java
+++ b/src/com/android/nfc/cardemulation/HostNfcFEmulationManager.java
@@ -20,8 +20,8 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
-import android.nfc.cardemulation.NfcFServiceInfo;
 import android.nfc.cardemulation.HostNfcFService;
+import android.nfc.cardemulation.NfcFServiceInfo;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -30,14 +30,13 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.util.Log;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.nfc.NfcService;
-
+import com.android.nfc.NfcStatsLog;
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 
-import android.util.StatsLog;
-
 public class HostNfcFEmulationManager {
     static final String TAG = "HostNfcFEmulationManager";
     static final boolean DBG = false;
@@ -135,8 +134,8 @@
                     mPendingPacket = data;
                     mState = STATE_W4_SERVICE;
                 }
-                StatsLog.write(StatsLog.NFC_CARDEMULATION_OCCURRED,
-                               StatsLog.NFC_CARDEMULATION_OCCURRED__CATEGORY__HCE_PAYMENT,
+                NfcStatsLog.write(NfcStatsLog.NFC_CARDEMULATION_OCCURRED,
+                               NfcStatsLog.NFC_CARDEMULATION_OCCURRED__CATEGORY__HCE_PAYMENT,
                                "HCEF");
                 break;
             case STATE_W4_SERVICE:
@@ -372,4 +371,19 @@
             pw.println("    service: " + mServiceName);
         }
     }
+
+    /**
+     * Dump debugging information as a HostNfcFEmulationManagerProto
+     *
+     * Note:
+     * See proto definition in frameworks/base/core/proto/android/nfc/card_emulation.proto
+     * When writing a nested message, must call {@link ProtoOutputStream#start(long)} before and
+     * {@link ProtoOutputStream#end(long)} after.
+     * Never reuse a proto field number. When removing a field, mark it as reserved.
+     */
+    void dumpDebug(ProtoOutputStream proto) {
+        if (mServiceBound) {
+            mServiceName.dumpDebug(proto, HostNfcFEmulationManagerProto.SERVICE_NAME);
+        }
+    }
 }
diff --git a/src/com/android/nfc/cardemulation/PreferredServices.java b/src/com/android/nfc/cardemulation/PreferredServices.java
index fadb879..7b0a6e6 100644
--- a/src/com/android/nfc/cardemulation/PreferredServices.java
+++ b/src/com/android/nfc/cardemulation/PreferredServices.java
@@ -35,6 +35,7 @@
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
 import android.util.Log;
+import android.util.proto.ProtoOutputStream;
 
 /**
  * This class keeps track of what HCE/SE-based services are
@@ -385,4 +386,35 @@
         pw.println("        Payment settings allows override: " + mPaymentDefaults.preferForeground);
         pw.println("");
     }
+
+    /**
+     * Dump debugging information as a PreferredServicesProto
+     *
+     * Note:
+     * See proto definition in frameworks/base/core/proto/android/nfc/card_emulation.proto
+     * When writing a nested message, must call {@link ProtoOutputStream#start(long)} before and
+     * {@link ProtoOutputStream#end(long)} after.
+     * Never reuse a proto field number. When removing a field, mark it as reserved.
+     */
+    void dumpDebug(ProtoOutputStream proto) {
+        if (mForegroundCurrent != null) {
+            mForegroundCurrent.dumpDebug(proto, PreferredServicesProto.FOREGROUND_CURRENT);
+        }
+        if (mPaymentDefaults.currentPreferred != null) {
+            mPaymentDefaults.currentPreferred.dumpDebug(proto,
+                    PreferredServicesProto.FOREGROUND_CURRENT);
+        }
+        if (mNextTapDefault != null) {
+            mNextTapDefault.dumpDebug(proto, PreferredServicesProto.NEXT_TAP_DEFAULT);
+        }
+        proto.write(PreferredServicesProto.FOREGROUND_UID, mForegroundUid);
+        if (mForegroundRequested != null) {
+            mForegroundRequested.dumpDebug(proto, PreferredServicesProto.FOREGROUND_REQUESTED);
+        }
+        if (mPaymentDefaults.settingsDefault != null) {
+            mPaymentDefaults.settingsDefault.dumpDebug(proto,
+                    PreferredServicesProto.SETTINGS_DEFAULT);
+        }
+        proto.write(PreferredServicesProto.PREFER_FOREGROUND, mPaymentDefaults.preferForeground);
+    }
 }
diff --git a/src/com/android/nfc/cardemulation/RegisteredAidCache.java b/src/com/android/nfc/cardemulation/RegisteredAidCache.java
index b8ad8fc..7618d38 100644
--- a/src/com/android/nfc/cardemulation/RegisteredAidCache.java
+++ b/src/com/android/nfc/cardemulation/RegisteredAidCache.java
@@ -22,6 +22,7 @@
 import android.nfc.cardemulation.ApduServiceInfo;
 import android.nfc.cardemulation.CardEmulation;
 import android.util.Log;
+import android.util.proto.ProtoOutputStream;
 
 import com.google.android.collect.Maps;
 import java.util.Collections;
@@ -885,6 +886,16 @@
         }
     }
 
+    public ComponentName getPreferredService() {
+        if (mPreferredForegroundService != null) {
+            // return current foreground service
+            return mPreferredForegroundService;
+        } else {
+            // return current preferred service
+            return mPreferredPaymentService;
+        }
+    }
+
     public void onNfcDisabled() {
         synchronized (mLock) {
             mNfcEnabled = false;
@@ -935,4 +946,45 @@
         mRoutingManager.dump(fd, pw, args);
         pw.println("");
     }
+
+    /**
+     * Dump debugging information as a RegisteredAidCacheProto
+     *
+     * Note:
+     * See proto definition in frameworks/base/core/proto/android/nfc/card_emulation.proto
+     * When writing a nested message, must call {@link ProtoOutputStream#start(long)} before and
+     * {@link ProtoOutputStream#end(long)} after.
+     * Never reuse a proto field number. When removing a field, mark it as reserved.
+     */
+    void dumpDebug(ProtoOutputStream proto) {
+        for (Map.Entry<String, AidResolveInfo> entry : mAidCache.entrySet()) {
+            long token = proto.start(RegisteredAidCacheProto.AID_CACHE_ENTRIES);
+            proto.write(RegisteredAidCacheProto.AidCacheEntry.KEY, entry.getKey());
+            proto.write(RegisteredAidCacheProto.AidCacheEntry.CATEGORY, entry.getValue().category);
+            ApduServiceInfo defaultServiceInfo = entry.getValue().defaultService;
+            ComponentName defaultComponent = defaultServiceInfo != null ?
+                    defaultServiceInfo.getComponent() : null;
+            if (defaultComponent != null) {
+                defaultComponent.dumpDebug(proto,
+                        RegisteredAidCacheProto.AidCacheEntry.DEFAULT_COMPONENT);
+            }
+            for (ApduServiceInfo serviceInfo : entry.getValue().services) {
+                long sToken = proto.start(RegisteredAidCacheProto.AidCacheEntry.SERVICES);
+                serviceInfo.dumpDebug(proto);
+                proto.end(sToken);
+            }
+            proto.end(token);
+        }
+        if (mPreferredForegroundService != null) {
+            mPreferredForegroundService.dumpDebug(proto,
+                    RegisteredAidCacheProto.PREFERRED_FOREGROUND_SERVICE);
+        }
+        if (mPreferredPaymentService != null) {
+            mPreferredPaymentService.dumpDebug(proto,
+                    RegisteredAidCacheProto.PREFERRED_PAYMENT_SERVICE);
+        }
+        long token = proto.start(RegisteredAidCacheProto.ROUTING_MANAGER);
+        mRoutingManager.dumpDebug(proto);
+        proto.end(token);
+    }
 }
diff --git a/src/com/android/nfc/cardemulation/RegisteredNfcFServicesCache.java b/src/com/android/nfc/cardemulation/RegisteredNfcFServicesCache.java
index 5867b3c..a89f370 100644
--- a/src/com/android/nfc/cardemulation/RegisteredNfcFServicesCache.java
+++ b/src/com/android/nfc/cardemulation/RegisteredNfcFServicesCache.java
@@ -38,6 +38,7 @@
 import android.util.Log;
 import android.util.SparseArray;
 import android.util.Xml;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.util.FastXmlSerializer;
 import com.google.android.collect.Maps;
@@ -726,4 +727,24 @@
         }
     }
 
+    /**
+     * Dump debugging information as a RegisteredNfcFServicesCacheProto
+     *
+     * Note:
+     * See proto definition in frameworks/base/core/proto/android/nfc/card_emulation.proto
+     * When writing a nested message, must call {@link ProtoOutputStream#start(long)} before and
+     * {@link ProtoOutputStream#end(long)} after.
+     * Never reuse a proto field number. When removing a field, mark it as reserved.
+     */
+    void dumpDebug(ProtoOutputStream proto) {
+        synchronized (mLock) {
+            UserServices userServices = findOrCreateUserLocked(ActivityManager.getCurrentUser());
+            for (NfcFServiceInfo service : userServices.services.values()) {
+                long token = proto.start(RegisteredNfcFServicesCacheProto.NFC_FSERVICE_INFO);
+                service.dumpDebug(proto);
+                proto.end(token);
+            }
+        }
+    }
+
 }
diff --git a/src/com/android/nfc/cardemulation/RegisteredServicesCache.java b/src/com/android/nfc/cardemulation/RegisteredServicesCache.java
index f16a60e..2f0d898 100644
--- a/src/com/android/nfc/cardemulation/RegisteredServicesCache.java
+++ b/src/com/android/nfc/cardemulation/RegisteredServicesCache.java
@@ -40,6 +40,7 @@
 import android.util.Log;
 import android.util.SparseArray;
 import android.util.Xml;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.util.FastXmlSerializer;
 import com.google.android.collect.Maps;
@@ -657,4 +658,21 @@
         pw.println("");
     }
 
+    /**
+     * Dump debugging information as a RegisteredServicesCacheProto
+     *
+     * Note:
+     * See proto definition in frameworks/base/core/proto/android/nfc/card_emulation.proto
+     * When writing a nested message, must call {@link ProtoOutputStream#start(long)} before and
+     * {@link ProtoOutputStream#end(long)} after.
+     * Never reuse a proto field number. When removing a field, mark it as reserved.
+     */
+    void dumpDebug(ProtoOutputStream proto) {
+        UserServices userServices = findOrCreateUserLocked(ActivityManager.getCurrentUser());
+        for (ApduServiceInfo service : userServices.services.values()) {
+            long token = proto.start(RegisteredServicesCacheProto.APDU_SERVICE_INFOS);
+            service.dumpDebug(proto);
+            proto.end(token);
+        }
+    }
 }
diff --git a/src/com/android/nfc/cardemulation/RegisteredT3tIdentifiersCache.java b/src/com/android/nfc/cardemulation/RegisteredT3tIdentifiersCache.java
index bc11ba0..f24ee69 100644
--- a/src/com/android/nfc/cardemulation/RegisteredT3tIdentifiersCache.java
+++ b/src/com/android/nfc/cardemulation/RegisteredT3tIdentifiersCache.java
@@ -21,6 +21,7 @@
 import android.content.Context;
 import android.nfc.cardemulation.NfcFServiceInfo;
 import android.util.Log;
+import android.util.proto.ProtoOutputStream;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -221,4 +222,25 @@
         mRoutingManager.dump(fd, pw, args);
         pw.println("");
     }
+
+    /**
+     * Dump debugging information as a RegisteredT3tIdentifiersCacheProto
+     *
+     * Note:
+     * See proto definition in frameworks/base/core/proto/android/nfc/card_emulation.proto
+     * When writing a nested message, must call {@link ProtoOutputStream#start(long)} before and
+     * {@link ProtoOutputStream#end(long)} after.
+     * Never reuse a proto field number. When removing a field, mark it as reserved.
+     */
+    void dumpDebug(ProtoOutputStream proto) {
+        for (NfcFServiceInfo serviceInfo : mForegroundT3tIdentifiersCache.values()) {
+            long token = proto.start(
+                    RegisteredT3tIdentifiersCacheProto.T3T_IDENTIFIER_CACHE_ENTRIES);
+            serviceInfo.dumpDebug(proto);
+            proto.end(token);
+        }
+        long token = proto.start(RegisteredT3tIdentifiersCacheProto.ROUTING_MANAGER);
+        mRoutingManager.dumpDebug(proto);
+        proto.end(token);
+    }
 }
diff --git a/src/com/android/nfc/cardemulation/SystemCodeRoutingManager.java b/src/com/android/nfc/cardemulation/SystemCodeRoutingManager.java
index a2d2a77..eb8c9cb 100644
--- a/src/com/android/nfc/cardemulation/SystemCodeRoutingManager.java
+++ b/src/com/android/nfc/cardemulation/SystemCodeRoutingManager.java
@@ -17,6 +17,7 @@
 package com.android.nfc.cardemulation;
 
 import android.util.Log;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.nfc.NfcService;
 import com.android.nfc.cardemulation.RegisteredT3tIdentifiersCache.T3tIdentifier;
@@ -113,4 +114,26 @@
             }
         }
     }
+
+    /**
+     * Dump debugging information as a SystemCodeRoutingManagerProto
+     *
+     * Note:
+     * See proto definition in frameworks/base/core/proto/android/nfc/card_emulation.proto
+     * When writing a nested message, must call {@link ProtoOutputStream#start(long)} before and
+     * {@link ProtoOutputStream#end(long)} after.
+     * Never reuse a proto field number. When removing a field, mark it as reserved.
+     */
+    void dumpDebug(ProtoOutputStream proto) {
+        synchronized (mLock) {
+            for (T3tIdentifier t3tIdentifier : mConfiguredT3tIdentifiers) {
+                long token = proto.start(SystemCodeRoutingManagerProto.T3T_IDENTIFIERS);
+                proto.write(SystemCodeRoutingManagerProto.T3tIdentifier.SYSTEM_CODE,
+                        t3tIdentifier.systemCode);
+                proto.write(SystemCodeRoutingManagerProto.T3tIdentifier.NFCID2,
+                        t3tIdentifier.nfcid2);
+                proto.end(token);
+            }
+        }
+    }
 }
diff --git a/src/com/android/nfc/cardemulation/TapAgainDialog.java b/src/com/android/nfc/cardemulation/TapAgainDialog.java
index 7515d54..e786ab3 100644
--- a/src/com/android/nfc/cardemulation/TapAgainDialog.java
+++ b/src/com/android/nfc/cardemulation/TapAgainDialog.java
@@ -56,7 +56,7 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        setTheme(R.style.Theme_DeviceDefault_Light_Dialog_Alert);
+        setTheme(com.android.nfc.R.style.DialogAlertDayNight);
 
         final NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
         mCardEmuManager = CardEmulation.getInstance(adapter);
diff --git a/src/com/android/nfc/handover/BluetoothPeripheralHandover.java b/src/com/android/nfc/handover/BluetoothPeripheralHandover.java
index 09cdf04..98d4057 100644
--- a/src/com/android/nfc/handover/BluetoothPeripheralHandover.java
+++ b/src/com/android/nfc/handover/BluetoothPeripheralHandover.java
@@ -234,10 +234,12 @@
                             mAction = ACTION_DISCONNECT;
                         } else {
                             // Check if each profile of the device is disabled or not
-                            if (mHeadset.getPriority(mDevice) == BluetoothProfile.PRIORITY_OFF) {
+                            if (mHeadset.getConnectionPolicy(mDevice) ==
+                                BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) {
                                 mIsHeadsetAvailable = false;
                             }
-                            if (mA2dp.getPriority(mDevice) == BluetoothProfile.PRIORITY_OFF) {
+                            if (mA2dp.getConnectionPolicy(mDevice) ==
+                                BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) {
                                 mIsA2dpAvailable = false;
                             }
                             if (!mIsHeadsetAvailable && !mIsA2dpAvailable) {
@@ -586,7 +588,7 @@
     boolean hasA2dpCapability(ParcelUuid[] uuids, BluetoothClass btClass) {
         if (uuids != null) {
             for (ParcelUuid uuid : uuids) {
-                if (BluetoothUuid.isAudioSink(uuid) || BluetoothUuid.isAdvAudioDist(uuid)) {
+                if (uuid.equals(BluetoothUuid.A2DP_SINK) || uuid.equals(BluetoothUuid.ADV_AUDIO_DIST)) {
                     return true;
                 }
             }
@@ -600,7 +602,7 @@
     boolean hasHeadsetCapability(ParcelUuid[] uuids, BluetoothClass btClass) {
         if (uuids != null) {
             for (ParcelUuid uuid : uuids) {
-                if (BluetoothUuid.isHandsfree(uuid) || BluetoothUuid.isHeadset(uuid)) {
+                if (uuid.equals(BluetoothUuid.HFP) || uuid.equals(BluetoothUuid.HSP)) {
                     return true;
                 }
             }
diff --git a/src/com/android/nfc/handover/ConfirmConnectActivity.java b/src/com/android/nfc/handover/ConfirmConnectActivity.java
index c87fc36..c4ac8ab 100644
--- a/src/com/android/nfc/handover/ConfirmConnectActivity.java
+++ b/src/com/android/nfc/handover/ConfirmConnectActivity.java
@@ -36,7 +36,7 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         AlertDialog.Builder builder = new AlertDialog.Builder(this,
-                AlertDialog.THEME_DEVICE_DEFAULT_LIGHT);
+                R.style.DialogAlertDayNight);
         Intent launchIntent = getIntent();
         mDevice = launchIntent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
         if (mDevice == null) finish();