Merge "Call OffHostAidSelected only when RF_NFCEE_ACTION_NTF is triggered with AID" into main
diff --git a/NfcNci/nci/jni/NativeNfcManager.cpp b/NfcNci/nci/jni/NativeNfcManager.cpp
index 1bb5d86..6d4ce1d 100644
--- a/NfcNci/nci/jni/NativeNfcManager.cpp
+++ b/NfcNci/nci/jni/NativeNfcManager.cpp
@@ -151,7 +151,7 @@
 static bool sIsNfaEnabled = false;
 static bool sDiscoveryEnabled = false;  // is polling or listening
 static bool sPollingEnabled = false;    // is polling for tag?
-static bool sIsDisabling = false;
+bool sIsDisabling = false;
 static bool sRfEnabled = false;   // whether RF discovery is enabled
 static bool sSeRfActive = false;  // whether RF with SE is likely active
 static bool sReaderModeEnabled = false;  // whether we're only reading tags, not allowing card emu
@@ -1787,28 +1787,22 @@
 }
 
 static tNFA_STATUS setTechAPollingLoopAnnotation(JNIEnv* env, jobject o,
-                                          jbyteArray tech_a_polling_loop_annotation) {
+                                                  const uint8_t* annotation_data,
+                                                  size_t annotation_size) {
     std::vector<uint8_t> command;
     command.push_back(NCI_ANDROID_SET_TECH_A_POLLING_LOOP_ANNOTATION);
-    if (tech_a_polling_loop_annotation == NULL) {
-      // Annotation is null, setting 0 annotations
+    if (annotation_data == NULL || annotation_size == 0) {
+      // Annotation is null or size is 0, setting 0 annotations
       command.push_back(0x00);
     } else {
-      ScopedByteArrayRO annotationBytes(env, tech_a_polling_loop_annotation);
-      if (annotationBytes.size() > 0) {
-        command.push_back(0x01);
-        command.push_back(0x00);
-        command.push_back(annotationBytes.size() + 3);
-        command.push_back(0x0a);
-        command.insert(command.end(), &annotationBytes[0],
-                      &annotationBytes[annotationBytes.size()]);
-      } else {
-        // Annotation is zero length, setting 0 annotations"
-        command.push_back(0x00);
-      }
+      command.push_back(0x01);                 // Number of frame entries.
+      command.push_back(0x21);                 // Position and type.
+      command.push_back(annotation_size + 3);  // Length
+      command.push_back(0x0a);                 // Waiting time
+      command.insert(command.end(), annotation_data, annotation_data + annotation_size);
+      command.push_back(0x00);
+      command.push_back(0x00);
     }
-    command.push_back(0x00);
-    command.push_back(0x00);
     SyncEventGuard guard(gNfaVsCommand);
     tNFA_STATUS status =
         NFA_SendVsCommand(NCI_MSG_PROP_ANDROID, command.size(), command.data(), nfaVSCallback);
@@ -1872,7 +1866,19 @@
   if (tech_mask != 0) {
     stopPolling_rfDiscoveryDisabled();
     if (isReaderModeAnnotationSupported(e, o)) {
-      setTechAPollingLoopAnnotation(e, o, tech_a_polling_loop_annotation);
+      if (reader_mode) {
+        if (tech_a_polling_loop_annotation == NULL) {
+          setTechAPollingLoopAnnotation(e, o, NULL, 0);
+        } else {
+          ScopedByteArrayRO annotationBytes(e, tech_a_polling_loop_annotation);
+          setTechAPollingLoopAnnotation(e, o,
+                                        (const uint8_t*)annotationBytes.get(),
+                                        annotationBytes.size());
+        }
+      } else {
+        uint8_t ignoreFrame[] = {0x6a, 0x01, 0xcf, 0x00, 0x00};
+        setTechAPollingLoopAnnotation(e, 0, ignoreFrame, 5);
+      }
     }
 
     startPolling_rfDiscoveryDisabled(tech_mask);
@@ -2062,6 +2068,35 @@
 
   LOG(DEBUG) << StringPrintf("%s: deregister VS callbacks", __func__);
   NFA_RegVSCback(false, &nfaVSCallback);
+  // abort any active waits
+  {
+    SyncEventGuard guard(sNfaSetPowerSubState);
+    sNfaSetPowerSubState.notifyOne();
+  }
+  {
+    SyncEventGuard guard(sNfaEnableDisablePollingEvent);
+    sNfaEnableDisablePollingEvent.notifyOne();
+  }
+  {
+    SyncEventGuard guard(gNfaSetConfigEvent);
+    gNfaSetConfigEvent.notifyOne();
+  }
+  {
+    SyncEventGuard guard(gNfaGetConfigEvent);
+    gNfaGetConfigEvent.notifyOne();
+  }
+  {
+    SyncEventGuard guard(gNfaVsCommand);
+    gNfaVsCommand.notifyOne();
+  }
+  {
+    SyncEventGuard guard(gSendRawVsCmdEvent);
+    gSendRawVsCmdEvent.notifyOne();
+  }
+  {
+    SyncEventGuard guard(gNfaRemoveEpEvent);
+    gNfaRemoveEpEvent.notifyOne();
+  }
 
   NfcAdaptation& theInstance = NfcAdaptation::GetInstance();
   theInstance.Finalize();
diff --git a/NfcNci/nci/jni/NativeNfcTag.cpp b/NfcNci/nci/jni/NativeNfcTag.cpp
index e01f1f1..168fe4d 100644
--- a/NfcNci/nci/jni/NativeNfcTag.cpp
+++ b/NfcNci/nci/jni/NativeNfcTag.cpp
@@ -42,6 +42,7 @@
 namespace android {
 extern nfc_jni_native_data* getNative(JNIEnv* e, jobject o);
 extern bool nfcManager_isNfcActive();
+extern bool sIsDisabling;
 }  // namespace android
 
 extern bool gActivated;
@@ -130,6 +131,7 @@
 static bool sReselectTagIdle = false;
 
 static int sPresCheckStatus = 0;
+static bool sIsDisconnecting = false;
 
 static int reSelect(tNFA_INTF_TYPE rfInterface, bool fSwitchIfNeeded);
 extern bool gIsDtaEnabled;
@@ -546,9 +548,18 @@
   int retCode = NFCSTATUS_SUCCESS;
   tNFA_INTF_TYPE intfType = NFA_INTERFACE_FRAME;
 
+  if (sIsDisabling) {
+    LOG(ERROR) << StringPrintf("%s: NFC disabling in progress", __func__);
+    return NFCSTATUS_FAILED;
+  }
   sIsoDepPresCheckCnt = 0;
   sPresCheckErrCnt = 0;
   sIsoDepPresCheckAlternate = false;
+  if (sIsDisconnecting) {
+    LOG(ERROR) << StringPrintf("%s: Disconnect in progress", __func__);
+    retCode = NFCSTATUS_FAILED;
+    goto TheEnd;
+  }
 
   if (i >= NfcTag::MAX_NUM_TECHNOLOGY) {
     LOG(ERROR) << StringPrintf("%s: Handle not found", __func__);
@@ -738,7 +749,7 @@
     }
 
     /*Retry logic in case of core Generic error while selecting a tag*/
-    if (sConnectOk == false) {
+    if ((sConnectOk == false) && !sIsDisabling) {
       LOG(ERROR) << StringPrintf("%s: waiting for Card to be activated",
                                  __func__);
       int retry = 0;
@@ -797,6 +808,15 @@
   int retCode = NFCSTATUS_SUCCESS;
   NfcTag& natTag = NfcTag::getInstance();
 
+  if (sIsDisabling) {
+    LOG(ERROR) << StringPrintf("%s: NFC disabling in progress", __func__);
+    return NFCSTATUS_FAILED;
+  }
+  if (sIsDisconnecting) {
+    LOG(ERROR) << StringPrintf("%s: Disconnect in progress", __func__);
+    retCode = NFCSTATUS_FAILED;
+    goto TheEnd;
+  }
   if (natTag.getActivationState() != NfcTag::Active) {
     LOG(ERROR) << StringPrintf("%s: tag already deactivated", __func__);
     retCode = NFCSTATUS_FAILED;
@@ -856,6 +876,11 @@
   LOG(DEBUG) << StringPrintf("%s: enter", __func__);
   tNFA_STATUS nfaStat = NFA_STATUS_OK;
 
+  if (sIsDisabling) {
+    LOG(ERROR) << StringPrintf("%s: NFC disabling in progress", __func__);
+    return JNI_FALSE;
+  }
+  sIsDisconnecting = true;
   NfcTag::getInstance().resetAllTransceiveTimeouts();
   sReselectTagIdle = false;
 
@@ -871,6 +896,7 @@
                                nfaStat);
 
 TheEnd:
+  sIsDisconnecting = false;
   LOG(DEBUG) << StringPrintf("%s: exit", __func__);
   return (nfaStat == NFA_STATUS_OK) ? JNI_TRUE : JNI_FALSE;
 }
@@ -939,6 +965,10 @@
   bool isNack = false;
   jint* targetLost = NULL;
   tNFA_STATUS status;
+  if (sIsDisabling) {
+    LOG(ERROR) << StringPrintf("%s: NFC disabling in progress", __func__);
+    return nullptr;
+  }
 
   if (NfcTag::getInstance().getActivationState() != NfcTag::Active) {
     if (statusTargetLost) {
@@ -1192,6 +1222,10 @@
   tNFA_STATUS status = NFA_STATUS_FAILED;
   jint* ndef = NULL;
 
+  if (sIsDisabling) {
+    LOG(ERROR) << StringPrintf("%s: NFC disabling in progress", __func__);
+    return NFA_STATUS_FAILED;
+  }
   LOG(DEBUG) << StringPrintf("%s: enter", __func__);
 
   // special case for Kovio
@@ -1329,6 +1363,10 @@
   LOG(DEBUG) << StringPrintf("%s", __func__);
   tNFA_STATUS status = NFA_STATUS_OK;
   bool isPresent = false;
+  if (sIsDisabling) {
+    LOG(ERROR) << StringPrintf("%s: NFC disabling in progress", __func__);
+    return JNI_FALSE;
+  }
 
   // Special case for Kovio.  The deactivation would have already occurred
   // but was ignored so that normal tag opertions could complete.  Now we
@@ -1579,6 +1617,10 @@
   LOG(DEBUG) << StringPrintf("%s: enter", __func__);
   tNFA_STATUS status = NFA_STATUS_OK;
 
+  if (sIsDisabling) {
+    LOG(ERROR) << StringPrintf("%s: NFC disabling in progress", __func__);
+    return JNI_FALSE;
+  }
   // Do not try to format if tag is already deactivated.
   if (NfcTag::getInstance().isActivated() == false) {
     LOG(DEBUG) << StringPrintf("%s: tag already deactivated(no need to format)",
@@ -1645,6 +1687,10 @@
 static jboolean nativeNfcTag_doMakeReadonly(JNIEnv* e, jobject o, jbyteArray) {
   jboolean result = JNI_FALSE;
   tNFA_STATUS status;
+  if (sIsDisabling) {
+    LOG(ERROR) << StringPrintf("%s: NFC disabling in progress", __func__);
+    return JNI_FALSE;
+  }
 
   LOG(DEBUG) << StringPrintf("%s", __func__);
 
diff --git a/NfcNci/nci/jni/RoutingManager.cpp b/NfcNci/nci/jni/RoutingManager.cpp
index 4e6270d..37a565e 100755
--- a/NfcNci/nci/jni/RoutingManager.cpp
+++ b/NfcNci/nci/jni/RoutingManager.cpp
@@ -461,6 +461,27 @@
   } else {
     LOG(DEBUG) << fn << ": No active EEs found";
   }
+  //release waits
+  {
+    SyncEventGuard guard(mEeRegisterEvent);
+    mEeRegisterEvent.notifyOne();
+  }
+  {
+    SyncEventGuard guard(mRoutingEvent);
+    mRoutingEvent.notifyOne();
+  }
+  {
+    SyncEventGuard guard(mEeSetModeEvent);
+    mEeSetModeEvent.notifyOne();
+  }
+  {
+    SyncEventGuard guard(mEePwrAndLinkCtrlEvent);
+    mEePwrAndLinkCtrlEvent.notifyOne();
+  }
+  {
+    SyncEventGuard guard(mAidAddRemoveEvent);
+    mAidAddRemoveEvent.notifyOne();
+  }
 }
 
 /*******************************************************************************
diff --git a/NfcNci/res/values/overlayable.xml b/NfcNci/res/values/overlayable.xml
index b500405..cb2ecce 100644
--- a/NfcNci/res/values/overlayable.xml
+++ b/NfcNci/res/values/overlayable.xml
@@ -26,6 +26,7 @@
             <item name="polling_disable_allowed" type="bool" />
             <item name="nfcc_always_on_allowed" type="bool" />
             <item name="enable_reader_option_support" type="bool" />
+            <item name="enable_service_for_category_other" type="bool" />
             <item name="payment_foreground_preference" type="bool" />
             <item name="tag_intent_app_pref_supported" type="bool" />
             <item name="max_antenna_blocked_failure_count" type="integer" />
diff --git a/NfcNci/src/com/android/nfc/DeviceConfigFacade.java b/NfcNci/src/com/android/nfc/DeviceConfigFacade.java
index 1e14404..8c0a40e 100644
--- a/NfcNci/src/com/android/nfc/DeviceConfigFacade.java
+++ b/NfcNci/src/com/android/nfc/DeviceConfigFacade.java
@@ -49,11 +49,24 @@
     private boolean mReaderOptionDefault;
     private boolean mSecureNfcCapable;
     private boolean mSecureNfcDefault;
+    private boolean mEnableAutoPlay;
+    private boolean mPollingDisableAllowed;
+    private boolean mNfccAlwaysOnAllowed;
+    private boolean mEnableServiceOther;
+    private boolean mTagIntentAppPrefSupported;
+    private boolean mProprietaryGetcapsSupported;
+    private boolean mEnableOemExtension;
+    private boolean mEnableDeveloperNotification;
+    private boolean mCheckDisplayStateForScreenState;
+    private boolean mIndicateUserActivityForHce;
     private String mDefaultRoute;
     private String mDefaultIsoDepRoute;
     private String mDefaultOffHostRoute;
     private String mDefaultScRoute;
     private int mSlowTapThresholdMillis;
+    private int mUnknownTagPollingDelay;
+    private int mUnknownTagPollingDelayMax;
+    private int mUnknownTagPollingDelayLong;
 
     private static DeviceConfigFacade sInstance;
     public static DeviceConfigFacade getInstance(Context context, Handler handler) {
@@ -101,6 +114,46 @@
             KEY_SECURE_NFC_DEFAULT,
             mContext.getResources().getBoolean(R.bool.secure_nfc_default));
 
+        mEnableAutoPlay = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_NFC,
+                "enable_auto_play",
+                mContext.getResources().getBoolean(R.bool.enable_auto_play));
+
+        mPollingDisableAllowed = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_NFC,
+                "polling_disable_allowed",
+                mContext.getResources().getBoolean(R.bool.polling_disable_allowed));
+
+        mNfccAlwaysOnAllowed = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_NFC,
+                "nfcc_always_on_allowed",
+                mContext.getResources().getBoolean(R.bool.nfcc_always_on_allowed));
+
+        mEnableServiceOther = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_NFC,
+                "enable_service_for_category_other",
+                mContext.getResources().getBoolean(R.bool.enable_service_for_category_other));
+
+        mTagIntentAppPrefSupported = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_NFC,
+                "tag_intent_app_pref_supported",
+                mContext.getResources().getBoolean(R.bool.tag_intent_app_pref_supported));
+
+        mProprietaryGetcapsSupported = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_NFC,
+                "nfc_proprietary_getcaps_supported",
+                mContext.getResources().getBoolean(R.bool.nfc_proprietary_getcaps_supported));
+
+        mEnableOemExtension = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_NFC,
+                "enable_oem_extension",
+                mContext.getResources().getBoolean(R.bool.enable_oem_extension));
+
+        mEnableDeveloperNotification = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_NFC,
+                "enable_developer_option_notification",
+                mContext.getResources().getBoolean(R.bool.enable_developer_option_notification));
+
+        mCheckDisplayStateForScreenState = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_NFC,
+                "check_display_state_for_screen_state",
+                mContext.getResources().getBoolean(R.bool.check_display_state_for_screen_state));
+
+        mIndicateUserActivityForHce = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_NFC,
+                "indicate_user_activity_for_hce",
+                mContext.getResources().getBoolean(R.bool.indicate_user_activity_for_hce));
+
         mDefaultRoute = DeviceConfig.getString(DEVICE_CONFIG_NAMESPACE_NFC,
                 "nfc_default_route",
                 mContext.getResources().getString(R.string.nfc_default_route));
@@ -120,6 +173,19 @@
         mSlowTapThresholdMillis = DeviceConfig.getInt(DEVICE_CONFIG_NAMESPACE_NFC,
                 KEY_SLOW_TAP_THRESHOLD_MILLIS,
                 mContext.getResources().getInteger(R.integer.slow_tap_threshold_millis));
+
+        mUnknownTagPollingDelay = DeviceConfig.getInt(DEVICE_CONFIG_NAMESPACE_NFC,
+                "unknown_tag_polling_delay",
+                mContext.getResources().getInteger(R.integer.unknown_tag_polling_delay));
+
+        mUnknownTagPollingDelayMax = DeviceConfig.getInt(DEVICE_CONFIG_NAMESPACE_NFC,
+                "unknown_tag_polling_delay_count_max",
+                mContext.getResources().getInteger(R.integer.unknown_tag_polling_delay_count_max));
+
+        mUnknownTagPollingDelayLong = DeviceConfig.getInt(DEVICE_CONFIG_NAMESPACE_NFC,
+                "unknown_tag_polling_delay_long",
+                mContext.getResources().getInteger(R.integer.unknown_tag_polling_delay_long));
+
     }
 
     private boolean isSecureNfcCapableDefault() {
@@ -158,6 +224,16 @@
     public boolean getDefaultSecureNfcState() {
         return mSecureNfcDefault;
     }
+    public boolean getEnableAutoPlay() { return mEnableAutoPlay; }
+    public boolean getPollingDisableAllowed() { return mPollingDisableAllowed; }
+    public boolean getNfccAlwaysOnAllowed() { return mNfccAlwaysOnAllowed; }
+    public boolean getEnableServiceOther() { return mEnableServiceOther; }
+    public boolean getTagIntentAppPrefSupported() { return mTagIntentAppPrefSupported; }
+    public boolean getProprietaryGetcapsSupported() { return mProprietaryGetcapsSupported; }
+    public boolean getEnableOemExtension() { return mEnableOemExtension; }
+    public boolean getEnableDeveloperNotification() { return mEnableDeveloperNotification; }
+    public boolean getCheckDisplayStateForScreenState() { return mCheckDisplayStateForScreenState; }
+    public boolean getIndicateUserActivityForHce() { return mIndicateUserActivityForHce; }
     public String getDefaultRoute() {
         return mDefaultRoute;
     }
@@ -173,4 +249,7 @@
     public int getSlowTapThresholdMillis() {
         return mSlowTapThresholdMillis;
     }
+    public int getUnknownTagPollingDelay() { return mUnknownTagPollingDelay; }
+    public int getUnknownTagPollingDelayMax() { return mUnknownTagPollingDelayMax; }
+    public int getUnknownTagPollingDelayLong() { return mUnknownTagPollingDelayLong; }
 }
diff --git a/NfcNci/src/com/android/nfc/NfcDispatcher.java b/NfcNci/src/com/android/nfc/NfcDispatcher.java
index 69566ea..bf7f929 100644
--- a/NfcNci/src/com/android/nfc/NfcDispatcher.java
+++ b/NfcNci/src/com/android/nfc/NfcDispatcher.java
@@ -115,6 +115,7 @@
     private final Handler mMessageHandler = new MessageHandler();
     private final Messenger mMessenger = new Messenger(mMessageHandler);
     private final AtomicBoolean mBluetoothEnabledByNfc;
+    private final DeviceConfigFacade mDeviceConfigFacade;
 
     // Locked on this
     private PendingIntent mOverrideIntent;
@@ -131,7 +132,7 @@
     NfcDispatcher(Context context,
                   HandoverDataParser handoverDataParser,
                   NfcInjector nfcInjector,
-                  boolean provisionOnly) {
+                  boolean provisionOnly, DeviceConfigFacade deviceConfigFacade) {
         mContext = context;
         mTechListFilters = new RegisteredComponentCache(mContext,
                 NfcAdapter.ACTION_TECH_DISCOVERED, NfcAdapter.ACTION_TECH_DISCOVERED);
@@ -144,6 +145,7 @@
         mForegroundUid = Process.INVALID_UID;
         mForegroundUtils = ForegroundUtils.getInstance(
                 context.getSystemService(ActivityManager.class));
+        mDeviceConfigFacade = deviceConfigFacade;
         synchronized (this) {
             mProvisioningOnly = provisionOnly;
         }
@@ -596,8 +598,7 @@
         boolean screenUnlocked = false;
         if (!provisioningOnly &&
                 mScreenStateHelper.checkScreenState(
-                        mContext.getResources().getBoolean(
-                                R.bool.check_display_state_for_screen_state))
+                        mDeviceConfigFacade.getCheckDisplayStateForScreenState())
                         == ScreenStateHelper.SCREEN_STATE_ON_LOCKED) {
             screenUnlocked = handleNfcUnlock(tag);
             if (!screenUnlocked)
diff --git a/NfcNci/src/com/android/nfc/NfcInjector.java b/NfcNci/src/com/android/nfc/NfcInjector.java
index 3abd3e0..8611ed2 100644
--- a/NfcNci/src/com/android/nfc/NfcInjector.java
+++ b/NfcNci/src/com/android/nfc/NfcInjector.java
@@ -110,7 +110,8 @@
         mHandoverDataParser = new HandoverDataParser();
         mDeviceConfigFacade = new DeviceConfigFacade(mContext, new Handler(mainLooper));
         mNfcDispatcher =
-            new NfcDispatcher(mContext, mHandoverDataParser, this, isInProvisionMode());
+            new NfcDispatcher(mContext, mHandoverDataParser, this,
+                    isInProvisionMode(), mDeviceConfigFacade);
         mVibrationEffect = VibrationEffect.createOneShot(200, VibrationEffect.DEFAULT_AMPLITUDE);
         mBackupManager = new BackupManager(mContext);
         mFeatureFlags = new com.android.nfc.flags.FeatureFlagsImpl();
diff --git a/NfcNci/src/com/android/nfc/NfcProprietaryCaps.java b/NfcNci/src/com/android/nfc/NfcProprietaryCaps.java
index 4079b09..9c87e2f 100644
--- a/NfcNci/src/com/android/nfc/NfcProprietaryCaps.java
+++ b/NfcNci/src/com/android/nfc/NfcProprietaryCaps.java
@@ -140,6 +140,8 @@
                 + mIsPowerSavingModeSupported
                 + ", isAutotransactPollingLoopFilterSupported="
                 + mIsAutotransactPollingLoopFilterSupported
+                + ", mIsReaderModeAnnotationSupported="
+                + mIsReaderModeAnnotationSupported
                 + '}';
     }
 }
diff --git a/NfcNci/src/com/android/nfc/NfcService.java b/NfcNci/src/com/android/nfc/NfcService.java
index a2940f5..4397aab 100644
--- a/NfcNci/src/com/android/nfc/NfcService.java
+++ b/NfcNci/src/com/android/nfc/NfcService.java
@@ -1157,8 +1157,7 @@
 
         mAlarmManager = mContext.getSystemService(AlarmManager.class);
 
-        mCheckDisplayStateForScreenState =
-                mContext.getResources().getBoolean(R.bool.check_display_state_for_screen_state);
+        mCheckDisplayStateForScreenState = mDeviceConfigFacade.getCheckDisplayStateForScreenState();
         if (mInProvisionMode) {
             mScreenState = mScreenStateHelper.checkScreenStateProvisionMode();
         } else {
@@ -1246,13 +1245,11 @@
         }
 
         // Polling delay count for switching from stage one to stage two.
-        mPollDelayCountMax =
-                mContext.getResources().getInteger(R.integer.unknown_tag_polling_delay_count_max);
+        mPollDelayCountMax = mDeviceConfigFacade.getUnknownTagPollingDelayMax();
         // Stage one: polling delay time for the first few unknown tag detections
-        mPollDelayTime = mContext.getResources().getInteger(R.integer.unknown_tag_polling_delay);
+        mPollDelayTime = mDeviceConfigFacade.getUnknownTagPollingDelay();
         // Stage two: longer polling delay time after max_poll_delay_count
-        mPollDelayTimeLong =
-                mContext.getResources().getInteger(R.integer.unknown_tag_polling_delay_long);
+        mPollDelayTimeLong = mDeviceConfigFacade.getUnknownTagPollingDelayLong();
         // Polling delay if read error found more than max count.
         mReadErrorCountMax =
                 mContext.getResources().getInteger(R.integer.unknown_tag_read_error_count_max);
@@ -1260,7 +1257,7 @@
         mNotifyDispatchFailed = mContext.getResources().getBoolean(R.bool.enable_notify_dispatch_failed);
         mNotifyReadFailed = mContext.getResources().getBoolean(R.bool.enable_notify_read_failed);
 
-        mPollingDisableAllowed = mContext.getResources().getBoolean(R.bool.polling_disable_allowed);
+        mPollingDisableAllowed = mDeviceConfigFacade.getPollingDisableAllowed();
         mAppInActivityDetectionTime =
             mContext.getResources().getInteger(R.integer.inactive_presence_check_allowed_time);
         mTagRemovalDetectionWaitTime =
@@ -1268,8 +1265,7 @@
         // Make sure this is only called when object construction is complete.
         mNfcInjector.getNfcManagerRegisterer().register(mNfcAdapter);
 
-        mIsAlwaysOnSupported =
-            mContext.getResources().getBoolean(R.bool.nfcc_always_on_allowed);
+        mIsAlwaysOnSupported = mDeviceConfigFacade.getNfccAlwaysOnAllowed();
 
         mIsTagAppPrefSupported =
             mContext.getResources().getBoolean(R.bool.tag_intent_app_pref_supported);
@@ -1351,8 +1347,8 @@
         executeTaskBoot();  // do blocking boot tasks
 
         if ((NFC_SNOOP_LOG_MODE.equals(NfcProperties.snoop_log_mode_values.FULL) ||
-            NFC_VENDOR_DEBUG_ENABLED) && mContext.getResources().getBoolean(
-                    R.bool.enable_developer_option_notification)) {
+            NFC_VENDOR_DEBUG_ENABLED) &&
+                mDeviceConfigFacade.getEnableDeveloperNotification()) {
             new NfcDeveloperOptionNotification(mContext).startNotification();
         }
 
@@ -1366,7 +1362,7 @@
     private void executeTaskBoot() {
         // If overlay is set, delay the NFC boot up until the OEM extension indicates it is ready to
         // proceed with NFC bootup.
-        if (mContext.getResources().getBoolean(R.bool.enable_oem_extension)) {
+        if (mDeviceConfigFacade.getEnableOemExtension()) {
             // Send intent for OEM extension to initialize.
             Intent intent = new Intent(NfcOemExtension.ACTION_OEM_EXTENSION_INIT);
             mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT, BIND_NFC_SERVICE);
@@ -4745,6 +4741,8 @@
 
     public int commitRouting(boolean isOverrideOrRecover) {
         if (!isOverrideOrRecover) {
+            // Clear the Handler queue, only last commit_msg is relevant
+            mHandler.removeMessages(MSG_COMMIT_ROUTING);
             mHandler.sendEmptyMessage(MSG_COMMIT_ROUTING);
             return STATUS_OK;
         }
@@ -5848,8 +5846,8 @@
                 applyScreenState(mScreenStateHelper.checkScreenState(mCheckDisplayStateForScreenState));
 
                 if ((NFC_SNOOP_LOG_MODE.equals(NfcProperties.snoop_log_mode_values.FULL) ||
-                        NFC_VENDOR_DEBUG_ENABLED) && mContext.getResources().getBoolean(
-                                R.bool.enable_developer_option_notification)) {
+                        NFC_VENDOR_DEBUG_ENABLED) &&
+                        mDeviceConfigFacade.getEnableDeveloperNotification()){
                     new NfcDeveloperOptionNotification(mContext.createContextAsUser(
                             UserHandle.of(ActivityManager.getCurrentUser()), /*flags=*/0))
                             .startNotification();
@@ -5863,8 +5861,8 @@
                 setPaymentForegroundPreference(userId);
 
                 if ((NFC_SNOOP_LOG_MODE.equals(NfcProperties.snoop_log_mode_values.FULL) ||
-                        NFC_VENDOR_DEBUG_ENABLED) && mContext.getResources().getBoolean(
-                        R.bool.enable_developer_option_notification)) {
+                        NFC_VENDOR_DEBUG_ENABLED) &&
+                        mDeviceConfigFacade.getEnableDeveloperNotification()) {
                     new NfcDeveloperOptionNotification(mContext.createContextAsUser(
                             UserHandle.of(ActivityManager.getCurrentUser()), /*flags=*/0))
                             .startNotification();
diff --git a/NfcNci/src/com/android/nfc/NfcShellCommand.java b/NfcNci/src/com/android/nfc/NfcShellCommand.java
index 2825f46..0f77f92 100644
--- a/NfcNci/src/com/android/nfc/NfcShellCommand.java
+++ b/NfcNci/src/com/android/nfc/NfcShellCommand.java
@@ -77,7 +77,10 @@
     @VisibleForTesting
     @Override
     public PrintWriter getOutPrintWriter() {
-        return mPrintWriter;
+        if( mPrintWriter != null )
+            return mPrintWriter;
+        else
+            return super.getOutPrintWriter();
     }
 
     @Override
diff --git a/NfcNci/src/com/android/nfc/cardemulation/CardEmulationManager.java b/NfcNci/src/com/android/nfc/cardemulation/CardEmulationManager.java
index 08ff9b6..fea52e8 100644
--- a/NfcNci/src/com/android/nfc/cardemulation/CardEmulationManager.java
+++ b/NfcNci/src/com/android/nfc/cardemulation/CardEmulationManager.java
@@ -68,6 +68,7 @@
 import com.android.nfc.NfcPermissions;
 import com.android.nfc.NfcService;
 import com.android.nfc.R;
+import com.android.nfc.cardemulation.util.StatsdUtils;
 import com.android.nfc.cardemulation.util.TelephonyUtils;
 import com.android.nfc.flags.Flags;
 import com.android.nfc.proto.NfcEventProto;
@@ -155,6 +156,9 @@
     private final int mVendorApiLevel;
     private PreferredSubscriptionService mPreferredSubscriptionService = null;
     private TelephonyUtils mTelephonyUtils = null;
+    @Nullable
+    private final StatsdUtils mStatsdUtils;
+    private final DeviceConfigFacade mDeviceConfigFacade;
 
     // TODO: Move this object instantiation and dependencies to NfcInjector.
     public CardEmulationManager(Context context, NfcInjector nfcInjector,
@@ -191,6 +195,8 @@
         mVendorApiLevel = SystemProperties.getInt(
                 "ro.vendor.api_level", Build.VERSION.DEVICE_INITIAL_SDK_INT);
         mPreferredSubscriptionService = new PreferredSubscriptionService(mContext, this);
+        mStatsdUtils = nfcInjector.getStatsdUtils();
+        mDeviceConfigFacade = deviceConfigFacade;
         initialize();
     }
 
@@ -209,7 +215,9 @@
             RoutingOptionManager routingOptionManager,
             PowerManager powerManager,
             NfcEventLog nfcEventLog,
-            PreferredSubscriptionService preferredSubscriptionService) {
+            PreferredSubscriptionService preferredSubscriptionService,
+            StatsdUtils statsdUtils,
+            DeviceConfigFacade deviceConfigFacade) {
         mContext = context;
         mCardEmulationInterface = new CardEmulationInterface();
         mNfcFCardEmulationInterface = new NfcFCardEmulationInterface();
@@ -231,6 +239,8 @@
         mVendorApiLevel = SystemProperties.getInt(
                 "ro.vendor.api_level", Build.VERSION.DEVICE_INITIAL_SDK_INT);
         mPreferredSubscriptionService = preferredSubscriptionService;
+        mStatsdUtils = statsdUtils;
+        mDeviceConfigFacade = deviceConfigFacade;
         initialize();
     }
 
@@ -287,7 +297,7 @@
                 Log.e(TAG, "onHostCardEmulationActivated: failed", e);
             }
         }
-        if (mContext.getResources().getBoolean(R.bool.indicate_user_activity_for_hce)
+        if (mDeviceConfigFacade.getIndicateUserActivityForHce()
                 && mPowerManager != null) {
             // Use USER_ACTIVITY_FLAG_INDIRECT to applying power hints without resets
             // the screen timeout
@@ -903,11 +913,6 @@
                                     .setPollingLoopFilter(pollingLoopFilter)
                                     .build())
                             .build());
-            if (Flags.exitFrames()
-                    && mAidCache.isDefaultOrAssociatedWalletPackage(service.getPackageName(),
-                    userId)) {
-                updateFirmwareExitFramesForWalletRole(userId);
-            }
             return true;
         }
 
@@ -943,11 +948,6 @@
                                     .setPollingLoopFilter(pollingLoopFilter)
                                     .build())
                             .build());
-            if (Flags.exitFrames()
-                    && mAidCache.isDefaultOrAssociatedWalletPackage(service.getPackageName(),
-                    userId)) {
-                updateFirmwareExitFramesForWalletRole(userId);
-            }
             return true;
         }
 
@@ -983,11 +983,6 @@
                                     .setPollingLoopFilter(pollingLoopPatternFilter)
                                     .build())
                             .build());
-            if (Flags.exitFrames()
-                    && mAidCache.isDefaultOrAssociatedWalletPackage(service.getPackageName(),
-                    userId)) {
-                updateFirmwareExitFramesForWalletRole(userId);
-            }
             return true;
         }
 
@@ -1023,11 +1018,6 @@
                                     .setPollingLoopFilter(pollingLoopPatternFilter)
                                     .build())
                             .build());
-            if (Flags.exitFrames()
-                    && mAidCache.isDefaultOrAssociatedWalletPackage(service.getPackageName(),
-                    userId)) {
-                updateFirmwareExitFramesForWalletRole(userId);
-            }
             return true;
         }
 
@@ -1152,7 +1142,7 @@
         @Override
         public int setServiceEnabledForCategoryOther(int userId,
                 ComponentName app, boolean status) throws RemoteException {
-            if (!mContext.getResources().getBoolean(R.bool.enable_service_for_category_other))
+            if (!mDeviceConfigFacade.getEnableServiceOther())
               return SET_SERVICE_ENABLED_STATUS_FAILURE_FEATURE_UNSUPPORTED;
             NfcPermissions.enforceUserPermissions(mContext);
 
@@ -1732,6 +1722,10 @@
             callNfcEventCallbacks(listener -> listener.onObserveModeDisabledInFirmware(exitFrame));
         }
         mHostEmulationManager.onObserveModeDisabledInFirmware(exitFrame);
+
+        if (mStatsdUtils != null) {
+            mStatsdUtils.logAutoTransactReported(StatsdUtils.PROCESSOR_NFCC, exitFrame.getData());
+        }
     }
 
     @Override
diff --git a/NfcNci/src/com/android/nfc/cardemulation/HostEmulationManager.java b/NfcNci/src/com/android/nfc/cardemulation/HostEmulationManager.java
index 3a76b80..506d199 100644
--- a/NfcNci/src/com/android/nfc/cardemulation/HostEmulationManager.java
+++ b/NfcNci/src/com/android/nfc/cardemulation/HostEmulationManager.java
@@ -252,7 +252,12 @@
                 @Override
                 public void run() {
                     synchronized (mLock) {
-                        unbindInactiveServicesLocked();
+                        if (isHostCardEmulationActivated()) {
+                            // Skip in active state
+                            rescheduleInactivityChecks();
+                        } else {
+                            unbindInactiveServicesLocked();
+                        }
                     }
                 }
 
diff --git a/NfcNci/src/com/android/nfc/cardemulation/RegisteredServicesCache.java b/NfcNci/src/com/android/nfc/cardemulation/RegisteredServicesCache.java
index 1cf3ba1..b273741 100644
--- a/NfcNci/src/com/android/nfc/cardemulation/RegisteredServicesCache.java
+++ b/NfcNci/src/com/android/nfc/cardemulation/RegisteredServicesCache.java
@@ -1266,6 +1266,9 @@
                 Log.e(TAG, "removePollingLoopFilterForService: UID mismatch");
                 return false;
             }
+            DynamicSettings dynamicSettings =
+                    getOrCreateSettings(services, componentName, serviceInfo.getUid());
+            dynamicSettings.pollingLoopFilters.remove(pollingLoopFilter);
             serviceInfo.removePollingLoopFilter(pollingLoopFilter);
             newServices = new ArrayList<ApduServiceInfo>(services.services.values());
         }
@@ -1328,6 +1331,9 @@
                 Log.e(TAG, "removePollingLoopPatternFilterForService: UID mismatch");
                 return false;
             }
+            DynamicSettings dynamicSettings =
+                    getOrCreateSettings(services, componentName, serviceInfo.getUid());
+            dynamicSettings.pollingLoopPatternFilters.remove(pollingLoopPatternFilter);
             serviceInfo.removePollingLoopPatternFilter(pollingLoopPatternFilter);
             newServices = new ArrayList<ApduServiceInfo>(services.services.values());
         }
diff --git a/NfcNci/src/com/android/nfc/handover/BluetoothPeripheralHandover.java b/NfcNci/src/com/android/nfc/handover/BluetoothPeripheralHandover.java
index 7648f51..ae9c808 100644
--- a/NfcNci/src/com/android/nfc/handover/BluetoothPeripheralHandover.java
+++ b/NfcNci/src/com/android/nfc/handover/BluetoothPeripheralHandover.java
@@ -43,6 +43,8 @@
 
 import androidx.annotation.VisibleForTesting;
 
+import com.android.nfc.DeviceConfigFacade;
+import com.android.nfc.NfcInjector;
 import com.android.nfc.R;
 
 import java.lang.reflect.Method;
@@ -102,6 +104,7 @@
     final int mTransport;
     final boolean mProvisioning;
     final AudioManager mAudioManager;
+    private DeviceConfigFacade mDeviceConfigFacade;
 
     final Object mLock = new Object();
 
@@ -177,6 +180,8 @@
 
         mAudioManager = mContext.getSystemService(AudioManager.class);
 
+        mDeviceConfigFacade = NfcInjector.getInstance().getDeviceConfigFacade();
+
         mState = STATE_INIT;
     }
 
@@ -608,7 +613,7 @@
     }
 
     void startTheMusic() {
-        if (!mContext.getResources().getBoolean(R.bool.enable_auto_play) && !mIsMusicActive) {
+        if (!mDeviceConfigFacade.getEnableAutoPlay() && !mIsMusicActive) {
             return;
         }
 
diff --git a/NfcNci/tests/testcases/hostsidetests/exitframes/nfc_exit_frame_multi_device_test.py b/NfcNci/tests/testcases/hostsidetests/exitframes/nfc_exit_frame_multi_device_test.py
index 5efa715..793a2a7 100644
--- a/NfcNci/tests/testcases/hostsidetests/exitframes/nfc_exit_frame_multi_device_test.py
+++ b/NfcNci/tests/testcases/hostsidetests/exitframes/nfc_exit_frame_multi_device_test.py
@@ -228,7 +228,7 @@
         self.emulator.nfc_emulator.turnScreenOn()
         self.emulator.nfc_emulator.pressMenu()
 
-    """Tests the autotransact functionality.
+    """Tests the autotransact functionality with exit frames.
 
     Test Steps:
         1. Start emulator activity and set up payment HCE Service.
@@ -239,16 +239,14 @@
 
         Verifies:
         1. Observe mode is disabled and a transaction occurs.
-        2. After the first transaction, verifies that observe mode is reenabled.
-        3. After observe mode is reenabled, verifies that the tag is not
+        2. Verify correct exit frame was used for transaction.
+        3. After the first transaction, verifies that observe mode is reenabled.
+        4. After observe mode is reenabled, verifies that the tag is not
         detected.
-
-        TODO(johnrjohn) Add methods to register unique polling frames so we can
-        test different filters
     """
-    def test_exit_frames(self):
+    def test_exit_frames_manifest_filter(self):
         self._set_up_emulator(
-            "41fbc7b9",
+            "41fbc7b9", [], True,
             start_emulator_fun=self.emulator.nfc_emulator.startExitFrameActivity,
             service_list=[_PAYMENT_SERVICE_1],
             expected_service=_PAYMENT_SERVICE_1,
@@ -290,11 +288,347 @@
                 self.pn532, command_apdus, response_apdus)
         asserts.assert_false(
                     tag_detected,
-                    "Reader detected emulator even though observemode was enabled."
+                    "Reader detected emulator even though observe mode was enabled."
                 )
 
         self.emulator.nfc_emulator.setObserveModeEnabled(False)
 
+    """Tests the autotransact functionality with exit frames.
+
+    Test Steps:
+        1. Start emulator activity and set up payment HCE Service.
+        2. Enable observe mode.
+        3. Poll with a broadcast frame, and attempt to transact.
+        4. Wait for observe mode to be reenabled.
+        5. Poll again and verify that the tag is not detected.
+
+        Verifies:
+        1. Observe mode is disabled and a transaction occurs.
+        2. Verify correct exit frame was used for transaction.
+        3. After the first transaction, verifies that observe mode is reenabled.
+        4. After observe mode is reenabled, verifies that the tag is not
+        detected.
+    """
+    def test_exit_frames_registered_filters(self):
+        self._set_up_emulator(
+            "12345678", ["12345678", "aaaa"], True,
+            start_emulator_fun=self.emulator.nfc_emulator.startExitFrameActivity,
+            service_list=[_PAYMENT_SERVICE_1],
+            expected_service=_PAYMENT_SERVICE_1,
+            is_payment=True,
+            payment_default_service=_PAYMENT_SERVICE_1
+        )
+        asserts.skip_if(
+                    not self.emulator.nfc_emulator.isObserveModeSupported(),
+                    f"{self.emulator} observe mode not supported",
+                )
+        asserts.assert_true(
+            self.emulator.nfc_emulator.setObserveModeEnabled(True),
+            f"{self.emulator} could not set observe mode",
+        )
+
+        command_apdus, response_apdus = get_apdus(self.emulator.nfc_emulator,
+                                                      _PAYMENT_SERVICE_1)
+        test_pass_handler = self.emulator.nfc_emulator.asyncWaitForTestPass(
+            'ExitFrameListenerSuccess'
+        )
+        tag_detected, transacted = poll_and_transact(
+                self.pn532, command_apdus, response_apdus, "12345678")
+        asserts.assert_true(
+            tag_detected, _FAILED_TAG_MSG
+        )
+        asserts.assert_true(transacted, _FAILED_TRANSACTION_MSG)
+        test_pass_handler.waitAndGet('ExitFrameListenerSuccess', _NFC_TIMEOUT_SEC)
+
+
+        time.sleep(_NFC_TIMEOUT_SEC)
+
+        # Poll again and see if tag is detected, observe mode should be enabled
+        # by now.
+        asserts.assert_true(
+                    self.emulator.nfc_emulator.isObserveModeEnabled(),
+                    f"{self.emulator} isObserveModeEnabled did not return True",
+                )
+        tag_detected, _ = poll_and_transact(
+                self.pn532, command_apdus, response_apdus)
+        asserts.assert_false(
+                    tag_detected,
+                    "Reader detected emulator even though observe mode was enabled."
+                )
+
+        self.emulator.nfc_emulator.setObserveModeEnabled(False)
+
+    """Tests the autotransact functionality with exit frames.
+
+    Test Steps:
+        1. Start emulator activity and set up payment HCE Service.
+        2. Enable observe mode.
+        3. Poll with a broadcast frame, and attempt to transact.
+        4. Wait for observe mode to be reenabled.
+        5. Poll again and verify that the tag is not detected.
+
+        Verifies:
+        1. Observe mode is disabled and a transaction occurs.
+        2. Verify correct exit frame was used for transaction.
+        3. After the first transaction, verifies that observe mode is reenabled.
+        4. After observe mode is reenabled, verifies that the tag is not
+        detected.
+    """
+    def test_exit_frames_prefix_match(self):
+        self._set_up_emulator(
+            "dd1234", ["12345678", "dd.*", "ee.*", "ff.."], True,
+            start_emulator_fun=self.emulator.nfc_emulator.startExitFrameActivity,
+            service_list=[_PAYMENT_SERVICE_1],
+            expected_service=_PAYMENT_SERVICE_1,
+            is_payment=True,
+            payment_default_service=_PAYMENT_SERVICE_1
+        )
+        asserts.skip_if(
+                    not self.emulator.nfc_emulator.isObserveModeSupported(),
+                    f"{self.emulator} observe mode not supported",
+                )
+        asserts.assert_true(
+            self.emulator.nfc_emulator.setObserveModeEnabled(True),
+            f"{self.emulator} could not set observe mode",
+        )
+
+        command_apdus, response_apdus = get_apdus(self.emulator.nfc_emulator,
+                                                      _PAYMENT_SERVICE_1)
+        test_pass_handler = self.emulator.nfc_emulator.asyncWaitForTestPass(
+            'ExitFrameListenerSuccess'
+        )
+        tag_detected, transacted = poll_and_transact(
+                self.pn532, command_apdus, response_apdus, "dd1234")
+        asserts.assert_true(
+            tag_detected, _FAILED_TAG_MSG
+        )
+        asserts.assert_true(transacted, _FAILED_TRANSACTION_MSG)
+        test_pass_handler.waitAndGet('ExitFrameListenerSuccess', _NFC_TIMEOUT_SEC)
+
+
+        time.sleep(_NFC_TIMEOUT_SEC)
+
+        # Poll again and see if tag is detected, observe mode should be enabled
+        # by now.
+        asserts.assert_true(
+                    self.emulator.nfc_emulator.isObserveModeEnabled(),
+                    f"{self.emulator} isObserveModeEnabled did not return True",
+                )
+        tag_detected, _ = poll_and_transact(
+                self.pn532, command_apdus, response_apdus)
+        asserts.assert_false(
+                    tag_detected,
+                    "Reader detected emulator even though observe mode was enabled."
+                )
+
+        self.emulator.nfc_emulator.setObserveModeEnabled(False)
+
+    """Tests the autotransact functionality with exit frames.
+
+    Test Steps:
+        1. Start emulator activity and set up payment HCE Service.
+        2. Enable observe mode.
+        3. Poll with a broadcast frame, and attempt to transact.
+        4. Wait for observe mode to be reenabled.
+        5. Poll again and verify that the tag is not detected.
+
+        Verifies:
+        1. Observe mode is disabled and a transaction occurs.
+        2. Verify correct exit frame was used for transaction.
+        3. After the first transaction, verifies that observe mode is reenabled.
+        4. After observe mode is reenabled, verifies that the tag is not
+        detected.
+    """
+    def test_exit_frames_mask_match(self):
+        self._set_up_emulator(
+            "ff11", ["12345678", "ff.."], True,
+            start_emulator_fun=self.emulator.nfc_emulator.startExitFrameActivity,
+            service_list=[_PAYMENT_SERVICE_1],
+            expected_service=_PAYMENT_SERVICE_1,
+            is_payment=True,
+            payment_default_service=_PAYMENT_SERVICE_1
+        )
+        asserts.skip_if(
+                    not self.emulator.nfc_emulator.isObserveModeSupported(),
+                    f"{self.emulator} observe mode not supported",
+                )
+        asserts.assert_true(
+            self.emulator.nfc_emulator.setObserveModeEnabled(True),
+            f"{self.emulator} could not set observe mode",
+        )
+
+        command_apdus, response_apdus = get_apdus(self.emulator.nfc_emulator,
+                                                      _PAYMENT_SERVICE_1)
+        test_pass_handler = self.emulator.nfc_emulator.asyncWaitForTestPass(
+            'ExitFrameListenerSuccess'
+        )
+        tag_detected, transacted = poll_and_transact(
+                self.pn532, command_apdus, response_apdus, "ff11")
+        asserts.assert_true(
+            tag_detected, _FAILED_TAG_MSG
+        )
+        asserts.assert_true(transacted, _FAILED_TRANSACTION_MSG)
+        test_pass_handler.waitAndGet('ExitFrameListenerSuccess', _NFC_TIMEOUT_SEC)
+
+
+        time.sleep(_NFC_TIMEOUT_SEC)
+
+        # Poll again and see if tag is detected, observe mode should be enabled
+        # by now.
+        asserts.assert_true(
+                    self.emulator.nfc_emulator.isObserveModeEnabled(),
+                    f"{self.emulator} isObserveModeEnabled did not return True",
+                )
+        tag_detected, _ = poll_and_transact(
+                self.pn532, command_apdus, response_apdus)
+        asserts.assert_false(
+                    tag_detected,
+                    "Reader detected emulator even though observe mode was enabled."
+                )
+
+        self.emulator.nfc_emulator.setObserveModeEnabled(False)
+
+    """Tests the autotransact functionality with exit frames.
+
+    Test Steps:
+        1. Start emulator activity and set up payment HCE Service.
+        2. Enable observe mode.
+        3. Poll with a broadcast frame, and attempt to transact.
+        4. Wait for observe mode to be reenabled.
+        5. Poll again and verify that the tag is not detected.
+
+        Verifies:
+        1. Observe mode is disabled and a transaction occurs.
+        2. Verify correct exit frame was used for transaction.
+        3. After the first transaction, verifies that observe mode is reenabled.
+        4. After observe mode is reenabled, verifies that the tag is not
+        detected.
+    """
+    def test_exit_frames_mask_and_prefix_match(self):
+        self._set_up_emulator(
+            "ddfe1134", ["12345678", "dd..11.*", "ee.*", "ff.."], True,
+            start_emulator_fun=self.emulator.nfc_emulator.startExitFrameActivity,
+            service_list=[_PAYMENT_SERVICE_1],
+            expected_service=_PAYMENT_SERVICE_1,
+            is_payment=True,
+            payment_default_service=_PAYMENT_SERVICE_1
+        )
+        asserts.skip_if(
+                    not self.emulator.nfc_emulator.isObserveModeSupported(),
+                    f"{self.emulator} observe mode not supported",
+                )
+        asserts.assert_true(
+            self.emulator.nfc_emulator.setObserveModeEnabled(True),
+            f"{self.emulator} could not set observe mode",
+        )
+
+        command_apdus, response_apdus = get_apdus(self.emulator.nfc_emulator,
+                                                      _PAYMENT_SERVICE_1)
+        test_pass_handler = self.emulator.nfc_emulator.asyncWaitForTestPass(
+            'ExitFrameListenerSuccess'
+        )
+        tag_detected, transacted = poll_and_transact(
+                self.pn532, command_apdus, response_apdus, "ddfe1134")
+        asserts.assert_true(
+            tag_detected, _FAILED_TAG_MSG
+        )
+        asserts.assert_true(transacted, _FAILED_TRANSACTION_MSG)
+        test_pass_handler.waitAndGet('ExitFrameListenerSuccess', _NFC_TIMEOUT_SEC)
+
+
+        time.sleep(_NFC_TIMEOUT_SEC)
+
+        # Poll again and see if tag is detected, observe mode should be enabled
+        # by now.
+        asserts.assert_true(
+                    self.emulator.nfc_emulator.isObserveModeEnabled(),
+                    f"{self.emulator} isObserveModeEnabled did not return True",
+                )
+        tag_detected, _ = poll_and_transact(
+                self.pn532, command_apdus, response_apdus)
+        asserts.assert_false(
+                    tag_detected,
+                    "Reader detected emulator even though observe mode was enabled."
+                )
+
+        self.emulator.nfc_emulator.setObserveModeEnabled(False)
+
+    """Tests the autotransact functionality with exit frames.
+
+    Test Steps:
+        1. Start emulator activity and set up payment HCE Service.
+        2. Enable observe mode.
+        3. Poll with a broadcast frame, don't transact though.
+        4. Wait for observe mode to be reenabled.
+        5. Poll again and verify that the tag is not detected.
+
+        Verifies:
+        1. Observe mode is disabled.
+        2. Verify correct exit frame was used for transaction.
+        3. After the first transaction, verifies that observe mode is reenabled.
+        4. After observe mode is reenabled, verifies that the tag is not
+        detected.
+    """
+    def test_exit_frames_no_transaction_observe_mode_reenabled(self):
+        self._set_up_emulator(
+            "12345678", ["12345678", "aaaa"], False,
+            start_emulator_fun=self.emulator.nfc_emulator.startExitFrameActivity,
+            service_list=[_PAYMENT_SERVICE_1],
+            expected_service=_PAYMENT_SERVICE_1,
+            is_payment=True,
+            payment_default_service=_PAYMENT_SERVICE_1
+        )
+        asserts.skip_if(
+                    not self.emulator.nfc_emulator.isObserveModeSupported(),
+                    f"{self.emulator} observe mode not supported",
+                )
+        asserts.assert_true(
+            self.emulator.nfc_emulator.setObserveModeEnabled(True),
+            f"{self.emulator} could not set observe mode",
+        )
+
+        command_apdus, response_apdus = get_apdus(self.emulator.nfc_emulator,
+                                                      _PAYMENT_SERVICE_1)
+        test_pass_handler = self.emulator.nfc_emulator.asyncWaitForTestPass(
+            'ExitFrameListenerSuccess'
+        )
+        tag_detected, transacted = poll_and_transact(
+                self.pn532, [], [], "12345678")
+        asserts.assert_true(
+            tag_detected, _FAILED_TAG_MSG
+        )
+        test_pass_handler.waitAndGet('ExitFrameListenerSuccess', _NFC_TIMEOUT_SEC)
+
+
+        time.sleep(_NFC_TIMEOUT_SEC)
+
+        # Poll again and see if tag is detected, observe mode should be enabled
+        # by now.
+        asserts.assert_true(
+                    self.emulator.nfc_emulator.isObserveModeEnabled(),
+                    f"{self.emulator} isObserveModeEnabled did not return True",
+                )
+        tag_detected, _ = poll_and_transact(
+                self.pn532, [], [])
+        asserts.assert_false(
+                    tag_detected,
+                    "Reader detected emulator even though observe mode was enabled."
+                )
+
+        self.emulator.nfc_emulator.setObserveModeEnabled(False)
+
+    def teardown_test(self):
+        if hasattr(self, 'emulator') and hasattr(self.emulator, 'nfc_emulator'):
+            self.emulator.nfc_emulator.closeActivity()
+            self.emulator.nfc_emulator.logInfo(
+                "*** TEST END: " + self.current_test_info.name + " ***")
+        self.pn532.reset_buffers()
+        self.pn532.mute()
+        param_list = [[self.emulator]]
+        utils.concurrent_exec(lambda d: d.services.create_output_excerpts_all(
+            self.current_test_info),
+                              param_list=param_list,
+                              raise_on_exception=True)
 
 if __name__ == '__main__':
     # Take test args
diff --git a/NfcNci/tests/unit/src/com/android/nfc/NfcDispatcherTest.java b/NfcNci/tests/unit/src/com/android/nfc/NfcDispatcherTest.java
index a347930..9b98ad8 100644
--- a/NfcNci/tests/unit/src/com/android/nfc/NfcDispatcherTest.java
+++ b/NfcNci/tests/unit/src/com/android/nfc/NfcDispatcherTest.java
@@ -116,6 +116,8 @@
     ForegroundUtils mForegroundUtils;
     @Mock
     AtomicBoolean mAtomicBoolean;
+    @Mock
+    DeviceConfigFacade mDeviceConfigFacade;
 
     @Before
     public void setUp() throws PackageManager.NameNotFoundException {
@@ -152,7 +154,7 @@
         when(mNfcInjector.createAtomicBoolean()).thenReturn(mAtomicBoolean);
 
         mNfcDispatcher = new NfcDispatcher(mockContext,
-                new HandoverDataParser(), mNfcInjector, true);
+                new HandoverDataParser(), mNfcInjector, true, mDeviceConfigFacade);
         mLooper.dispatchAll();
     }
 
diff --git a/NfcNci/tests/unit/src/com/android/nfc/NfcProprietaryCapsTest.java b/NfcNci/tests/unit/src/com/android/nfc/NfcProprietaryCapsTest.java
index dcfb92d..82c0d4f 100644
--- a/NfcNci/tests/unit/src/com/android/nfc/NfcProprietaryCapsTest.java
+++ b/NfcNci/tests/unit/src/com/android/nfc/NfcProprietaryCapsTest.java
@@ -133,7 +133,8 @@
                 "passiveObserveMode=SUPPORT_WITHOUT_RF_DEACTIVATION, " +
                 "isPollingFrameNotificationSupported=true, " +
                 "isPowerSavingModeSupported=false, " +
-                "isAutotransactPollingLoopFilterSupported=true}";
+                "isAutotransactPollingLoopFilterSupported=true, " +
+                "mIsReaderModeAnnotationSupported=false}";
 
         assertEquals(expected, caps.toString());
     }
diff --git a/NfcNci/tests/unit/src/com/android/nfc/NfcReaderConflictOccurredTest.java b/NfcNci/tests/unit/src/com/android/nfc/NfcReaderConflictOccurredTest.java
index 8f1218e..d8a6e4d 100644
--- a/NfcNci/tests/unit/src/com/android/nfc/NfcReaderConflictOccurredTest.java
+++ b/NfcNci/tests/unit/src/com/android/nfc/NfcReaderConflictOccurredTest.java
@@ -61,6 +61,7 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
 import org.mockito.MockitoSession;
 import org.mockito.quality.Strictness;
 
@@ -70,6 +71,8 @@
     private static final String TAG = NfcReaderConflictOccurredTest.class.getSimpleName();
     private NfcInjector mNfcInjector;
     AtomicBoolean mAtomicBoolean;
+    @Mock
+    DeviceConfigFacade mDeviceConfigFacade;
 
     private MockitoSession mStaticMockSession;
     private NfcDispatcher mNfcDispatcher;
@@ -80,7 +83,8 @@
                 .mockStatic(NfcStatsLog.class)
                 .strictness(Strictness.LENIENT)
                 .startMocking();
-	Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        MockitoAnnotations.initMocks(this);
+        Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
         PackageManager mockPackageManager = Mockito.mock(PackageManager.class);
         // multiple resolveInfos for Tag
         when(mockPackageManager.queryIntentActivitiesAsUser(
@@ -128,7 +132,8 @@
         when(mNfcInjector.createAtomicBoolean()).thenReturn(mAtomicBoolean);
         InstrumentationRegistry.getInstrumentation().runOnMainSync(
               () -> mNfcDispatcher = new NfcDispatcher(
-                      mockContext, new HandoverDataParser(), mNfcInjector, false));
+                      mockContext, new HandoverDataParser(), mNfcInjector, false,
+                      mDeviceConfigFacade));
         Assert.assertNotNull(mNfcDispatcher);
     }
 
diff --git a/NfcNci/tests/unit/src/com/android/nfc/NfcServiceTest.java b/NfcNci/tests/unit/src/com/android/nfc/NfcServiceTest.java
index 69b51cd..98b53f1 100644
--- a/NfcNci/tests/unit/src/com/android/nfc/NfcServiceTest.java
+++ b/NfcNci/tests/unit/src/com/android/nfc/NfcServiceTest.java
@@ -254,7 +254,7 @@
         when(mApplication.getSystemService(KeyguardManager.class)).thenReturn(mKeyguardManager);
         when(mApplication.getSystemService(AlarmManager.class)).thenReturn(mAlarmManager);
         when(mApplication.getPackageManager()).thenReturn(mPackageManager);
-        when(mResources.getBoolean(R.bool.check_display_state_for_screen_state)).thenReturn(true);
+        when(mDeviceConfigFacade.getCheckDisplayStateForScreenState()).thenReturn(true);
         when(mApplication.getResources()).thenReturn(mResources);
         when(mApplication.createContextAsUser(any(), anyInt())).thenReturn(mApplication);
         when(mApplication.getContentResolver()).thenReturn(mContentResolver);
@@ -264,7 +264,7 @@
         when(mUserManager.getUserRestrictions()).thenReturn(mUserRestrictions);
         when(mResources.getStringArray(R.array.nfc_allow_list)).thenReturn(new String[0]);
         when(mResources.getBoolean(R.bool.tag_intent_app_pref_supported)).thenReturn(true);
-        when(mResources.getBoolean(R.bool.nfcc_always_on_allowed)).thenReturn(true);
+        when(mDeviceConfigFacade.getNfccAlwaysOnAllowed()).thenReturn(true);
         when(mPreferences.edit()).thenReturn(mPreferencesEditor);
         when(mPowerManager.newWakeLock(anyInt(), anyString()))
                 .thenReturn(mock(PowerManager.WakeLock.class));
@@ -344,7 +344,7 @@
 
     @Test
     public void testEnable_WheOemExtensionEnabledAndNotInitialized() throws Exception {
-        when(mResources.getBoolean(R.bool.enable_oem_extension)).thenReturn(true);
+        when(mDeviceConfigFacade.getEnableOemExtension()).thenReturn(true);
         when(NfcProperties.initialized()).thenReturn(Optional.of(Boolean.FALSE));
 
         createNfcService();
@@ -371,7 +371,7 @@
 
     @Test
     public void testBootupWithNfcOn_WhenOemExtensionEnabled() throws Exception {
-        when(mResources.getBoolean(R.bool.enable_oem_extension)).thenReturn(true);
+        when(mDeviceConfigFacade.getEnableOemExtension()).thenReturn(true);
         createNfcService();
 
         verifyNoMoreInteractions(mDeviceHost);
diff --git a/NfcNci/tests/unit/src/com/android/nfc/cardemulation/CardEmulationManagerTest.java b/NfcNci/tests/unit/src/com/android/nfc/cardemulation/CardEmulationManagerTest.java
index bfe1f62..fbf52a7 100644
--- a/NfcNci/tests/unit/src/com/android/nfc/cardemulation/CardEmulationManagerTest.java
+++ b/NfcNci/tests/unit/src/com/android/nfc/cardemulation/CardEmulationManagerTest.java
@@ -71,6 +71,7 @@
 import android.util.proto.ProtoOutputStream;
 
 import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.nfc.DeviceConfigFacade;
 import com.android.nfc.ExitFrame;
 import com.android.nfc.ForegroundUtils;
 import com.android.nfc.NfcEventLog;
@@ -78,6 +79,7 @@
 import com.android.nfc.NfcPermissions;
 import com.android.nfc.NfcService;
 import com.android.nfc.R;
+import com.android.nfc.cardemulation.util.StatsdUtils;
 import com.android.nfc.cardemulation.util.TelephonyUtils;
 import com.android.nfc.flags.Flags;
 
@@ -185,6 +187,10 @@
     private NfcEventLog mNfcEventLog;
     @Mock
     private PreferredSubscriptionService mPreferredSubscriptionService;
+    @Mock
+    private StatsdUtils mStatsdUtils;
+    @Mock
+    private DeviceConfigFacade mDeviceConfigFacade;
     @Captor
     private ArgumentCaptor<List<PollingFrame>> mPollingLoopFrameCaptor;
     @Captor
@@ -220,7 +226,7 @@
         when(mContext.createContextAsUser(any(), anyInt())).thenReturn(mContext);
         when(mContext.getResources()).thenReturn(mResources);
         when(mContext.getSystemService(eq(UserManager.class))).thenReturn(mUserManager);
-        when(mResources.getBoolean(R.bool.indicate_user_activity_for_hce)).thenReturn(true);
+        when(mDeviceConfigFacade.getIndicateUserActivityForHce()).thenReturn(true);
         when(android.nfc.Flags.nfcEventListener()).thenReturn(true);
         when(android.nfc.Flags.enableCardEmulationEuicc()).thenReturn(true);
         mCardEmulationManager = createInstanceWithMockParams();
@@ -439,6 +445,9 @@
     @Test
     public void testOnServicesUpdated_walletEnabledPollingLoopEnabled() {
         when(mWalletRoleObserver.isWalletRoleFeatureEnabled()).thenReturn(true);
+        when(Flags.exitFrames()).thenReturn(true);
+        when(mNfcService.isFirmwareExitFramesSupported()).thenReturn(true);
+        when(mNfcService.getNumberOfFirmwareExitFramesSupported()).thenReturn(5);
 
         mCardEmulationManager.onServicesUpdated(USER_ID, UPDATED_SERVICES, false);
 
@@ -447,6 +456,7 @@
         verify(mPreferredServices).onServicesUpdated();
         verify(mHostEmulationManager)
                 .updatePollingLoopFilters(eq(USER_ID), mServiceListCaptor.capture());
+        verify(mNfcService).setFirmwareExitFrameTable(any(), anyInt());
         verify(mNfcService).onPreferredPaymentChanged(eq(NfcAdapter.PREFERRED_PAYMENT_UPDATED));
         assertEquals(UPDATED_SERVICES, mServiceListCaptor.getAllValues().getFirst());
         assertEquals(UPDATED_SERVICES, mServiceListCaptor.getAllValues().getLast());
@@ -1500,7 +1510,7 @@
     @Test
     public void testCardEmulationSetServiceEnabledForCategoryOther_resourceTrue()
             throws RemoteException {
-        when(mResources.getBoolean(R.bool.enable_service_for_category_other)).thenReturn(true);
+        when(mDeviceConfigFacade.getEnableServiceOther()).thenReturn(true);
         when(mRegisteredServicesCache.registerOtherForService(anyInt(), any(), anyBoolean()))
                 .thenReturn(SET_SERVICE_ENABLED_STATUS_OK);
 
@@ -1523,7 +1533,7 @@
     @Test
     public void testCardEmulationSetServiceEnabledForCategoryOther_resourceFalse()
             throws RemoteException {
-        when(mResources.getBoolean(R.bool.enable_service_for_category_other)).thenReturn(false);
+        when(mDeviceConfigFacade.getEnableServiceOther()).thenReturn(false);
         when(mRegisteredServicesCache.registerOtherForService(anyInt(), any(), anyBoolean()))
                 .thenReturn(SET_SERVICE_ENABLED_STATUS_OK);
 
@@ -2265,7 +2275,9 @@
                 mRoutingOptionManager,
                 mPowerManager,
                 mNfcEventLog,
-                mPreferredSubscriptionService);
+                mPreferredSubscriptionService,
+                mStatsdUtils,
+                mDeviceConfigFacade);
     }
 
     @Test
@@ -2514,8 +2526,7 @@
         assertThat(iNfcCardEmulation).isNotNull();
         ComponentName componentName = ComponentName
                 .unflattenFromString("com.android.test.component/.Component");
-        when(mResources.getBoolean(R.bool.enable_service_for_category_other))
-                .thenReturn(true);
+        when(mDeviceConfigFacade.getEnableServiceOther()).thenReturn(true);
         when(mRegisteredServicesCache.registerOtherForService(1,
                 componentName, true)).thenReturn(1);
         int result = iNfcCardEmulation
@@ -2713,167 +2724,6 @@
     }
 
     @Test
-    public void registerPollingLoopFilterForService_roleService_setsExitFrames() throws Exception {
-        when(Flags.exitFrames()).thenReturn(true);
-        when(mNfcService.isFirmwareExitFramesSupported()).thenReturn(true);
-        when(mNfcService.getNumberOfFirmwareExitFramesSupported()).thenReturn(5);
-        when(mRegisteredServicesCache.hasService(eq(USER_ID), any())).thenReturn(true);
-        when(mRegisteredServicesCache.registerPollingLoopFilterForService(anyInt(), anyInt(), any(),
-                any(), anyBoolean())).thenReturn(true);
-        when(mRegisteredAidCache.isDefaultOrAssociatedWalletPackage(
-                eq(WALLET_PAYMENT_SERVICE.getPackageName()), eq(USER_ID))).thenReturn(true);
-        when(mRegisteredServicesCache.getServices(USER_ID)).thenReturn(List.of());
-
-        mCardEmulationManager.getNfcCardEmulationInterface().registerPollingLoopFilterForService(
-                USER_ID, WALLET_PAYMENT_SERVICE, "aa", true);
-
-        verify(mNfcService).setFirmwareExitFrameTable(any(), anyInt());
-    }
-
-    @Test
-    public void registerPollingLoopFilterForService_notRoleService_doesNotSetExitFrames()
-            throws Exception {
-        when(Flags.exitFrames()).thenReturn(true);
-        when(mNfcService.isFirmwareExitFramesSupported()).thenReturn(true);
-        when(mNfcService.getNumberOfFirmwareExitFramesSupported()).thenReturn(5);
-        when(mRegisteredServicesCache.hasService(eq(USER_ID), any())).thenReturn(true);
-        when(mRegisteredServicesCache.registerPollingLoopFilterForService(anyInt(), anyInt(), any(),
-                any(), anyBoolean())).thenReturn(true);
-        when(mRegisteredAidCache.isDefaultOrAssociatedWalletPackage(
-                eq("com.android.test"), eq(USER_ID))).thenReturn(false);
-        when(mRegisteredServicesCache.getServices(USER_ID)).thenReturn(List.of());
-
-        mCardEmulationManager.getNfcCardEmulationInterface().registerPollingLoopFilterForService(
-                USER_ID, new ComponentName("com.android.test", "com.android.test.Service"), "aa",
-                true);
-
-        verify(mNfcService, never()).setFirmwareExitFrameTable(any(), anyInt());
-    }
-
-    @Test
-    public void removePollingLoopFilterForService_roleService_setsExitFrames()
-            throws Exception {
-        when(Flags.exitFrames()).thenReturn(true);
-        when(mNfcService.isFirmwareExitFramesSupported()).thenReturn(true);
-        when(mNfcService.getNumberOfFirmwareExitFramesSupported()).thenReturn(5);
-        when(mRegisteredServicesCache.hasService(eq(USER_ID), any())).thenReturn(true);
-        when(mRegisteredServicesCache.removePollingLoopFilterForService(anyInt(), anyInt(), any(),
-                any())).thenReturn(true);
-        when(mRegisteredAidCache.isDefaultOrAssociatedWalletPackage(
-                eq(WALLET_PAYMENT_SERVICE.getPackageName()), eq(USER_ID))).thenReturn(true);
-        when(mRegisteredServicesCache.getServices(USER_ID)).thenReturn(List.of());
-
-        mCardEmulationManager.getNfcCardEmulationInterface().removePollingLoopFilterForService(
-                USER_ID, WALLET_PAYMENT_SERVICE, "aa");
-
-        verify(mNfcService).setFirmwareExitFrameTable(any(), anyInt());
-    }
-
-    @Test
-    public void removePollingLoopFilterForService_notRoleService_doesNotSetExitFrames()
-            throws Exception {
-        when(Flags.exitFrames()).thenReturn(true);
-        when(mNfcService.isFirmwareExitFramesSupported()).thenReturn(true);
-        when(mNfcService.getNumberOfFirmwareExitFramesSupported()).thenReturn(5);
-        when(mRegisteredServicesCache.hasService(eq(USER_ID), any())).thenReturn(true);
-        when(mRegisteredServicesCache.removePollingLoopFilterForService(anyInt(), anyInt(), any(),
-                any())).thenReturn(true);
-        when(mRegisteredAidCache.isDefaultOrAssociatedWalletPackage(
-                eq("com.android.test"), eq(USER_ID))).thenReturn(false);
-        when(mRegisteredServicesCache.getServices(USER_ID)).thenReturn(List.of());
-
-        mCardEmulationManager.getNfcCardEmulationInterface().removePollingLoopFilterForService(
-                USER_ID, new ComponentName("com.android.test", "com.android.test.Service"), "aa");
-
-        verify(mNfcService, never()).setFirmwareExitFrameTable(any(), anyInt());
-    }
-
-    @Test
-    public void registerPollingLoopPatternFilterForService_roleService_setsExitFrames()
-            throws Exception {
-        when(Flags.exitFrames()).thenReturn(true);
-        when(mNfcService.isFirmwareExitFramesSupported()).thenReturn(true);
-        when(mNfcService.getNumberOfFirmwareExitFramesSupported()).thenReturn(5);
-        when(mRegisteredServicesCache.hasService(eq(USER_ID), any())).thenReturn(true);
-        when(mRegisteredServicesCache.registerPollingLoopPatternFilterForService(anyInt(), anyInt(),
-                any(), any(), anyBoolean())).thenReturn(true);
-        when(mRegisteredAidCache.isDefaultOrAssociatedWalletPackage(
-                eq(WALLET_PAYMENT_SERVICE.getPackageName()), eq(USER_ID))).thenReturn(true);
-        when(mRegisteredServicesCache.getServices(USER_ID)).thenReturn(List.of());
-
-        mCardEmulationManager.getNfcCardEmulationInterface()
-                .registerPollingLoopPatternFilterForService(
-                        USER_ID, WALLET_PAYMENT_SERVICE, "aa", true);
-
-        verify(mNfcService).setFirmwareExitFrameTable(any(), anyInt());
-    }
-
-    @Test
-    public void registerPollingLoopPatternFilterForService_notRoleService_doesNotSetExitFrames()
-            throws Exception {
-        when(Flags.exitFrames()).thenReturn(true);
-        when(mNfcService.isFirmwareExitFramesSupported()).thenReturn(true);
-        when(mNfcService.getNumberOfFirmwareExitFramesSupported()).thenReturn(5);
-        when(mRegisteredServicesCache.hasService(eq(USER_ID), any())).thenReturn(true);
-        when(mRegisteredServicesCache.registerPollingLoopPatternFilterForService(anyInt(), anyInt(),
-                any(), any(), anyBoolean())).thenReturn(true);
-        when(mRegisteredAidCache.isDefaultOrAssociatedWalletPackage(
-                eq("com.android.test"), eq(USER_ID))).thenReturn(false);
-        when(mRegisteredServicesCache.getServices(USER_ID)).thenReturn(List.of());
-
-        mCardEmulationManager.getNfcCardEmulationInterface()
-                .registerPollingLoopPatternFilterForService(
-                        USER_ID,
-                        new ComponentName("com.android.test", "com.android.test.Service"),
-                        "aa",
-                        true);
-
-        verify(mNfcService, never()).setFirmwareExitFrameTable(any(), anyInt());
-    }
-
-    @Test
-    public void removePollingLoopPatternFilterForService_roleService_setsExitFrames()
-            throws Exception {
-        when(Flags.exitFrames()).thenReturn(true);
-        when(mNfcService.isFirmwareExitFramesSupported()).thenReturn(true);
-        when(mNfcService.getNumberOfFirmwareExitFramesSupported()).thenReturn(5);
-        when(mRegisteredServicesCache.hasService(eq(USER_ID), any())).thenReturn(true);
-        when(mRegisteredServicesCache.removePollingLoopPatternFilterForService(anyInt(), anyInt(),
-                any(), any())).thenReturn(true);
-        when(mRegisteredAidCache.isDefaultOrAssociatedWalletPackage(
-                eq(WALLET_PAYMENT_SERVICE.getPackageName()), eq(USER_ID))).thenReturn(true);
-        when(mRegisteredServicesCache.getServices(USER_ID)).thenReturn(List.of());
-
-        mCardEmulationManager.getNfcCardEmulationInterface()
-                .removePollingLoopPatternFilterForService(
-                        USER_ID, WALLET_PAYMENT_SERVICE, "aa");
-
-        verify(mNfcService).setFirmwareExitFrameTable(any(), anyInt());
-    }
-
-    @Test
-    public void removePollingLoopPatternFilterForService_notRoleService_doesNotSetExitFrames()
-            throws Exception {
-        when(Flags.exitFrames()).thenReturn(true);
-        when(mNfcService.isFirmwareExitFramesSupported()).thenReturn(true);
-        when(mNfcService.getNumberOfFirmwareExitFramesSupported()).thenReturn(5);
-        when(mRegisteredServicesCache.hasService(eq(USER_ID), any())).thenReturn(true);
-        when(mRegisteredServicesCache.removePollingLoopPatternFilterForService(anyInt(), anyInt(),
-                any(), any())).thenReturn(true);
-        when(mRegisteredAidCache.isDefaultOrAssociatedWalletPackage(
-                eq("com.android.test"), eq(USER_ID))).thenReturn(false);
-        when(mRegisteredServicesCache.getServices(USER_ID)).thenReturn(List.of());
-
-        mCardEmulationManager.getNfcCardEmulationInterface()
-                .removePollingLoopPatternFilterForService(
-                        USER_ID,
-                        new ComponentName("com.android.test", "com.android.test.Service"),
-                        "aa");
-
-        verify(mNfcService, never()).setFirmwareExitFrameTable(any(), anyInt());
-    }
-
-    @Test
     public void testDump() {
         FileDescriptor fd = mock(FileDescriptor.class);
         PrintWriter pw = mock(PrintWriter.class);
@@ -3367,4 +3217,20 @@
         verify(mContext).createPackageContextAsUser("android", 0, userHandle);
         verify(pm, never()).getApplicationInfo(anyString(), eq(0));
     }
+
+    @Test
+    public void testOnObserveModeDisabledInFirmware() {
+        PollingFrame exitFrame = new PollingFrame(
+                PollingFrame.POLLING_LOOP_TYPE_UNKNOWN,
+                HexFormat.of().parseHex("42123456"),
+                0,
+                0,
+                true);
+
+        mCardEmulationManager.onObserveModeDisabledInFirmware(exitFrame);
+
+        verify(mHostEmulationManager).onObserveModeDisabledInFirmware(exitFrame);
+        verify(mStatsdUtils).logAutoTransactReported(StatsdUtils.PROCESSOR_NFCC,
+            exitFrame.getData());
+    }
 }
diff --git a/NfcNci/tests/unit/src/com/android/nfc/handover/BluetoothPeripheralHandoverTest.java b/NfcNci/tests/unit/src/com/android/nfc/handover/BluetoothPeripheralHandoverTest.java
index 1d88fe3..d8aeb8a 100644
--- a/NfcNci/tests/unit/src/com/android/nfc/handover/BluetoothPeripheralHandoverTest.java
+++ b/NfcNci/tests/unit/src/com/android/nfc/handover/BluetoothPeripheralHandoverTest.java
@@ -69,6 +69,8 @@
 import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.nfc.DeviceConfigFacade;
+import com.android.nfc.NfcInjector;
 
 import org.junit.After;
 import org.junit.Before;
@@ -114,18 +116,25 @@
     Intent mockIntent;
     @Mock
     BluetoothA2dp mockA2dp;
+    @Mock
+    DeviceConfigFacade mDeviceConfigFacade;
     private MockitoSession mStaticMockSession;
     BluetoothPeripheralHandover bluetoothPeripheralHandover;
 
     @Before
     public void setUp() {
-        mStaticMockSession = ExtendedMockito.mockitoSession().mockStatic(
-                Settings.Global.class).mockStatic(Toast.class).strictness(
-                Strictness.LENIENT).startMocking();
+        mStaticMockSession = ExtendedMockito.mockitoSession()
+                .mockStatic(Settings.Global.class)
+                .mockStatic(Toast.class)
+                .mockStatic(NfcInjector.class)
+                .strictness(Strictness.LENIENT).startMocking();
         MockitoAnnotations.initMocks(this);
         when(mockContext.getSystemService(AudioManager.class)).thenReturn(mockAudioManager);
         when(mockContext.getContentResolver()).thenReturn(mockContentResolver);
         when(mockContext.getResources()).thenReturn(mockResources);
+        NfcInjector nfcInjector = mock(NfcInjector.class);
+        when(NfcInjector.getInstance()).thenReturn(nfcInjector);
+        when(nfcInjector.getDeviceConfigFacade()).thenReturn(mDeviceConfigFacade);
         when(Toast.makeText(any(), anyString(), anyInt())).thenReturn(mockToast);
         bluetoothPeripheralHandover = createBluetoothPerHandOvrInstance(
                 BluetoothDevice.TRANSPORT_LE);
diff --git a/NfcNci/testutils/src/com/android/nfc/emulator/ExitFrameEmulatorActivity.java b/NfcNci/testutils/src/com/android/nfc/emulator/ExitFrameEmulatorActivity.java
index d66ebf3..143cae2 100644
--- a/NfcNci/testutils/src/com/android/nfc/emulator/ExitFrameEmulatorActivity.java
+++ b/NfcNci/testutils/src/com/android/nfc/emulator/ExitFrameEmulatorActivity.java
@@ -22,32 +22,44 @@
 import android.util.Log;
 
 import com.android.nfc.service.PaymentService1;
-import com.android.nfc.service.TransportService1;
 
+import java.util.ArrayList;
 import java.util.HexFormat;
+import java.util.List;
+import java.util.regex.Pattern;
 
 public class ExitFrameEmulatorActivity extends BaseEmulatorActivity {
     private static final String TAG = "ExitFrameEmulatorActivity";
 
     public static final String EXIT_FRAME_KEY = "EXIT_FRAME";
+    public static final String REGISTER_PATTERNS_KEY = "REGISTER_PATTERNS";
+    public static final String WAIT_FOR_TRANSACTION_KEY = "WAIT_FOR_TRANSACTION";
 
     private String mReceivedExitFrame = null;
     private String mIntendedExitFrameData = null;
+    private final List<String> mRegisteredPatterns = new ArrayList<>();
+    private boolean mWaitForTransaction = true;
+    private ComponentName mServiceName = null;
 
     private final CardEmulation.NfcEventCallback mEventListener =
             new CardEmulation.NfcEventCallback() {
-        public void onObserveModeDisabledInFirmware(PollingFrame exitFrame) {
-            if (exitFrame != null) {
-                mReceivedExitFrame = HexFormat.of().formatHex(exitFrame.getData());
-                Log.d(TAG, "Received exit frame: " + mReceivedExitFrame);
-            }
-        }
-    };
+                public void onObserveModeDisabledInFirmware(PollingFrame exitFrame) {
+                    if (exitFrame != null) {
+                        mReceivedExitFrame = HexFormat.of().formatHex(exitFrame.getData());
+                        Log.d(TAG, "Received exit frame: " + mReceivedExitFrame);
+                    }
+
+                    if (!mWaitForTransaction) {
+                        verifyExitFrameAndPassTest();
+                    }
+                }
+            };
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         mIntendedExitFrameData = getIntent().getStringExtra(EXIT_FRAME_KEY);
+        mWaitForTransaction = getIntent().getBooleanExtra(WAIT_FOR_TRANSACTION_KEY, true);
 
         setupServices(PaymentService1.COMPONENT);
         makeDefaultWalletRoleHolder();
@@ -55,29 +67,38 @@
 
     public void onResume() {
         super.onResume();
-        ComponentName serviceName =
+        mServiceName =
                 new ComponentName(this.getApplicationContext(), PaymentService1.class);
-        mCardEmulation.setPreferredService(this, serviceName);
+        mCardEmulation.setPreferredService(this, mServiceName);
         waitForPreferredService();
 
         mReceivedExitFrame = null;
         registerEventListener(mEventListener);
+
+        if (getIntent().hasExtra(REGISTER_PATTERNS_KEY)) {
+            getIntent().getStringArrayListExtra(REGISTER_PATTERNS_KEY).forEach(
+                    plpf -> {
+                        mCardEmulation.registerPollingLoopPatternFilterForService(mServiceName,
+                                plpf, true);
+                        mRegisteredPatterns.add(plpf);
+                    }
+            );
+        }
     }
 
     @Override
     public void onPause() {
         super.onPause();
 
+        mRegisteredPatterns.forEach(
+                plpf -> mCardEmulation.removePollingLoopPatternFilterForService(mServiceName,
+                        plpf));
         mCardEmulation.unsetPreferredService(this);
     }
 
     @Override
     public void onApduSequenceComplete(ComponentName component, long duration) {
-        boolean success = mIntendedExitFrameData.equals(mReceivedExitFrame);
-
-        if (success) {
-            setTestPassed();
-        }
+        verifyExitFrameAndPassTest();
     }
 
     @Override
@@ -87,7 +108,20 @@
     }
 
     @Override
-    public ComponentName getPreferredServiceComponent(){
-        return TransportService1.COMPONENT;
+    public ComponentName getPreferredServiceComponent() {
+        return PaymentService1.COMPONENT;
+    }
+
+    private void verifyExitFrameAndPassTest() {
+        if (mIntendedExitFrameData == null || mReceivedExitFrame == null) {
+            return;
+        }
+
+        boolean success =
+                Pattern.compile(mIntendedExitFrameData).matcher(mReceivedExitFrame).matches();
+
+        if (success) {
+            setTestPassed();
+        }
     }
 }
\ No newline at end of file
diff --git a/NfcNci/testutils/src/com/android/nfc/emulator/NfcEmulatorDeviceSnippet.java b/NfcNci/testutils/src/com/android/nfc/emulator/NfcEmulatorDeviceSnippet.java
index dba810d..832c803 100644
--- a/NfcNci/testutils/src/com/android/nfc/emulator/NfcEmulatorDeviceSnippet.java
+++ b/NfcNci/testutils/src/com/android/nfc/emulator/NfcEmulatorDeviceSnippet.java
@@ -29,6 +29,7 @@
 import androidx.test.uiautomator.UiScrollable;
 import androidx.test.uiautomator.UiSelector;
 
+import com.android.compatibility.common.util.CommonTestUtils;
 import com.android.nfc.service.AccessServiceTurnObserveModeOnProcessApdu;
 import com.android.nfc.utils.CommandApdu;
 import com.android.nfc.utils.HceUtils;
@@ -413,7 +414,8 @@
     }
 
     @Rpc(description = "Opens the Exit Frame Activity")
-    public void startExitFrameActivity(String intendedExitFrame) {
+    public void startExitFrameActivity(String intendedExitFrame, String[] plpfs,
+            boolean waitForTransaction) {
         Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
 
         Intent intent = new Intent(Intent.ACTION_MAIN);
@@ -421,6 +423,9 @@
         intent.setClassName(instrumentation.getTargetContext(),
                 ExitFrameEmulatorActivity.class.getName());
         intent.putExtra(ExitFrameEmulatorActivity.EXIT_FRAME_KEY, intendedExitFrame);
+        intent.putStringArrayListExtra(ExitFrameEmulatorActivity.REGISTER_PATTERNS_KEY,
+                new ArrayList<>(Arrays.asList(plpfs)));
+        intent.putExtra(ExitFrameEmulatorActivity.WAIT_FOR_TRANSACTION_KEY, waitForTransaction);
 
         mActivity = (ExitFrameEmulatorActivity) instrumentation.startActivitySync(intent);
     }
@@ -559,6 +564,14 @@
     public void closeActivity() {
         if (mActivity != null) {
             mActivity.finish();
+            try {
+                CommonTestUtils.waitUntil(
+                        "Activity didn't finish in 5 seconds",
+                        5,
+                        () -> mActivity.isDestroyed()
+                );
+            } catch (InterruptedException | AssertionError e) {
+            }
         }
     }
 
diff --git a/libnfc-nci/src/adaptation/NfcAdaptation.cc b/libnfc-nci/src/adaptation/NfcAdaptation.cc
index 62b7df0..2cb10dc 100644
--- a/libnfc-nci/src/adaptation/NfcAdaptation.cc
+++ b/libnfc-nci/src/adaptation/NfcAdaptation.cc
@@ -1005,6 +1005,9 @@
 void NfcAdaptation::HalClose() {
   const char* func = "NfcAdaptation::HalClose";
   LOG(VERBOSE) << StringPrintf("%s", func);
+  if (sVndExtnsPresent) {
+    sNfcVendorExtn->processEvent(HANDLE_NFC_HAL_CLOSE, HAL_NFC_STATUS_OK);
+  }
   if (mAidlHal != nullptr) {
     mAidlHal->close(NfcCloseType::DISABLE);
   } else if (mHal != nullptr) {
@@ -1056,6 +1059,10 @@
                                        uint8_t* p_core_init_rsp_params) {
   const char* func = "NfcAdaptation::HalCoreInitialized";
   LOG(VERBOSE) << StringPrintf("%s", func);
+  if (sVndExtnsPresent) {
+    sNfcVendorExtn->processEvent(HANDLE_NFC_HAL_CORE_INITIALIZE,
+                                 HAL_NFC_STATUS_OK);
+  }
   if (mAidlHal != nullptr) {
     // AIDL coreInitialized doesn't send data to HAL.
     mAidlHal->coreInitialized();
@@ -1140,6 +1147,9 @@
 void NfcAdaptation::HalPowerCycle() {
   const char* func = "NfcAdaptation::HalPowerCycle";
   LOG(VERBOSE) << StringPrintf("%s", func);
+  if (sVndExtnsPresent) {
+    sNfcVendorExtn->processEvent(HANDLE_NFC_HAL_POWER_CYCLE, HAL_NFC_STATUS_OK);
+  }
   if (mAidlHal != nullptr) {
     mAidlHal->powerCycle();
   } else if (mHal != nullptr) {
@@ -1159,7 +1169,9 @@
 uint8_t NfcAdaptation::HalGetMaxNfcee() {
   const char* func = "NfcAdaptation::HalGetMaxNfcee";
   LOG(VERBOSE) << StringPrintf("%s", func);
-
+  if (sVndExtnsPresent) {
+    sNfcVendorExtn->processEvent(HANDLE_NFC_GET_MAX_NFCEE, HAL_NFC_STATUS_OK);
+  }
   return nfa_ee_max_ee_cfg;
 }
 
diff --git a/libnfc-nci/src/gki/common/gki_buffer.cc b/libnfc-nci/src/gki/common/gki_buffer.cc
index 5a69ca6..b461382 100644
--- a/libnfc-nci/src/gki/common/gki_buffer.cc
+++ b/libnfc-nci/src/gki/common/gki_buffer.cc
@@ -120,7 +120,10 @@
 **
 *******************************************************************************/
 void gki_buffer_init(void) {
-  uint8_t i, tt, mb;
+#if (GKI_NUM_FIXED_BUF_POOLS > 0)
+  uint8_t i;
+#endif
+  uint8_t tt, mb;
   tGKI_COM_CB* p_cb = &gki_cb.com;
 
   /* Initialize mailboxes */
@@ -211,10 +214,12 @@
   gki_init_free_queue(15, GKI_BUF15_SIZE, GKI_BUF15_MAX, p_cb->bufpool15);
 #endif
 
-  /* add pools to the pool_list which is arranged in the order of size */
+/* add pools to the pool_list which is arranged in the order of size */
+#if (GKI_NUM_FIXED_BUF_POOLS > 0)
   for (i = 0; i < GKI_NUM_FIXED_BUF_POOLS; i++) {
     p_cb->pool_list[i] = i;
   }
+#endif
 
   p_cb->curr_total_no_of_pools = GKI_NUM_FIXED_BUF_POOLS;
 
diff --git a/libnfc-nci/src/nfa/hci/nfa_hci_utils.cc b/libnfc-nci/src/nfa/hci/nfa_hci_utils.cc
index 98f7822..ca2f119 100644
--- a/libnfc-nci/src/nfa/hci/nfa_hci_utils.cc
+++ b/libnfc-nci/src/nfa/hci/nfa_hci_utils.cc
@@ -303,6 +303,10 @@
   bool first_pkt = true;
   uint16_t data_len;
   tNFA_STATUS status = NFA_STATUS_OK;
+  if ((msg_len != 0) && (p_msg == nullptr)) {
+    LOG(ERROR) << StringPrintf("%s: msg_len is 0 and p_msg is null", __func__);
+    return NFA_STATUS_FAILED;
+  }
   uint16_t max_seg_hcp_pkt_size;
   if (nfa_hci_cb.buff_size > (NCI_DATA_HDR_SIZE + 2)) {
     max_seg_hcp_pkt_size = nfa_hci_cb.buff_size - NCI_DATA_HDR_SIZE;
diff --git a/libnfc-nci/src/nfc_vendor_extn/NfcVendorExtn.cc b/libnfc-nci/src/nfc_vendor_extn/NfcVendorExtn.cc
index 76af1cc..602f43f 100644
--- a/libnfc-nci/src/nfc_vendor_extn/NfcVendorExtn.cc
+++ b/libnfc-nci/src/nfc_vendor_extn/NfcVendorExtn.cc
@@ -16,22 +16,19 @@
 
 #include "NfcVendorExtn.h"
 
+#include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android/log.h>
 #include <dlfcn.h>
 #include <error.h>
 #include <log/log.h>
+#include <string>
 
 using android::base::StringPrintf;
 #define UNUSED_PROP(X) (void)(X);
 
-std::string mLibName = "libnfc_vendor_extn.so";
-#if (defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64))
-std::string mLibPathName = "/system/lib64/" + mLibName;
-#else
-std::string mLibPathName = "/system/lib/" + mLibName;
-#endif
 const std::string vendor_nfc_init_name = "vendor_nfc_init";
 const std::string vendor_nfc_de_init_name = "vendor_nfc_de_init";
 const std::string vendor_nfc_handle_event_name = "vendor_nfc_handle_event";
@@ -52,9 +49,47 @@
 fp_extn_on_config_update_t fp_extn_on_config_update = NULL;
 
 NfcExtEventData_t mNfcExtEventData;
+std::string mLibPathName = "";
 
 void* p_oem_extn_handle = NULL;
 
+namespace {
+  std::string searchLibPath(std::string file_name) {
+    const std::vector<std::string> search_path = {
+        "/system/lib/", "/system/lib64/"
+    };
+    for (std::string path : search_path) {
+      path.append(file_name);
+      struct stat file_stat;
+      if (stat(path.c_str(), &file_stat) != 0) continue;
+      if (S_ISREG(file_stat.st_mode)) return path;
+    }
+    return "";
+  }
+  // Extension library file Search sequence
+  // 1. If prop_lib_file_name is defined.(where prop_config_file_name is the
+  //   value of the property persist.nfc_vendor_extn.lib_file_name)
+  //   Search a file matches prop_lib_file_name.
+  // 2. If SKU is defined (where SKU is the value of the property
+  //   ro.boot.product.hardware.sku)
+  //   Search a file matches libnfc_vendor_extn-SKU.so
+  // 3. If none of 1,2 is defined, search a default file name "libnfc_vendor_extn.so".
+  std::string findLibPath() {
+    std::string f_path = searchLibPath(
+        android::base::GetProperty("persist.nfc_vendor_extn.lib_file_name", ""));
+    if (!f_path.empty()) return f_path;
+
+    // Search for libnfc_vendor_extn-SKU.so
+    f_path = searchLibPath(
+        "libnfc_vendor_extn-" +
+        android::base::GetProperty("ro.boot.product.hardware.sku", "") + ".so");
+    if (!f_path.empty()) return f_path;
+
+    // load default file if the desired file not found.
+    return searchLibPath("libnfc_vendor_extn.so");
+  }
+}  // namespace
+
 NfcVendorExtn::NfcVendorExtn() {}
 
 NfcVendorExtn::~NfcVendorExtn() { sNfcVendorExtn = nullptr; }
@@ -78,6 +113,12 @@
 
 bool NfcExtn_LibSetup() {
   LOG(VERBOSE) << __func__;
+  mLibPathName = findLibPath();
+  if (mLibPathName.empty()) {
+    LOG(ERROR) << StringPrintf("%s: Failed to find %s !!", __func__,
+                               mLibPathName.c_str());
+    return false;
+  }
   p_oem_extn_handle = dlopen(mLibPathName.c_str(), RTLD_NOW);
   if (p_oem_extn_handle == NULL) {
     LOG(DEBUG) << StringPrintf(
@@ -209,7 +250,7 @@
     }
   }
   if (p_oem_extn_handle != NULL) {
-    LOG(DEBUG) << StringPrintf("%s: Closing %s!!", __func__, mLibName.c_str());
+    LOG(DEBUG) << StringPrintf("%s: Closing %s!!", __func__, mLibPathName.c_str());
     dlclose(p_oem_extn_handle);
     p_oem_extn_handle = NULL;
   }
diff --git a/libnfc-nci/src/nfc_vendor_extn/include/NfcVendorExtn.h b/libnfc-nci/src/nfc_vendor_extn/include/NfcVendorExtn.h
index 95ab51b..7d35fa8 100644
--- a/libnfc-nci/src/nfc_vendor_extn/include/NfcVendorExtn.h
+++ b/libnfc-nci/src/nfc_vendor_extn/include/NfcVendorExtn.h
@@ -90,6 +90,10 @@
   HANDLE_DOWNLOAD_FIRMWARE_REQUEST,
   HANDLE_NFC_ADAPTATION_INIT,
   HANDLE_NFC_PRE_DISCOVER,
+  HANDLE_NFC_HAL_CORE_INITIALIZE,
+  HANDLE_NFC_HAL_POWER_CYCLE,
+  HANDLE_NFC_GET_MAX_NFCEE,
+  HANDLE_NFC_HAL_CLOSE,
 } NfcExtEvent_t;
 
 typedef enum {