Merge "Code cleanup: removed support of P2P in NfcDiscoveryParameters" into main am: 7176a36187 am: 96c409471a

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

Change-Id: Id2889fd45193be29ce7802d7e902609a38c7a5e9
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/Android.bp b/Android.bp
index 830da16..60489f4 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1,12 +1,13 @@
 package {
+    default_team: "trendy_team_fwk_nfc",
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
 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",
+    cmd: "$(location stats-log-api-gen) --java $(out) --module nfc --javaPackage com.android.nfc" +
+        " --javaClass NfcStatsLog",
     out: ["com/android/nfc/NfcStatsLog.java"],
 }
 
@@ -23,37 +24,12 @@
         "//apex_available:platform",
         "com.android.nfcservices",
     ],
-    min_sdk_version: "VanillaIceCream",
+    min_sdk_version: "current",
     sdk_version: "module_current",
 }
 
-// NCI Configuration
-android_app {
-    name: "NfcNci",
-    srcs: [
-        "src/**/*.java",
-        "nci/**/*.java",
-        ":framework-nfc-javastream-protos",
-        // TODO(b/263565193): Temp hack until NFC APK can link against module_current.
-        ":framework-nfc-updatable-sources",
-        ":statslog-Nfc-java-gen",
-    ],
-    platform_apis: true,
-    privileged: true,
-    certificate: "platform",
-    // TODO(b/263565193): Change when NFC APK can link against module_current.
-    // min_sdk_version: "VanillaIceCream",
-    // sdk_version: "module_current",
-    jni_libs: ["libnfc_nci_jni"],
-    libs: [
-        "framework-annotations-lib",
-        "framework-bluetooth",
-        "framework-configinfrastructure",
-        "framework-nfc.impl",
-        "framework-statsd.stubs.module_lib",
-        "framework-wifi",
-        "unsupportedappusage"
-    ],
+java_defaults {
+    name: "NfcNciDefaults",
     static_libs: [
         "android.se.omapi-V1-java",
         "androidx.annotation_annotation",
@@ -61,24 +37,85 @@
         "bluetooth-protos-nfc-enums-java-gen",
         "com.google.android.material_material",
         "modules-utils-fastxmlserializer",
+        "modules-utils-shell-command-handler",
         "PlatformProperties",
+        "nfc-event-log-proto",
         "nfc_flags_lib",
     ],
+    privileged: true,
     optimize: {
         proguard_flags_files: ["proguard.flags"],
     },
-    apex_available: [
-        "//apex_available:platform",
-        "com.android.nfcservices"
-    ],
     jarjar_rules: "jarjar-rules.txt",
     privapp_allowlist: ":privapp_allowlist_com.android.nfc.xml",
 }
 
+// NCI Configuration used without NFC apex
+// This version compiles against platform.
+android_app {
+    name: "NfcNci",
+    defaults: ["NfcNciDefaults"],
+    certificate: "platform",
+    sdk_version: "core_platform",
+    srcs: [
+        ":nfc-sources",
+        "shim_src/non_apex/**/*.java",
+    ],
+    libs: [
+        // order matters: classes in framework-nfc are resolved before framework, meaning
+        // @hide APIs in framework-nfc are resolved before @SystemApi stubs in framework
+        "framework-nfc.impl",
+        "framework",
+
+        // if sdk_version="" this gets automatically included, but here we need to add manually.
+        "framework-res",
+    ],
+    jni_libs: ["libnfc_nci_jni"],
+}
+
+// NCI Configuration embedded in NFC apex.
+// This version compiles against SDK API's.
+android_app {
+    name: "NfcNciApex",
+    defaults: ["NfcNciDefaults"],
+    min_sdk_version: "current",
+    sdk_version: "module_current",
+    certificate: "nfc",
+    srcs: [
+        ":nfc-sources",
+        "shim_src/apex/**/*.java",
+    ],
+    libs: [
+        "framework-annotations-lib",
+        "framework-bluetooth",
+        "framework-configinfrastructure",
+        "framework-nfc.impl",
+        "framework-permission-s",
+        "framework-permission",
+        "framework-statsd.stubs.module_lib",
+        "framework-wifi",
+        "android.nfc.flags-aconfig-java",
+        "android.permission.flags-aconfig-java",
+        "android.service.chooser.flags-aconfig-java",
+        "unsupportedappusage",
+    ],
+    // prevent NfcNciApex from using product-specific resources
+    aaptflags: ["--product default"],
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.nfcservices",
+    ],
+    lint: {
+        baseline_filename: "lint-baseline.xml",
+    },
+}
+
 filegroup {
     name: "nfc-sources",
     srcs: [
         "src/**/*.java",
+        "nci/**/*.java",
+        ":framework-nfc-javastream-protos",
         ":statslog-Nfc-java-gen",
     ],
     visibility: [
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 4470f3f..f5a72b5 100755
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -65,6 +65,12 @@
     <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
     <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
     <uses-permission android:name="android.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE" />
+    <uses-permission android:name="android.permission.SHOW_CUSTOMIZED_RESOLVER" />
+    <uses-permission android:name="android.permission.MANAGE_ROLE_HOLDERS" />
+    <uses-permission android:name="android.permission.OBSERVE_ROLE_HOLDERS"/>
+    <uses-permission android:name="android.permission.DUMP"/>
+    <uses-permission android:name="android.permission.QUERY_CLONED_APPS"/>
+    <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
 
     <application android:name=".NfcApplication"
                  android:icon="@drawable/icon"
@@ -72,6 +78,7 @@
                  android:theme="@android:style/Theme.Material.Light"
                  android:persistent="true"
                  android:persistentWhenFeatureAvailable="android.hardware.nfc.any"
+                 android:restoreAnyVersion="true"
                  android:backupAgent="com.android.nfc.NfcBackupAgent"
                  android:killAfterRestore="false"
                  android:usesCleartextTraffic="false"
@@ -132,6 +139,10 @@
             android:theme="@android:style/Theme.Translucent.NoTitleBar"
             android:noHistory="true"
         />
+        <activity android:name=".NfcEnableAllowlistActivity"
+            android:theme="@android:style/Theme.Translucent.NoTitleBar"
+            android:noHistory="true"
+        />
 
         <receiver android:name=".NfcBootCompletedReceiver"
             android:exported="true">
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 7fb1636..0ccc9cb 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,3 +1,6 @@
+[Hook Scripts]
+hidden_api_txt_checksorted_hook = ${REPO_ROOT}/tools/platform-compat/hiddenapi/checksorted_sha.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT}
+
 [Options]
 ignore_merged_commits = true
 
diff --git a/apex/Android.bp b/apex/Android.bp
index 53523a8..0ffbd56 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -13,6 +13,7 @@
 // limitations under the License.
 
 package {
+    default_team: "trendy_team_fwk_nfc",
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
@@ -27,18 +28,36 @@
     certificate: "com.android.nfcservices",
 }
 
-apex {
+soong_config_module_type {
+    name: "custom_apex",
+    module_type: "apex",
+    config_namespace: "bootclasspath",
+    bool_variables: [
+        "nfc_apex_bootclasspath_fragment",
+    ],
+    properties: [
+        "bootclasspath_fragments",
+    ],
+}
+
+custom_apex {
     name: "com.android.nfcservices",
     manifest: "manifest.json",
-    // apps: ["NfcNci"],
+    apps: ["NfcNciApex"],
     multilib: {
-       first: {
-           // Extractor process runs only with the primary ABI.
-           jni_libs: [
-               "libnfc_nci_jni",
-           ],
-       },
+        first: {
+            // Extractor process runs only with the primary ABI.
+            jni_libs: [
+                "libnfc_nci_jni",
+            ],
+        },
     },
+    soong_config_variables: {
+        nfc_apex_bootclasspath_fragment: {
+            bootclasspath_fragments: ["com.android.nfcservices-bootclasspath-fragment"],
+        },
+    },
+
     required: [
         // Provide a default libnfc-nci.conf in /system/etc for devices that
         // does not ship one in /product
@@ -48,3 +67,77 @@
     certificate: ":com.android.nfcservices.certificate",
     updatable: false,
 }
+
+soong_config_module_type {
+    name: "custom_bootclasspath_fragment",
+    module_type: "bootclasspath_fragment",
+    config_namespace: "bootclasspath",
+    bool_variables: [
+        "nfc_apex_bootclasspath_fragment",
+    ],
+    properties: [
+        "enabled",
+    ],
+}
+
+// Encapsulate the contributions made by the com.android.nfc to the bootclasspath.
+custom_bootclasspath_fragment {
+    name: "com.android.nfcservices-bootclasspath-fragment",
+    contents: ["framework-nfc"],
+    apex_available: ["com.android.nfcservices"],
+    // This is disabled for now since the build system does not allow for adding a bootclasspath
+    // fragment and conditionally adding it to PRODUCT_APEX_BOOT_JARS.
+    // When `RELEASE_PACKAGE_NFC_STACK` is set to `com.android.nfcservices`, this needs to be
+    // set to true.
+    enabled: false,
+    soong_config_variables: {
+        nfc_apex_bootclasspath_fragment: {
+            enabled: true,
+        },
+    },
+
+    // The bootclasspath_fragments that provide APIs on which this depends.
+    fragments: [
+        // Needed to access core java APIs.
+        {
+            apex: "com.android.art",
+            module: "art-bootclasspath-fragment",
+        },
+    ],
+
+    // Additional stubs libraries that this fragment's contents use which are
+    // not provided by another bootclasspath_fragment.
+    additional_stubs: [
+        // Needed to access platform APIs.
+        "android-non-updatable",
+    ],
+    hidden_api: {
+        // Additional hidden API flag files to override the defaults. This must only be
+        // modified by the Soong or platform compat team.
+        max_target_o_low_priority: ["hiddenapi/hiddenapi-max-target-o.txt"],
+        max_target_r_low_priority: ["hiddenapi/hiddenapi-max-target-r-loprio.txt"],
+
+        // The following packages contain classes from other modules on the
+        // bootclasspath. That means that the hidden API flags for this module
+        // has to explicitly list every single class this module provides in
+        // that package to differentiate them from the classes provided by other
+        // modules. That can include private classes that are not part of the
+        // API.
+        split_packages: [
+            "android.nfc",
+            "android.nfc.cardemulation",
+        ],
+
+        // The following packages and all their subpackages currently only
+        // contain classes from this bootclasspath_fragment. Listing a package
+        // here won't prevent other bootclasspath modules from adding classes in
+        // any of those packages but it will prevent them from adding those
+        // classes into an API surface, e.g. public, system, etc.. Doing so will
+        // result in a build failure due to inconsistent flags.
+        package_prefixes: [
+            "android.nfc.dta",
+            "android.nfc.tech",
+            "com.android.nfc",
+        ],
+    },
+}
diff --git a/apex/hiddenapi/OWNERS b/apex/hiddenapi/OWNERS
new file mode 100644
index 0000000..ac8a2b6
--- /dev/null
+++ b/apex/hiddenapi/OWNERS
@@ -0,0 +1,5 @@
+# soong-team@ as the hiddenapi files are tightly coupled with Soong
+file:platform/build/soong:/OWNERS
+
+# compat-team@ for changes to hiddenapi files
+file:tools/platform-compat:/OWNERS
diff --git a/apex/hiddenapi/hiddenapi-max-target-o.txt b/apex/hiddenapi/hiddenapi-max-target-o.txt
new file mode 100644
index 0000000..9012923
--- /dev/null
+++ b/apex/hiddenapi/hiddenapi-max-target-o.txt
@@ -0,0 +1,643 @@
+Landroid/nfc/ApduList;-><init>()V
+Landroid/nfc/ApduList;-><init>(Landroid/os/Parcel;)V
+Landroid/nfc/ApduList;->add([B)V
+Landroid/nfc/ApduList;->commands:Ljava/util/ArrayList;
+Landroid/nfc/ApduList;->CREATOR:Landroid/os/Parcelable$Creator;
+Landroid/nfc/ApduList;->get()Ljava/util/List;
+Landroid/nfc/BeamShareData;-><init>(Landroid/nfc/NdefMessage;[Landroid/net/Uri;Landroid/os/UserHandle;I)V
+Landroid/nfc/BeamShareData;->CREATOR:Landroid/os/Parcelable$Creator;
+Landroid/nfc/BeamShareData;->flags:I
+Landroid/nfc/BeamShareData;->ndefMessage:Landroid/nfc/NdefMessage;
+Landroid/nfc/BeamShareData;->uris:[Landroid/net/Uri;
+Landroid/nfc/BeamShareData;->userHandle:Landroid/os/UserHandle;
+Landroid/nfc/cardemulation/AidGroup;-><init>(Ljava/util/List;Ljava/lang/String;)V
+Landroid/nfc/cardemulation/AidGroup;->isValidCategory(Ljava/lang/String;)Z
+Landroid/nfc/cardemulation/AidGroup;->MAX_NUM_AIDS:I
+Landroid/nfc/cardemulation/AidGroup;->TAG:Ljava/lang/String;
+Landroid/nfc/cardemulation/ApduServiceInfo;->dump(Ljava/io/FileDescriptor;Ljava/io/PrintWriter;[Ljava/lang/String;)V
+Landroid/nfc/cardemulation/ApduServiceInfo;->getAidGroups()Ljava/util/ArrayList;
+Landroid/nfc/cardemulation/ApduServiceInfo;->getAids()Ljava/util/List;
+Landroid/nfc/cardemulation/ApduServiceInfo;->getCategoryForAid(Ljava/lang/String;)Ljava/lang/String;
+Landroid/nfc/cardemulation/ApduServiceInfo;->getComponent()Landroid/content/ComponentName;
+Landroid/nfc/cardemulation/ApduServiceInfo;->getDynamicAidGroupForCategory(Ljava/lang/String;)Landroid/nfc/cardemulation/AidGroup;
+Landroid/nfc/cardemulation/ApduServiceInfo;->getPrefixAids()Ljava/util/List;
+Landroid/nfc/cardemulation/ApduServiceInfo;->getSubsetAids()Ljava/util/List;
+Landroid/nfc/cardemulation/ApduServiceInfo;->hasCategory(Ljava/lang/String;)Z
+Landroid/nfc/cardemulation/ApduServiceInfo;->loadAppLabel(Landroid/content/pm/PackageManager;)Ljava/lang/CharSequence;
+Landroid/nfc/cardemulation/ApduServiceInfo;->loadIcon(Landroid/content/pm/PackageManager;)Landroid/graphics/drawable/Drawable;
+Landroid/nfc/cardemulation/ApduServiceInfo;->loadLabel(Landroid/content/pm/PackageManager;)Ljava/lang/CharSequence;
+Landroid/nfc/cardemulation/ApduServiceInfo;->mBannerResourceId:I
+Landroid/nfc/cardemulation/ApduServiceInfo;->mDescription:Ljava/lang/String;
+Landroid/nfc/cardemulation/ApduServiceInfo;->mOnHost:Z
+Landroid/nfc/cardemulation/ApduServiceInfo;->mRequiresDeviceUnlock:Z
+Landroid/nfc/cardemulation/ApduServiceInfo;->mSettingsActivityName:Ljava/lang/String;
+Landroid/nfc/cardemulation/ApduServiceInfo;->mUid:I
+Landroid/nfc/cardemulation/ApduServiceInfo;->removeDynamicAidGroupForCategory(Ljava/lang/String;)Z
+Landroid/nfc/cardemulation/ApduServiceInfo;->setOrReplaceDynamicAidGroup(Landroid/nfc/cardemulation/AidGroup;)V
+Landroid/nfc/cardemulation/ApduServiceInfo;->TAG:Ljava/lang/String;
+Landroid/nfc/cardemulation/CardEmulation;-><init>(Landroid/content/Context;Landroid/nfc/INfcCardEmulation;)V
+Landroid/nfc/cardemulation/CardEmulation;->getServices(Ljava/lang/String;)Ljava/util/List;
+Landroid/nfc/cardemulation/CardEmulation;->isValidAid(Ljava/lang/String;)Z
+Landroid/nfc/cardemulation/CardEmulation;->mContext:Landroid/content/Context;
+Landroid/nfc/cardemulation/CardEmulation;->recoverService()V
+Landroid/nfc/cardemulation/CardEmulation;->sCardEmus:Ljava/util/HashMap;
+Landroid/nfc/cardemulation/CardEmulation;->setDefaultForNextTap(Landroid/content/ComponentName;)Z
+Landroid/nfc/cardemulation/CardEmulation;->setDefaultServiceForCategory(Landroid/content/ComponentName;Ljava/lang/String;)Z
+Landroid/nfc/cardemulation/CardEmulation;->sIsInitialized:Z
+Landroid/nfc/cardemulation/CardEmulation;->sService:Landroid/nfc/INfcCardEmulation;
+Landroid/nfc/cardemulation/CardEmulation;->TAG:Ljava/lang/String;
+Landroid/nfc/cardemulation/HostApduService;->KEY_DATA:Ljava/lang/String;
+Landroid/nfc/cardemulation/HostApduService;->mMessenger:Landroid/os/Messenger;
+Landroid/nfc/cardemulation/HostApduService;->mNfcService:Landroid/os/Messenger;
+Landroid/nfc/cardemulation/HostApduService;->MSG_COMMAND_APDU:I
+Landroid/nfc/cardemulation/HostApduService;->MSG_DEACTIVATED:I
+Landroid/nfc/cardemulation/HostApduService;->MSG_RESPONSE_APDU:I
+Landroid/nfc/cardemulation/HostApduService;->MSG_UNHANDLED:I
+Landroid/nfc/cardemulation/HostApduService;->TAG:Ljava/lang/String;
+Landroid/nfc/cardemulation/HostNfcFService;->KEY_DATA:Ljava/lang/String;
+Landroid/nfc/cardemulation/HostNfcFService;->KEY_MESSENGER:Ljava/lang/String;
+Landroid/nfc/cardemulation/HostNfcFService;->mMessenger:Landroid/os/Messenger;
+Landroid/nfc/cardemulation/HostNfcFService;->mNfcService:Landroid/os/Messenger;
+Landroid/nfc/cardemulation/HostNfcFService;->MSG_COMMAND_PACKET:I
+Landroid/nfc/cardemulation/HostNfcFService;->MSG_DEACTIVATED:I
+Landroid/nfc/cardemulation/HostNfcFService;->MSG_RESPONSE_PACKET:I
+Landroid/nfc/cardemulation/HostNfcFService;->TAG:Ljava/lang/String;
+Landroid/nfc/cardemulation/NfcFCardEmulation;-><init>(Landroid/content/Context;Landroid/nfc/INfcFCardEmulation;)V
+Landroid/nfc/cardemulation/NfcFCardEmulation;->getMaxNumOfRegisterableSystemCodes()I
+Landroid/nfc/cardemulation/NfcFCardEmulation;->getNfcFServices()Ljava/util/List;
+Landroid/nfc/cardemulation/NfcFCardEmulation;->isValidNfcid2(Ljava/lang/String;)Z
+Landroid/nfc/cardemulation/NfcFCardEmulation;->isValidSystemCode(Ljava/lang/String;)Z
+Landroid/nfc/cardemulation/NfcFCardEmulation;->mContext:Landroid/content/Context;
+Landroid/nfc/cardemulation/NfcFCardEmulation;->recoverService()V
+Landroid/nfc/cardemulation/NfcFCardEmulation;->sCardEmus:Ljava/util/HashMap;
+Landroid/nfc/cardemulation/NfcFCardEmulation;->sIsInitialized:Z
+Landroid/nfc/cardemulation/NfcFCardEmulation;->sService:Landroid/nfc/INfcFCardEmulation;
+Landroid/nfc/cardemulation/NfcFCardEmulation;->TAG:Ljava/lang/String;
+Landroid/nfc/cardemulation/NfcFServiceInfo;-><init>(Landroid/content/pm/PackageManager;Landroid/content/pm/ResolveInfo;)V
+Landroid/nfc/cardemulation/NfcFServiceInfo;-><init>(Landroid/content/pm/ResolveInfo;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;)V
+Landroid/nfc/cardemulation/NfcFServiceInfo;->CREATOR:Landroid/os/Parcelable$Creator;
+Landroid/nfc/cardemulation/NfcFServiceInfo;->DEFAULT_T3T_PMM:Ljava/lang/String;
+Landroid/nfc/cardemulation/NfcFServiceInfo;->dump(Ljava/io/FileDescriptor;Ljava/io/PrintWriter;[Ljava/lang/String;)V
+Landroid/nfc/cardemulation/NfcFServiceInfo;->getComponent()Landroid/content/ComponentName;
+Landroid/nfc/cardemulation/NfcFServiceInfo;->getDescription()Ljava/lang/String;
+Landroid/nfc/cardemulation/NfcFServiceInfo;->getNfcid2()Ljava/lang/String;
+Landroid/nfc/cardemulation/NfcFServiceInfo;->getSystemCode()Ljava/lang/String;
+Landroid/nfc/cardemulation/NfcFServiceInfo;->getT3tPmm()Ljava/lang/String;
+Landroid/nfc/cardemulation/NfcFServiceInfo;->getUid()I
+Landroid/nfc/cardemulation/NfcFServiceInfo;->loadIcon(Landroid/content/pm/PackageManager;)Landroid/graphics/drawable/Drawable;
+Landroid/nfc/cardemulation/NfcFServiceInfo;->loadLabel(Landroid/content/pm/PackageManager;)Ljava/lang/CharSequence;
+Landroid/nfc/cardemulation/NfcFServiceInfo;->mDescription:Ljava/lang/String;
+Landroid/nfc/cardemulation/NfcFServiceInfo;->mDynamicNfcid2:Ljava/lang/String;
+Landroid/nfc/cardemulation/NfcFServiceInfo;->mDynamicSystemCode:Ljava/lang/String;
+Landroid/nfc/cardemulation/NfcFServiceInfo;->mNfcid2:Ljava/lang/String;
+Landroid/nfc/cardemulation/NfcFServiceInfo;->mService:Landroid/content/pm/ResolveInfo;
+Landroid/nfc/cardemulation/NfcFServiceInfo;->mSystemCode:Ljava/lang/String;
+Landroid/nfc/cardemulation/NfcFServiceInfo;->mT3tPmm:Ljava/lang/String;
+Landroid/nfc/cardemulation/NfcFServiceInfo;->mUid:I
+Landroid/nfc/cardemulation/NfcFServiceInfo;->setOrReplaceDynamicNfcid2(Ljava/lang/String;)V
+Landroid/nfc/cardemulation/NfcFServiceInfo;->setOrReplaceDynamicSystemCode(Ljava/lang/String;)V
+Landroid/nfc/cardemulation/NfcFServiceInfo;->TAG:Ljava/lang/String;
+Landroid/nfc/ErrorCodes;-><init>()V
+Landroid/nfc/ErrorCodes;->asString(I)Ljava/lang/String;
+Landroid/nfc/ErrorCodes;->ERROR_BUFFER_TO_SMALL:I
+Landroid/nfc/ErrorCodes;->ERROR_BUSY:I
+Landroid/nfc/ErrorCodes;->ERROR_CANCELLED:I
+Landroid/nfc/ErrorCodes;->ERROR_CONNECT:I
+Landroid/nfc/ErrorCodes;->ERROR_DISCONNECT:I
+Landroid/nfc/ErrorCodes;->ERROR_INSUFFICIENT_RESOURCES:I
+Landroid/nfc/ErrorCodes;->ERROR_INVALID_PARAM:I
+Landroid/nfc/ErrorCodes;->ERROR_IO:I
+Landroid/nfc/ErrorCodes;->ERROR_NFC_ON:I
+Landroid/nfc/ErrorCodes;->ERROR_NOT_INITIALIZED:I
+Landroid/nfc/ErrorCodes;->ERROR_NOT_SUPPORTED:I
+Landroid/nfc/ErrorCodes;->ERROR_NO_SE_CONNECTED:I
+Landroid/nfc/ErrorCodes;->ERROR_READ:I
+Landroid/nfc/ErrorCodes;->ERROR_SAP_USED:I
+Landroid/nfc/ErrorCodes;->ERROR_SERVICE_NAME_USED:I
+Landroid/nfc/ErrorCodes;->ERROR_SE_ALREADY_SELECTED:I
+Landroid/nfc/ErrorCodes;->ERROR_SE_CONNECTED:I
+Landroid/nfc/ErrorCodes;->ERROR_SOCKET_CREATION:I
+Landroid/nfc/ErrorCodes;->ERROR_SOCKET_NOT_CONNECTED:I
+Landroid/nfc/ErrorCodes;->ERROR_SOCKET_OPTIONS:I
+Landroid/nfc/ErrorCodes;->ERROR_TIMEOUT:I
+Landroid/nfc/ErrorCodes;->ERROR_WRITE:I
+Landroid/nfc/ErrorCodes;->SUCCESS:I
+Landroid/nfc/IAppCallback$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
+Landroid/nfc/IAppCallback$Stub$Proxy;->createBeamShareData(B)Landroid/nfc/BeamShareData;
+Landroid/nfc/IAppCallback$Stub$Proxy;->getInterfaceDescriptor()Ljava/lang/String;
+Landroid/nfc/IAppCallback$Stub$Proxy;->mRemote:Landroid/os/IBinder;
+Landroid/nfc/IAppCallback$Stub$Proxy;->onNdefPushComplete(B)V
+Landroid/nfc/IAppCallback$Stub$Proxy;->onTagDiscovered(Landroid/nfc/Tag;)V
+Landroid/nfc/IAppCallback$Stub;-><init>()V
+Landroid/nfc/IAppCallback$Stub;->asInterface(Landroid/os/IBinder;)Landroid/nfc/IAppCallback;
+Landroid/nfc/IAppCallback$Stub;->DESCRIPTOR:Ljava/lang/String;
+Landroid/nfc/IAppCallback$Stub;->TRANSACTION_createBeamShareData:I
+Landroid/nfc/IAppCallback$Stub;->TRANSACTION_onNdefPushComplete:I
+Landroid/nfc/IAppCallback$Stub;->TRANSACTION_onTagDiscovered:I
+Landroid/nfc/IAppCallback;->createBeamShareData(B)Landroid/nfc/BeamShareData;
+Landroid/nfc/IAppCallback;->onNdefPushComplete(B)V
+Landroid/nfc/IAppCallback;->onTagDiscovered(Landroid/nfc/Tag;)V
+Landroid/nfc/INfcAdapter$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
+Landroid/nfc/INfcAdapter$Stub$Proxy;->addNfcUnlockHandler(Landroid/nfc/INfcUnlockHandler;[I)V
+Landroid/nfc/INfcAdapter$Stub$Proxy;->disable(Z)Z
+Landroid/nfc/INfcAdapter$Stub$Proxy;->disableNdefPush()Z
+Landroid/nfc/INfcAdapter$Stub$Proxy;->dispatch(Landroid/nfc/Tag;)V
+Landroid/nfc/INfcAdapter$Stub$Proxy;->enable()Z
+Landroid/nfc/INfcAdapter$Stub$Proxy;->enableNdefPush()Z
+Landroid/nfc/INfcAdapter$Stub$Proxy;->getInterfaceDescriptor()Ljava/lang/String;
+Landroid/nfc/INfcAdapter$Stub$Proxy;->getNfcAdapterExtrasInterface(Ljava/lang/String;)Landroid/nfc/INfcAdapterExtras;
+Landroid/nfc/INfcAdapter$Stub$Proxy;->getNfcCardEmulationInterface()Landroid/nfc/INfcCardEmulation;
+Landroid/nfc/INfcAdapter$Stub$Proxy;->getNfcDtaInterface(Ljava/lang/String;)Landroid/nfc/INfcDta;
+Landroid/nfc/INfcAdapter$Stub$Proxy;->getNfcFCardEmulationInterface()Landroid/nfc/INfcFCardEmulation;
+Landroid/nfc/INfcAdapter$Stub$Proxy;->getNfcTagInterface()Landroid/nfc/INfcTag;
+Landroid/nfc/INfcAdapter$Stub$Proxy;->getState()I
+Landroid/nfc/INfcAdapter$Stub$Proxy;->ignore(IILandroid/nfc/ITagRemovedCallback;)Z
+Landroid/nfc/INfcAdapter$Stub$Proxy;->invokeBeam()V
+Landroid/nfc/INfcAdapter$Stub$Proxy;->invokeBeamInternal(Landroid/nfc/BeamShareData;)V
+Landroid/nfc/INfcAdapter$Stub$Proxy;->isNdefPushEnabled()Z
+Landroid/nfc/INfcAdapter$Stub$Proxy;->mRemote:Landroid/os/IBinder;
+Landroid/nfc/INfcAdapter$Stub$Proxy;->pausePolling(I)V
+Landroid/nfc/INfcAdapter$Stub$Proxy;->removeNfcUnlockHandler(Landroid/nfc/INfcUnlockHandler;)V
+Landroid/nfc/INfcAdapter$Stub$Proxy;->resumePolling()V
+Landroid/nfc/INfcAdapter$Stub$Proxy;->setAppCallback(Landroid/nfc/IAppCallback;)V
+Landroid/nfc/INfcAdapter$Stub$Proxy;->setForegroundDispatch(Landroid/app/PendingIntent;[Landroid/content/IntentFilter;Landroid/nfc/TechListParcel;)V
+Landroid/nfc/INfcAdapter$Stub$Proxy;->setP2pModes(II)V
+Landroid/nfc/INfcAdapter$Stub$Proxy;->setReaderMode(Landroid/os/IBinder;Landroid/nfc/IAppCallback;ILandroid/os/Bundle;)V
+Landroid/nfc/INfcAdapter$Stub$Proxy;->verifyNfcPermission()V
+Landroid/nfc/INfcAdapter$Stub;-><init>()V
+Landroid/nfc/INfcAdapter$Stub;->asInterface(Landroid/os/IBinder;)Landroid/nfc/INfcAdapter;
+Landroid/nfc/INfcAdapter$Stub;->DESCRIPTOR:Ljava/lang/String;
+Landroid/nfc/INfcAdapter$Stub;->TRANSACTION_addNfcUnlockHandler:I
+Landroid/nfc/INfcAdapter$Stub;->TRANSACTION_disable:I
+Landroid/nfc/INfcAdapter$Stub;->TRANSACTION_disableNdefPush:I
+Landroid/nfc/INfcAdapter$Stub;->TRANSACTION_dispatch:I
+Landroid/nfc/INfcAdapter$Stub;->TRANSACTION_enableNdefPush:I
+Landroid/nfc/INfcAdapter$Stub;->TRANSACTION_getNfcAdapterExtrasInterface:I
+Landroid/nfc/INfcAdapter$Stub;->TRANSACTION_getNfcCardEmulationInterface:I
+Landroid/nfc/INfcAdapter$Stub;->TRANSACTION_getNfcDtaInterface:I
+Landroid/nfc/INfcAdapter$Stub;->TRANSACTION_getNfcFCardEmulationInterface:I
+Landroid/nfc/INfcAdapter$Stub;->TRANSACTION_getNfcTagInterface:I
+Landroid/nfc/INfcAdapter$Stub;->TRANSACTION_getState:I
+Landroid/nfc/INfcAdapter$Stub;->TRANSACTION_ignore:I
+Landroid/nfc/INfcAdapter$Stub;->TRANSACTION_invokeBeam:I
+Landroid/nfc/INfcAdapter$Stub;->TRANSACTION_invokeBeamInternal:I
+Landroid/nfc/INfcAdapter$Stub;->TRANSACTION_isNdefPushEnabled:I
+Landroid/nfc/INfcAdapter$Stub;->TRANSACTION_pausePolling:I
+Landroid/nfc/INfcAdapter$Stub;->TRANSACTION_removeNfcUnlockHandler:I
+Landroid/nfc/INfcAdapter$Stub;->TRANSACTION_resumePolling:I
+Landroid/nfc/INfcAdapter$Stub;->TRANSACTION_setAppCallback:I
+Landroid/nfc/INfcAdapter$Stub;->TRANSACTION_setForegroundDispatch:I
+Landroid/nfc/INfcAdapter$Stub;->TRANSACTION_setP2pModes:I
+Landroid/nfc/INfcAdapter$Stub;->TRANSACTION_setReaderMode:I
+Landroid/nfc/INfcAdapter$Stub;->TRANSACTION_verifyNfcPermission:I
+Landroid/nfc/INfcAdapter;->addNfcUnlockHandler(Landroid/nfc/INfcUnlockHandler;[I)V
+Landroid/nfc/INfcAdapter;->disable(Z)Z
+Landroid/nfc/INfcAdapter;->disableNdefPush()Z
+Landroid/nfc/INfcAdapter;->dispatch(Landroid/nfc/Tag;)V
+Landroid/nfc/INfcAdapter;->enable()Z
+Landroid/nfc/INfcAdapter;->enableNdefPush()Z
+Landroid/nfc/INfcAdapter;->getNfcAdapterExtrasInterface(Ljava/lang/String;)Landroid/nfc/INfcAdapterExtras;
+Landroid/nfc/INfcAdapter;->getNfcCardEmulationInterface()Landroid/nfc/INfcCardEmulation;
+Landroid/nfc/INfcAdapter;->getNfcDtaInterface(Ljava/lang/String;)Landroid/nfc/INfcDta;
+Landroid/nfc/INfcAdapter;->getNfcFCardEmulationInterface()Landroid/nfc/INfcFCardEmulation;
+Landroid/nfc/INfcAdapter;->getNfcTagInterface()Landroid/nfc/INfcTag;
+Landroid/nfc/INfcAdapter;->getState()I
+Landroid/nfc/INfcAdapter;->ignore(IILandroid/nfc/ITagRemovedCallback;)Z
+Landroid/nfc/INfcAdapter;->invokeBeam()V
+Landroid/nfc/INfcAdapter;->invokeBeamInternal(Landroid/nfc/BeamShareData;)V
+Landroid/nfc/INfcAdapter;->isNdefPushEnabled()Z
+Landroid/nfc/INfcAdapter;->pausePolling(I)V
+Landroid/nfc/INfcAdapter;->removeNfcUnlockHandler(Landroid/nfc/INfcUnlockHandler;)V
+Landroid/nfc/INfcAdapter;->resumePolling()V
+Landroid/nfc/INfcAdapter;->setAppCallback(Landroid/nfc/IAppCallback;)V
+Landroid/nfc/INfcAdapter;->setForegroundDispatch(Landroid/app/PendingIntent;[Landroid/content/IntentFilter;Landroid/nfc/TechListParcel;)V
+Landroid/nfc/INfcAdapter;->setP2pModes(II)V
+Landroid/nfc/INfcAdapter;->setReaderMode(Landroid/os/IBinder;Landroid/nfc/IAppCallback;ILandroid/os/Bundle;)V
+Landroid/nfc/INfcAdapter;->verifyNfcPermission()V
+Landroid/nfc/INfcAdapterExtras$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
+Landroid/nfc/INfcAdapterExtras$Stub$Proxy;->authenticate(Ljava/lang/String;[B)V
+Landroid/nfc/INfcAdapterExtras$Stub$Proxy;->close(Ljava/lang/String;Landroid/os/IBinder;)Landroid/os/Bundle;
+Landroid/nfc/INfcAdapterExtras$Stub$Proxy;->getCardEmulationRoute(Ljava/lang/String;)I
+Landroid/nfc/INfcAdapterExtras$Stub$Proxy;->getDriverName(Ljava/lang/String;)Ljava/lang/String;
+Landroid/nfc/INfcAdapterExtras$Stub$Proxy;->getInterfaceDescriptor()Ljava/lang/String;
+Landroid/nfc/INfcAdapterExtras$Stub$Proxy;->mRemote:Landroid/os/IBinder;
+Landroid/nfc/INfcAdapterExtras$Stub$Proxy;->open(Ljava/lang/String;Landroid/os/IBinder;)Landroid/os/Bundle;
+Landroid/nfc/INfcAdapterExtras$Stub$Proxy;->setCardEmulationRoute(Ljava/lang/String;I)V
+Landroid/nfc/INfcAdapterExtras$Stub$Proxy;->transceive(Ljava/lang/String;[B)Landroid/os/Bundle;
+Landroid/nfc/INfcAdapterExtras$Stub;-><init>()V
+Landroid/nfc/INfcAdapterExtras$Stub;->asInterface(Landroid/os/IBinder;)Landroid/nfc/INfcAdapterExtras;
+Landroid/nfc/INfcAdapterExtras$Stub;->DESCRIPTOR:Ljava/lang/String;
+Landroid/nfc/INfcAdapterExtras$Stub;->TRANSACTION_authenticate:I
+Landroid/nfc/INfcAdapterExtras$Stub;->TRANSACTION_close:I
+Landroid/nfc/INfcAdapterExtras$Stub;->TRANSACTION_getCardEmulationRoute:I
+Landroid/nfc/INfcAdapterExtras$Stub;->TRANSACTION_getDriverName:I
+Landroid/nfc/INfcAdapterExtras$Stub;->TRANSACTION_open:I
+Landroid/nfc/INfcAdapterExtras$Stub;->TRANSACTION_setCardEmulationRoute:I
+Landroid/nfc/INfcAdapterExtras$Stub;->TRANSACTION_transceive:I
+Landroid/nfc/INfcCardEmulation$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
+Landroid/nfc/INfcCardEmulation$Stub$Proxy;->getAidGroupForService(ILandroid/content/ComponentName;Ljava/lang/String;)Landroid/nfc/cardemulation/AidGroup;
+Landroid/nfc/INfcCardEmulation$Stub$Proxy;->getInterfaceDescriptor()Ljava/lang/String;
+Landroid/nfc/INfcCardEmulation$Stub$Proxy;->getServices(ILjava/lang/String;)Ljava/util/List;
+Landroid/nfc/INfcCardEmulation$Stub$Proxy;->isDefaultServiceForAid(ILandroid/content/ComponentName;Ljava/lang/String;)Z
+Landroid/nfc/INfcCardEmulation$Stub$Proxy;->isDefaultServiceForCategory(ILandroid/content/ComponentName;Ljava/lang/String;)Z
+Landroid/nfc/INfcCardEmulation$Stub$Proxy;->mRemote:Landroid/os/IBinder;
+Landroid/nfc/INfcCardEmulation$Stub$Proxy;->registerAidGroupForService(ILandroid/content/ComponentName;Landroid/nfc/cardemulation/AidGroup;)Z
+Landroid/nfc/INfcCardEmulation$Stub$Proxy;->removeAidGroupForService(ILandroid/content/ComponentName;Ljava/lang/String;)Z
+Landroid/nfc/INfcCardEmulation$Stub$Proxy;->setDefaultForNextTap(ILandroid/content/ComponentName;)Z
+Landroid/nfc/INfcCardEmulation$Stub$Proxy;->setDefaultServiceForCategory(ILandroid/content/ComponentName;Ljava/lang/String;)Z
+Landroid/nfc/INfcCardEmulation$Stub$Proxy;->setPreferredService(Landroid/content/ComponentName;)Z
+Landroid/nfc/INfcCardEmulation$Stub$Proxy;->supportsAidPrefixRegistration()Z
+Landroid/nfc/INfcCardEmulation$Stub$Proxy;->unsetPreferredService()Z
+Landroid/nfc/INfcCardEmulation$Stub;-><init>()V
+Landroid/nfc/INfcCardEmulation$Stub;->asInterface(Landroid/os/IBinder;)Landroid/nfc/INfcCardEmulation;
+Landroid/nfc/INfcCardEmulation$Stub;->DESCRIPTOR:Ljava/lang/String;
+Landroid/nfc/INfcCardEmulation$Stub;->TRANSACTION_getAidGroupForService:I
+Landroid/nfc/INfcCardEmulation$Stub;->TRANSACTION_getServices:I
+Landroid/nfc/INfcCardEmulation$Stub;->TRANSACTION_isDefaultServiceForAid:I
+Landroid/nfc/INfcCardEmulation$Stub;->TRANSACTION_isDefaultServiceForCategory:I
+Landroid/nfc/INfcCardEmulation$Stub;->TRANSACTION_registerAidGroupForService:I
+Landroid/nfc/INfcCardEmulation$Stub;->TRANSACTION_removeAidGroupForService:I
+Landroid/nfc/INfcCardEmulation$Stub;->TRANSACTION_setDefaultForNextTap:I
+Landroid/nfc/INfcCardEmulation$Stub;->TRANSACTION_setDefaultServiceForCategory:I
+Landroid/nfc/INfcCardEmulation$Stub;->TRANSACTION_setPreferredService:I
+Landroid/nfc/INfcCardEmulation$Stub;->TRANSACTION_supportsAidPrefixRegistration:I
+Landroid/nfc/INfcCardEmulation$Stub;->TRANSACTION_unsetPreferredService:I
+Landroid/nfc/INfcCardEmulation;->getAidGroupForService(ILandroid/content/ComponentName;Ljava/lang/String;)Landroid/nfc/cardemulation/AidGroup;
+Landroid/nfc/INfcCardEmulation;->getServices(ILjava/lang/String;)Ljava/util/List;
+Landroid/nfc/INfcCardEmulation;->isDefaultServiceForAid(ILandroid/content/ComponentName;Ljava/lang/String;)Z
+Landroid/nfc/INfcCardEmulation;->isDefaultServiceForCategory(ILandroid/content/ComponentName;Ljava/lang/String;)Z
+Landroid/nfc/INfcCardEmulation;->registerAidGroupForService(ILandroid/content/ComponentName;Landroid/nfc/cardemulation/AidGroup;)Z
+Landroid/nfc/INfcCardEmulation;->removeAidGroupForService(ILandroid/content/ComponentName;Ljava/lang/String;)Z
+Landroid/nfc/INfcCardEmulation;->setDefaultForNextTap(ILandroid/content/ComponentName;)Z
+Landroid/nfc/INfcCardEmulation;->setDefaultServiceForCategory(ILandroid/content/ComponentName;Ljava/lang/String;)Z
+Landroid/nfc/INfcCardEmulation;->setPreferredService(Landroid/content/ComponentName;)Z
+Landroid/nfc/INfcCardEmulation;->supportsAidPrefixRegistration()Z
+Landroid/nfc/INfcCardEmulation;->unsetPreferredService()Z
+Landroid/nfc/INfcDta$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
+Landroid/nfc/INfcDta$Stub$Proxy;->disableClient()V
+Landroid/nfc/INfcDta$Stub$Proxy;->disableDta()V
+Landroid/nfc/INfcDta$Stub$Proxy;->disableServer()V
+Landroid/nfc/INfcDta$Stub$Proxy;->enableClient(Ljava/lang/String;III)Z
+Landroid/nfc/INfcDta$Stub$Proxy;->enableDta()V
+Landroid/nfc/INfcDta$Stub$Proxy;->enableServer(Ljava/lang/String;IIII)Z
+Landroid/nfc/INfcDta$Stub$Proxy;->getInterfaceDescriptor()Ljava/lang/String;
+Landroid/nfc/INfcDta$Stub$Proxy;->mRemote:Landroid/os/IBinder;
+Landroid/nfc/INfcDta$Stub$Proxy;->registerMessageService(Ljava/lang/String;)Z
+Landroid/nfc/INfcDta$Stub;-><init>()V
+Landroid/nfc/INfcDta$Stub;->asInterface(Landroid/os/IBinder;)Landroid/nfc/INfcDta;
+Landroid/nfc/INfcDta$Stub;->DESCRIPTOR:Ljava/lang/String;
+Landroid/nfc/INfcDta$Stub;->TRANSACTION_disableClient:I
+Landroid/nfc/INfcDta$Stub;->TRANSACTION_disableDta:I
+Landroid/nfc/INfcDta$Stub;->TRANSACTION_disableServer:I
+Landroid/nfc/INfcDta$Stub;->TRANSACTION_enableClient:I
+Landroid/nfc/INfcDta$Stub;->TRANSACTION_enableDta:I
+Landroid/nfc/INfcDta$Stub;->TRANSACTION_enableServer:I
+Landroid/nfc/INfcDta$Stub;->TRANSACTION_registerMessageService:I
+Landroid/nfc/INfcDta;->disableClient()V
+Landroid/nfc/INfcDta;->disableDta()V
+Landroid/nfc/INfcDta;->disableServer()V
+Landroid/nfc/INfcDta;->enableClient(Ljava/lang/String;III)Z
+Landroid/nfc/INfcDta;->enableDta()V
+Landroid/nfc/INfcDta;->enableServer(Ljava/lang/String;IIII)Z
+Landroid/nfc/INfcDta;->registerMessageService(Ljava/lang/String;)Z
+Landroid/nfc/INfcFCardEmulation$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
+Landroid/nfc/INfcFCardEmulation$Stub$Proxy;->disableNfcFForegroundService()Z
+Landroid/nfc/INfcFCardEmulation$Stub$Proxy;->enableNfcFForegroundService(Landroid/content/ComponentName;)Z
+Landroid/nfc/INfcFCardEmulation$Stub$Proxy;->getInterfaceDescriptor()Ljava/lang/String;
+Landroid/nfc/INfcFCardEmulation$Stub$Proxy;->getMaxNumOfRegisterableSystemCodes()I
+Landroid/nfc/INfcFCardEmulation$Stub$Proxy;->getNfcFServices(I)Ljava/util/List;
+Landroid/nfc/INfcFCardEmulation$Stub$Proxy;->getNfcid2ForService(ILandroid/content/ComponentName;)Ljava/lang/String;
+Landroid/nfc/INfcFCardEmulation$Stub$Proxy;->getSystemCodeForService(ILandroid/content/ComponentName;)Ljava/lang/String;
+Landroid/nfc/INfcFCardEmulation$Stub$Proxy;->mRemote:Landroid/os/IBinder;
+Landroid/nfc/INfcFCardEmulation$Stub$Proxy;->registerSystemCodeForService(ILandroid/content/ComponentName;Ljava/lang/String;)Z
+Landroid/nfc/INfcFCardEmulation$Stub$Proxy;->removeSystemCodeForService(ILandroid/content/ComponentName;)Z
+Landroid/nfc/INfcFCardEmulation$Stub$Proxy;->setNfcid2ForService(ILandroid/content/ComponentName;Ljava/lang/String;)Z
+Landroid/nfc/INfcFCardEmulation$Stub;-><init>()V
+Landroid/nfc/INfcFCardEmulation$Stub;->asInterface(Landroid/os/IBinder;)Landroid/nfc/INfcFCardEmulation;
+Landroid/nfc/INfcFCardEmulation$Stub;->DESCRIPTOR:Ljava/lang/String;
+Landroid/nfc/INfcFCardEmulation$Stub;->TRANSACTION_disableNfcFForegroundService:I
+Landroid/nfc/INfcFCardEmulation$Stub;->TRANSACTION_enableNfcFForegroundService:I
+Landroid/nfc/INfcFCardEmulation$Stub;->TRANSACTION_getMaxNumOfRegisterableSystemCodes:I
+Landroid/nfc/INfcFCardEmulation$Stub;->TRANSACTION_getNfcFServices:I
+Landroid/nfc/INfcFCardEmulation$Stub;->TRANSACTION_getNfcid2ForService:I
+Landroid/nfc/INfcFCardEmulation$Stub;->TRANSACTION_getSystemCodeForService:I
+Landroid/nfc/INfcFCardEmulation$Stub;->TRANSACTION_registerSystemCodeForService:I
+Landroid/nfc/INfcFCardEmulation$Stub;->TRANSACTION_removeSystemCodeForService:I
+Landroid/nfc/INfcFCardEmulation$Stub;->TRANSACTION_setNfcid2ForService:I
+Landroid/nfc/INfcFCardEmulation;->disableNfcFForegroundService()Z
+Landroid/nfc/INfcFCardEmulation;->enableNfcFForegroundService(Landroid/content/ComponentName;)Z
+Landroid/nfc/INfcFCardEmulation;->getMaxNumOfRegisterableSystemCodes()I
+Landroid/nfc/INfcFCardEmulation;->getNfcFServices(I)Ljava/util/List;
+Landroid/nfc/INfcFCardEmulation;->getNfcid2ForService(ILandroid/content/ComponentName;)Ljava/lang/String;
+Landroid/nfc/INfcFCardEmulation;->getSystemCodeForService(ILandroid/content/ComponentName;)Ljava/lang/String;
+Landroid/nfc/INfcFCardEmulation;->registerSystemCodeForService(ILandroid/content/ComponentName;Ljava/lang/String;)Z
+Landroid/nfc/INfcFCardEmulation;->removeSystemCodeForService(ILandroid/content/ComponentName;)Z
+Landroid/nfc/INfcFCardEmulation;->setNfcid2ForService(ILandroid/content/ComponentName;Ljava/lang/String;)Z
+Landroid/nfc/INfcTag$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
+Landroid/nfc/INfcTag$Stub$Proxy;->canMakeReadOnly(I)Z
+Landroid/nfc/INfcTag$Stub$Proxy;->connect(II)I
+Landroid/nfc/INfcTag$Stub$Proxy;->formatNdef(I[B)I
+Landroid/nfc/INfcTag$Stub$Proxy;->getExtendedLengthApdusSupported()Z
+Landroid/nfc/INfcTag$Stub$Proxy;->getInterfaceDescriptor()Ljava/lang/String;
+Landroid/nfc/INfcTag$Stub$Proxy;->getMaxTransceiveLength(I)I
+Landroid/nfc/INfcTag$Stub$Proxy;->getTechList(I)[I
+Landroid/nfc/INfcTag$Stub$Proxy;->getTimeout(I)I
+Landroid/nfc/INfcTag$Stub$Proxy;->isNdef(I)Z
+Landroid/nfc/INfcTag$Stub$Proxy;->isPresent(I)Z
+Landroid/nfc/INfcTag$Stub$Proxy;->mRemote:Landroid/os/IBinder;
+Landroid/nfc/INfcTag$Stub$Proxy;->ndefIsWritable(I)Z
+Landroid/nfc/INfcTag$Stub$Proxy;->ndefMakeReadOnly(I)I
+Landroid/nfc/INfcTag$Stub$Proxy;->ndefRead(I)Landroid/nfc/NdefMessage;
+Landroid/nfc/INfcTag$Stub$Proxy;->ndefWrite(ILandroid/nfc/NdefMessage;)I
+Landroid/nfc/INfcTag$Stub$Proxy;->reconnect(I)I
+Landroid/nfc/INfcTag$Stub$Proxy;->rediscover(I)Landroid/nfc/Tag;
+Landroid/nfc/INfcTag$Stub$Proxy;->resetTimeouts()V
+Landroid/nfc/INfcTag$Stub$Proxy;->setTimeout(II)I
+Landroid/nfc/INfcTag$Stub$Proxy;->transceive(I[BZ)Landroid/nfc/TransceiveResult;
+Landroid/nfc/INfcTag$Stub;-><init>()V
+Landroid/nfc/INfcTag$Stub;->asInterface(Landroid/os/IBinder;)Landroid/nfc/INfcTag;
+Landroid/nfc/INfcTag$Stub;->DESCRIPTOR:Ljava/lang/String;
+Landroid/nfc/INfcTag$Stub;->TRANSACTION_canMakeReadOnly:I
+Landroid/nfc/INfcTag$Stub;->TRANSACTION_connect:I
+Landroid/nfc/INfcTag$Stub;->TRANSACTION_formatNdef:I
+Landroid/nfc/INfcTag$Stub;->TRANSACTION_getExtendedLengthApdusSupported:I
+Landroid/nfc/INfcTag$Stub;->TRANSACTION_getMaxTransceiveLength:I
+Landroid/nfc/INfcTag$Stub;->TRANSACTION_getTechList:I
+Landroid/nfc/INfcTag$Stub;->TRANSACTION_getTimeout:I
+Landroid/nfc/INfcTag$Stub;->TRANSACTION_isNdef:I
+Landroid/nfc/INfcTag$Stub;->TRANSACTION_isPresent:I
+Landroid/nfc/INfcTag$Stub;->TRANSACTION_ndefIsWritable:I
+Landroid/nfc/INfcTag$Stub;->TRANSACTION_ndefMakeReadOnly:I
+Landroid/nfc/INfcTag$Stub;->TRANSACTION_ndefRead:I
+Landroid/nfc/INfcTag$Stub;->TRANSACTION_ndefWrite:I
+Landroid/nfc/INfcTag$Stub;->TRANSACTION_reconnect:I
+Landroid/nfc/INfcTag$Stub;->TRANSACTION_rediscover:I
+Landroid/nfc/INfcTag$Stub;->TRANSACTION_resetTimeouts:I
+Landroid/nfc/INfcTag$Stub;->TRANSACTION_setTimeout:I
+Landroid/nfc/INfcTag$Stub;->TRANSACTION_transceive:I
+Landroid/nfc/INfcTag;->canMakeReadOnly(I)Z
+Landroid/nfc/INfcTag;->connect(II)I
+Landroid/nfc/INfcTag;->formatNdef(I[B)I
+Landroid/nfc/INfcTag;->getExtendedLengthApdusSupported()Z
+Landroid/nfc/INfcTag;->getMaxTransceiveLength(I)I
+Landroid/nfc/INfcTag;->getTechList(I)[I
+Landroid/nfc/INfcTag;->getTimeout(I)I
+Landroid/nfc/INfcTag;->isNdef(I)Z
+Landroid/nfc/INfcTag;->isPresent(I)Z
+Landroid/nfc/INfcTag;->ndefIsWritable(I)Z
+Landroid/nfc/INfcTag;->ndefMakeReadOnly(I)I
+Landroid/nfc/INfcTag;->ndefRead(I)Landroid/nfc/NdefMessage;
+Landroid/nfc/INfcTag;->ndefWrite(ILandroid/nfc/NdefMessage;)I
+Landroid/nfc/INfcTag;->reconnect(I)I
+Landroid/nfc/INfcTag;->rediscover(I)Landroid/nfc/Tag;
+Landroid/nfc/INfcTag;->resetTimeouts()V
+Landroid/nfc/INfcTag;->setTimeout(II)I
+Landroid/nfc/INfcTag;->transceive(I[BZ)Landroid/nfc/TransceiveResult;
+Landroid/nfc/INfcUnlockHandler$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
+Landroid/nfc/INfcUnlockHandler$Stub$Proxy;->getInterfaceDescriptor()Ljava/lang/String;
+Landroid/nfc/INfcUnlockHandler$Stub$Proxy;->mRemote:Landroid/os/IBinder;
+Landroid/nfc/INfcUnlockHandler$Stub$Proxy;->onUnlockAttempted(Landroid/nfc/Tag;)Z
+Landroid/nfc/INfcUnlockHandler$Stub;-><init>()V
+Landroid/nfc/INfcUnlockHandler$Stub;->asInterface(Landroid/os/IBinder;)Landroid/nfc/INfcUnlockHandler;
+Landroid/nfc/INfcUnlockHandler$Stub;->DESCRIPTOR:Ljava/lang/String;
+Landroid/nfc/INfcUnlockHandler$Stub;->TRANSACTION_onUnlockAttempted:I
+Landroid/nfc/INfcUnlockHandler;->onUnlockAttempted(Landroid/nfc/Tag;)Z
+Landroid/nfc/ITagRemovedCallback$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
+Landroid/nfc/ITagRemovedCallback$Stub$Proxy;->getInterfaceDescriptor()Ljava/lang/String;
+Landroid/nfc/ITagRemovedCallback$Stub$Proxy;->mRemote:Landroid/os/IBinder;
+Landroid/nfc/ITagRemovedCallback$Stub$Proxy;->onTagRemoved()V
+Landroid/nfc/ITagRemovedCallback$Stub;-><init>()V
+Landroid/nfc/ITagRemovedCallback$Stub;->asInterface(Landroid/os/IBinder;)Landroid/nfc/ITagRemovedCallback;
+Landroid/nfc/ITagRemovedCallback$Stub;->DESCRIPTOR:Ljava/lang/String;
+Landroid/nfc/ITagRemovedCallback$Stub;->TRANSACTION_onTagRemoved:I
+Landroid/nfc/ITagRemovedCallback;->onTagRemoved()V
+Landroid/nfc/NdefMessage;->mRecords:[Landroid/nfc/NdefRecord;
+Landroid/nfc/NdefRecord;->bytesToString([B)Ljava/lang/StringBuilder;
+Landroid/nfc/NdefRecord;->EMPTY_BYTE_ARRAY:[B
+Landroid/nfc/NdefRecord;->ensureSanePayloadSize(J)V
+Landroid/nfc/NdefRecord;->FLAG_CF:B
+Landroid/nfc/NdefRecord;->FLAG_IL:B
+Landroid/nfc/NdefRecord;->FLAG_MB:B
+Landroid/nfc/NdefRecord;->FLAG_ME:B
+Landroid/nfc/NdefRecord;->FLAG_SR:B
+Landroid/nfc/NdefRecord;->getByteLength()I
+Landroid/nfc/NdefRecord;->MAX_PAYLOAD_SIZE:I
+Landroid/nfc/NdefRecord;->mPayload:[B
+Landroid/nfc/NdefRecord;->mTnf:S
+Landroid/nfc/NdefRecord;->mType:[B
+Landroid/nfc/NdefRecord;->parse(Ljava/nio/ByteBuffer;Z)[Landroid/nfc/NdefRecord;
+Landroid/nfc/NdefRecord;->parseWktUri()Landroid/net/Uri;
+Landroid/nfc/NdefRecord;->RTD_ANDROID_APP:[B
+Landroid/nfc/NdefRecord;->TNF_RESERVED:S
+Landroid/nfc/NdefRecord;->toUri(Z)Landroid/net/Uri;
+Landroid/nfc/NdefRecord;->URI_PREFIX_MAP:[Ljava/lang/String;
+Landroid/nfc/NdefRecord;->validateTnf(S[B[B[B)Ljava/lang/String;
+Landroid/nfc/NdefRecord;->writeToByteBuffer(Ljava/nio/ByteBuffer;ZZ)V
+Landroid/nfc/NfcActivityManager$NfcActivityState;->activity:Landroid/app/Activity;
+Landroid/nfc/NfcActivityManager$NfcActivityState;->destroy()V
+Landroid/nfc/NfcActivityManager$NfcActivityState;->flags:I
+Landroid/nfc/NfcActivityManager$NfcActivityState;->ndefMessage:Landroid/nfc/NdefMessage;
+Landroid/nfc/NfcActivityManager$NfcActivityState;->ndefMessageCallback:Landroid/nfc/NfcAdapter$CreateNdefMessageCallback;
+Landroid/nfc/NfcActivityManager$NfcActivityState;->onNdefPushCompleteCallback:Landroid/nfc/NfcAdapter$OnNdefPushCompleteCallback;
+Landroid/nfc/NfcActivityManager$NfcActivityState;->readerCallback:Landroid/nfc/NfcAdapter$ReaderCallback;
+Landroid/nfc/NfcActivityManager$NfcActivityState;->readerModeExtras:Landroid/os/Bundle;
+Landroid/nfc/NfcActivityManager$NfcActivityState;->readerModeFlags:I
+Landroid/nfc/NfcActivityManager$NfcActivityState;->resumed:Z
+Landroid/nfc/NfcActivityManager$NfcActivityState;->token:Landroid/os/Binder;
+Landroid/nfc/NfcActivityManager$NfcActivityState;->uriCallback:Landroid/nfc/NfcAdapter$CreateBeamUrisCallback;
+Landroid/nfc/NfcActivityManager$NfcActivityState;->uris:[Landroid/net/Uri;
+Landroid/nfc/NfcActivityManager$NfcApplicationState;->app:Landroid/app/Application;
+Landroid/nfc/NfcActivityManager$NfcApplicationState;->refCount:I
+Landroid/nfc/NfcActivityManager$NfcApplicationState;->register()V
+Landroid/nfc/NfcActivityManager$NfcApplicationState;->unregister()V
+Landroid/nfc/NfcActivityManager;-><init>(Landroid/nfc/NfcAdapter;)V
+Landroid/nfc/NfcActivityManager;->createBeamShareData(B)Landroid/nfc/BeamShareData;
+Landroid/nfc/NfcActivityManager;->DBG:Ljava/lang/Boolean;
+Landroid/nfc/NfcActivityManager;->destroyActivityState(Landroid/app/Activity;)V
+Landroid/nfc/NfcActivityManager;->disableReaderMode(Landroid/app/Activity;)V
+Landroid/nfc/NfcActivityManager;->enableReaderMode(Landroid/app/Activity;Landroid/nfc/NfcAdapter$ReaderCallback;ILandroid/os/Bundle;)V
+Landroid/nfc/NfcActivityManager;->findActivityState(Landroid/app/Activity;)Landroid/nfc/NfcActivityManager$NfcActivityState;
+Landroid/nfc/NfcActivityManager;->findAppState(Landroid/app/Application;)Landroid/nfc/NfcActivityManager$NfcApplicationState;
+Landroid/nfc/NfcActivityManager;->findResumedActivityState()Landroid/nfc/NfcActivityManager$NfcActivityState;
+Landroid/nfc/NfcActivityManager;->getActivityState(Landroid/app/Activity;)Landroid/nfc/NfcActivityManager$NfcActivityState;
+Landroid/nfc/NfcActivityManager;->mActivities:Ljava/util/List;
+Landroid/nfc/NfcActivityManager;->mApps:Ljava/util/List;
+Landroid/nfc/NfcActivityManager;->onNdefPushComplete(B)V
+Landroid/nfc/NfcActivityManager;->onTagDiscovered(Landroid/nfc/Tag;)V
+Landroid/nfc/NfcActivityManager;->registerApplication(Landroid/app/Application;)V
+Landroid/nfc/NfcActivityManager;->requestNfcServiceCallback()V
+Landroid/nfc/NfcActivityManager;->setNdefPushContentUri(Landroid/app/Activity;[Landroid/net/Uri;)V
+Landroid/nfc/NfcActivityManager;->setNdefPushContentUriCallback(Landroid/app/Activity;Landroid/nfc/NfcAdapter$CreateBeamUrisCallback;)V
+Landroid/nfc/NfcActivityManager;->setNdefPushMessage(Landroid/app/Activity;Landroid/nfc/NdefMessage;I)V
+Landroid/nfc/NfcActivityManager;->setNdefPushMessageCallback(Landroid/app/Activity;Landroid/nfc/NfcAdapter$CreateNdefMessageCallback;I)V
+Landroid/nfc/NfcActivityManager;->setOnNdefPushCompleteCallback(Landroid/app/Activity;Landroid/nfc/NfcAdapter$OnNdefPushCompleteCallback;)V
+Landroid/nfc/NfcActivityManager;->setReaderMode(Landroid/os/Binder;ILandroid/os/Bundle;)V
+Landroid/nfc/NfcActivityManager;->TAG:Ljava/lang/String;
+Landroid/nfc/NfcActivityManager;->unregisterApplication(Landroid/app/Application;)V
+Landroid/nfc/NfcActivityManager;->verifyNfcPermission()V
+Landroid/nfc/NfcAdapter;-><init>(Landroid/content/Context;)V
+Landroid/nfc/NfcAdapter;->ACTION_HANDOVER_TRANSFER_DONE:Ljava/lang/String;
+Landroid/nfc/NfcAdapter;->ACTION_HANDOVER_TRANSFER_STARTED:Ljava/lang/String;
+Landroid/nfc/NfcAdapter;->ACTION_TAG_LEFT_FIELD:Ljava/lang/String;
+Landroid/nfc/NfcAdapter;->disableForegroundDispatchInternal(Landroid/app/Activity;Z)V
+Landroid/nfc/NfcAdapter;->dispatch(Landroid/nfc/Tag;)V
+Landroid/nfc/NfcAdapter;->enforceResumed(Landroid/app/Activity;)V
+Landroid/nfc/NfcAdapter;->EXTRA_HANDOVER_TRANSFER_STATUS:Ljava/lang/String;
+Landroid/nfc/NfcAdapter;->EXTRA_HANDOVER_TRANSFER_URI:Ljava/lang/String;
+Landroid/nfc/NfcAdapter;->getCardEmulationService()Landroid/nfc/INfcCardEmulation;
+Landroid/nfc/NfcAdapter;->getNfcDtaInterface()Landroid/nfc/INfcDta;
+Landroid/nfc/NfcAdapter;->getNfcFCardEmulationService()Landroid/nfc/INfcFCardEmulation;
+Landroid/nfc/NfcAdapter;->getSdkVersion()I
+Landroid/nfc/NfcAdapter;->getServiceInterface()Landroid/nfc/INfcAdapter;
+Landroid/nfc/NfcAdapter;->getTagService()Landroid/nfc/INfcTag;
+Landroid/nfc/NfcAdapter;->HANDOVER_TRANSFER_STATUS_FAILURE:I
+Landroid/nfc/NfcAdapter;->HANDOVER_TRANSFER_STATUS_SUCCESS:I
+Landroid/nfc/NfcAdapter;->hasNfcFeature()Z
+Landroid/nfc/NfcAdapter;->hasNfcHceFeature()Z
+Landroid/nfc/NfcAdapter;->invokeBeam(Landroid/nfc/BeamShareData;)Z
+Landroid/nfc/NfcAdapter;->mContext:Landroid/content/Context;
+Landroid/nfc/NfcAdapter;->mForegroundDispatchListener:Landroid/app/OnActivityPausedListener;
+Landroid/nfc/NfcAdapter;->mLock:Ljava/lang/Object;
+Landroid/nfc/NfcAdapter;->mNfcActivityManager:Landroid/nfc/NfcActivityManager;
+Landroid/nfc/NfcAdapter;->mNfcUnlockHandlers:Ljava/util/HashMap;
+Landroid/nfc/NfcAdapter;->mTagRemovedListener:Landroid/nfc/ITagRemovedCallback;
+Landroid/nfc/NfcAdapter;->pausePolling(I)V
+Landroid/nfc/NfcAdapter;->resumePolling()V
+Landroid/nfc/NfcAdapter;->sCardEmulationService:Landroid/nfc/INfcCardEmulation;
+Landroid/nfc/NfcAdapter;->setP2pModes(II)V
+Landroid/nfc/NfcAdapter;->sHasNfcFeature:Z
+Landroid/nfc/NfcAdapter;->sIsInitialized:Z
+Landroid/nfc/NfcAdapter;->sNfcAdapters:Ljava/util/HashMap;
+Landroid/nfc/NfcAdapter;->sNfcFCardEmulationService:Landroid/nfc/INfcFCardEmulation;
+Landroid/nfc/NfcAdapter;->sNullContextNfcAdapter:Landroid/nfc/NfcAdapter;
+Landroid/nfc/NfcAdapter;->sTagService:Landroid/nfc/INfcTag;
+Landroid/nfc/NfcAdapter;->TAG:Ljava/lang/String;
+Landroid/nfc/NfcEvent;-><init>(Landroid/nfc/NfcAdapter;B)V
+Landroid/nfc/NfcManager;->mAdapter:Landroid/nfc/NfcAdapter;
+Landroid/nfc/Tag;-><init>([B[I[Landroid/os/Bundle;ILandroid/nfc/INfcTag;)V
+Landroid/nfc/Tag;->createMockTag([B[I[Landroid/os/Bundle;)Landroid/nfc/Tag;
+Landroid/nfc/Tag;->generateTechStringList([I)[Ljava/lang/String;
+Landroid/nfc/Tag;->getConnectedTechnology()I
+Landroid/nfc/Tag;->getTechCodeList()[I
+Landroid/nfc/Tag;->getTechCodesFromStrings([Ljava/lang/String;)[I
+Landroid/nfc/Tag;->getTechExtras(I)Landroid/os/Bundle;
+Landroid/nfc/Tag;->getTechStringToCodeMap()Ljava/util/HashMap;
+Landroid/nfc/Tag;->hasTech(I)Z
+Landroid/nfc/Tag;->mConnectedTechnology:I
+Landroid/nfc/Tag;->mServiceHandle:I
+Landroid/nfc/Tag;->mTagService:Landroid/nfc/INfcTag;
+Landroid/nfc/Tag;->mTechExtras:[Landroid/os/Bundle;
+Landroid/nfc/Tag;->mTechList:[I
+Landroid/nfc/Tag;->mTechStringList:[Ljava/lang/String;
+Landroid/nfc/Tag;->readBytesWithNull(Landroid/os/Parcel;)[B
+Landroid/nfc/Tag;->rediscover()Landroid/nfc/Tag;
+Landroid/nfc/Tag;->setConnectedTechnology(I)V
+Landroid/nfc/Tag;->setTechnologyDisconnected()V
+Landroid/nfc/Tag;->writeBytesWithNull(Landroid/os/Parcel;[B)V
+Landroid/nfc/tech/BasicTagTechnology;-><init>(Landroid/nfc/Tag;I)V
+Landroid/nfc/tech/BasicTagTechnology;->checkConnected()V
+Landroid/nfc/tech/BasicTagTechnology;->getMaxTransceiveLengthInternal()I
+Landroid/nfc/tech/BasicTagTechnology;->mIsConnected:Z
+Landroid/nfc/tech/BasicTagTechnology;->mSelectedTechnology:I
+Landroid/nfc/tech/BasicTagTechnology;->mTag:Landroid/nfc/Tag;
+Landroid/nfc/tech/BasicTagTechnology;->reconnect()V
+Landroid/nfc/tech/BasicTagTechnology;->TAG:Ljava/lang/String;
+Landroid/nfc/tech/BasicTagTechnology;->transceive([BZ)[B
+Landroid/nfc/tech/IsoDep;-><init>(Landroid/nfc/Tag;)V
+Landroid/nfc/tech/IsoDep;->EXTRA_HIST_BYTES:Ljava/lang/String;
+Landroid/nfc/tech/IsoDep;->EXTRA_HI_LAYER_RESP:Ljava/lang/String;
+Landroid/nfc/tech/IsoDep;->mHiLayerResponse:[B
+Landroid/nfc/tech/IsoDep;->mHistBytes:[B
+Landroid/nfc/tech/IsoDep;->TAG:Ljava/lang/String;
+Landroid/nfc/tech/MifareClassic;-><init>(Landroid/nfc/Tag;)V
+Landroid/nfc/tech/MifareClassic;->authenticate(I[BZ)Z
+Landroid/nfc/tech/MifareClassic;->isEmulated()Z
+Landroid/nfc/tech/MifareClassic;->MAX_BLOCK_COUNT:I
+Landroid/nfc/tech/MifareClassic;->MAX_SECTOR_COUNT:I
+Landroid/nfc/tech/MifareClassic;->mIsEmulated:Z
+Landroid/nfc/tech/MifareClassic;->mSize:I
+Landroid/nfc/tech/MifareClassic;->mType:I
+Landroid/nfc/tech/MifareClassic;->TAG:Ljava/lang/String;
+Landroid/nfc/tech/MifareClassic;->validateBlock(I)V
+Landroid/nfc/tech/MifareClassic;->validateSector(I)V
+Landroid/nfc/tech/MifareClassic;->validateValueOperand(I)V
+Landroid/nfc/tech/MifareUltralight;-><init>(Landroid/nfc/Tag;)V
+Landroid/nfc/tech/MifareUltralight;->EXTRA_IS_UL_C:Ljava/lang/String;
+Landroid/nfc/tech/MifareUltralight;->MAX_PAGE_COUNT:I
+Landroid/nfc/tech/MifareUltralight;->mType:I
+Landroid/nfc/tech/MifareUltralight;->NXP_MANUFACTURER_ID:I
+Landroid/nfc/tech/MifareUltralight;->TAG:Ljava/lang/String;
+Landroid/nfc/tech/MifareUltralight;->validatePageIndex(I)V
+Landroid/nfc/tech/Ndef;-><init>(Landroid/nfc/Tag;)V
+Landroid/nfc/tech/Ndef;->EXTRA_NDEF_CARDSTATE:Ljava/lang/String;
+Landroid/nfc/tech/Ndef;->EXTRA_NDEF_MAXLENGTH:Ljava/lang/String;
+Landroid/nfc/tech/Ndef;->EXTRA_NDEF_MSG:Ljava/lang/String;
+Landroid/nfc/tech/Ndef;->EXTRA_NDEF_TYPE:Ljava/lang/String;
+Landroid/nfc/tech/Ndef;->ICODE_SLI:Ljava/lang/String;
+Landroid/nfc/tech/Ndef;->mCardState:I
+Landroid/nfc/tech/Ndef;->mMaxNdefSize:I
+Landroid/nfc/tech/Ndef;->mNdefMsg:Landroid/nfc/NdefMessage;
+Landroid/nfc/tech/Ndef;->mNdefType:I
+Landroid/nfc/tech/Ndef;->NDEF_MODE_READ_ONLY:I
+Landroid/nfc/tech/Ndef;->NDEF_MODE_READ_WRITE:I
+Landroid/nfc/tech/Ndef;->NDEF_MODE_UNKNOWN:I
+Landroid/nfc/tech/Ndef;->TAG:Ljava/lang/String;
+Landroid/nfc/tech/Ndef;->TYPE_1:I
+Landroid/nfc/tech/Ndef;->TYPE_2:I
+Landroid/nfc/tech/Ndef;->TYPE_3:I
+Landroid/nfc/tech/Ndef;->TYPE_4:I
+Landroid/nfc/tech/Ndef;->TYPE_ICODE_SLI:I
+Landroid/nfc/tech/Ndef;->TYPE_MIFARE_CLASSIC:I
+Landroid/nfc/tech/Ndef;->TYPE_OTHER:I
+Landroid/nfc/tech/Ndef;->UNKNOWN:Ljava/lang/String;
+Landroid/nfc/tech/NdefFormatable;-><init>(Landroid/nfc/Tag;)V
+Landroid/nfc/tech/NdefFormatable;->format(Landroid/nfc/NdefMessage;Z)V
+Landroid/nfc/tech/NdefFormatable;->TAG:Ljava/lang/String;
+Landroid/nfc/tech/NfcA;-><init>(Landroid/nfc/Tag;)V
+Landroid/nfc/tech/NfcA;->EXTRA_ATQA:Ljava/lang/String;
+Landroid/nfc/tech/NfcA;->EXTRA_SAK:Ljava/lang/String;
+Landroid/nfc/tech/NfcA;->mAtqa:[B
+Landroid/nfc/tech/NfcA;->mSak:S
+Landroid/nfc/tech/NfcA;->TAG:Ljava/lang/String;
+Landroid/nfc/tech/NfcB;-><init>(Landroid/nfc/Tag;)V
+Landroid/nfc/tech/NfcB;->EXTRA_APPDATA:Ljava/lang/String;
+Landroid/nfc/tech/NfcB;->EXTRA_PROTINFO:Ljava/lang/String;
+Landroid/nfc/tech/NfcB;->mAppData:[B
+Landroid/nfc/tech/NfcB;->mProtInfo:[B
+Landroid/nfc/tech/NfcBarcode;-><init>(Landroid/nfc/Tag;)V
+Landroid/nfc/tech/NfcBarcode;->EXTRA_BARCODE_TYPE:Ljava/lang/String;
+Landroid/nfc/tech/NfcBarcode;->mType:I
+Landroid/nfc/tech/NfcF;-><init>(Landroid/nfc/Tag;)V
+Landroid/nfc/tech/NfcF;->EXTRA_PMM:Ljava/lang/String;
+Landroid/nfc/tech/NfcF;->EXTRA_SC:Ljava/lang/String;
+Landroid/nfc/tech/NfcF;->mManufacturer:[B
+Landroid/nfc/tech/NfcF;->mSystemCode:[B
+Landroid/nfc/tech/NfcF;->TAG:Ljava/lang/String;
+Landroid/nfc/tech/NfcV;-><init>(Landroid/nfc/Tag;)V
+Landroid/nfc/tech/NfcV;->EXTRA_DSFID:Ljava/lang/String;
+Landroid/nfc/tech/NfcV;->EXTRA_RESP_FLAGS:Ljava/lang/String;
+Landroid/nfc/tech/NfcV;->mDsfId:B
+Landroid/nfc/tech/NfcV;->mRespFlags:B
+Landroid/nfc/tech/TagTechnology;->ISO_DEP:I
+Landroid/nfc/tech/TagTechnology;->MIFARE_CLASSIC:I
+Landroid/nfc/tech/TagTechnology;->MIFARE_ULTRALIGHT:I
+Landroid/nfc/tech/TagTechnology;->NDEF:I
+Landroid/nfc/tech/TagTechnology;->NDEF_FORMATABLE:I
+Landroid/nfc/tech/TagTechnology;->NFC_A:I
+Landroid/nfc/tech/TagTechnology;->NFC_B:I
+Landroid/nfc/tech/TagTechnology;->NFC_BARCODE:I
+Landroid/nfc/tech/TagTechnology;->NFC_F:I
+Landroid/nfc/tech/TagTechnology;->NFC_V:I
+Landroid/nfc/tech/TagTechnology;->reconnect()V
+Landroid/nfc/TechListParcel;->CREATOR:Landroid/os/Parcelable$Creator;
+Landroid/nfc/TechListParcel;->getTechLists()[[Ljava/lang/String;
+Landroid/nfc/TechListParcel;->mTechLists:[[Ljava/lang/String;
+Landroid/nfc/TransceiveResult;-><init>(I[B)V
+Landroid/nfc/TransceiveResult;->CREATOR:Landroid/os/Parcelable$Creator;
+Landroid/nfc/TransceiveResult;->getResponseOrThrow()[B
+Landroid/nfc/TransceiveResult;->mResponseData:[B
+Landroid/nfc/TransceiveResult;->mResult:I
+Landroid/nfc/TransceiveResult;->RESULT_EXCEEDED_LENGTH:I
+Landroid/nfc/TransceiveResult;->RESULT_FAILURE:I
+Landroid/nfc/TransceiveResult;->RESULT_SUCCESS:I
+Landroid/nfc/TransceiveResult;->RESULT_TAGLOST:I
diff --git a/apex/hiddenapi/hiddenapi-max-target-r-loprio.txt b/apex/hiddenapi/hiddenapi-max-target-r-loprio.txt
new file mode 100644
index 0000000..f53aa1c
--- /dev/null
+++ b/apex/hiddenapi/hiddenapi-max-target-r-loprio.txt
@@ -0,0 +1 @@
+Landroid/nfc/INfcAdapter$Stub;->TRANSACTION_enable:I
diff --git a/com.android.nfc.xml b/com.android.nfc.xml
index 320d433..2046ab5 100644
--- a/com.android.nfc.xml
+++ b/com.android.nfc.xml
@@ -31,5 +31,9 @@
         <permission name="android.permission.DISPATCH_NFC_MESSAGE"/>
         <permission name="android.permission.NFC_SET_CONTROLLER_ALWAYS_ON"/>
         <permission name="android.permission.OVERRIDE_WIFI_CONFIG"/>
+        <permission name="android.permission.SHOW_CUSTOMIZED_RESOLVER"/>
+        <permission name="android.permission.DUMP"/>
+        <permission name="android.permission.QUERY_CLONED_APPS"/>
+        <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
     </privapp-permissions>
 </permissions>
diff --git a/flags/Android.bp b/flags/Android.bp
index 0ceb60b..8a201f1 100644
--- a/flags/Android.bp
+++ b/flags/Android.bp
@@ -15,6 +15,7 @@
 //
 
 package {
+    default_team: "trendy_team_fwk_nfc",
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
diff --git a/jarjar-rules.txt b/jarjar-rules.txt
index 655c09e..677424a 100644
--- a/jarjar-rules.txt
+++ b/jarjar-rules.txt
@@ -11,3 +11,33 @@
 # com.google.android.material_material
 rule com.google.android.material.** com.android.nfc.x.@0
 rule com.android.internal.util.FastXmlSerializer* com.android.nfc.x.@0
+
+# Used for proto debug dumping
+rule android.app.PendingIntentProto* com.android.nfc.x.@0
+rule android.content.ComponentNameProto* com.android.nfc.x.@0
+rule android.content.IntentProto* com.android.nfc.x.@0
+rule android.content.IntentFilterProto* com.android.nfc.x.@0
+rule android.content.AuthorityEntryProto* com.android.nfc.x.@0
+rule android.nfc.cardemulation.AidGroupProto* com.android.nfc.x.@0
+rule android.nfc.cardemulation.ApduServiceInfoProto* com.android.nfc.x.@0
+rule android.nfc.cardemulation.NfcFServiceInfoProto* com.android.nfc.x.@0
+rule android.nfc.NdefMessageProto* com.android.nfc.x.@0
+rule android.nfc.NdefRecordProto* com.android.nfc.x.@0
+rule com.android.nfc.cardemulation.CardEmulationManagerProto* com.android.nfc.x.@0
+rule com.android.nfc.cardemulation.RegisteredServicesCacheProto* com.android.nfc.x.@0
+rule com.android.nfc.cardemulation.RegisteredNfcFServicesCacheProto* com.android.nfc.x.@0
+rule com.android.nfc.cardemulation.PreferredServicesProto* com.android.nfc.x.@0
+rule com.android.nfc.cardemulation.EnabledNfcFServicesProto* com.android.nfc.x.@0
+rule com.android.nfc.cardemulation.RegisteredAidCacheProto* com.android.nfc.x.@0
+rule com.android.nfc.cardemulation.AidRoutingManagerProto* com.android.nfc.x.@0
+rule com.android.nfc.cardemulation.RegisteredT3tIdentifiersCacheProto* com.android.nfc.x.@0
+rule com.android.nfc.cardemulation.SystemCodeRoutingManagerProto* com.android.nfc.x.@0
+rule com.android.nfc.cardemulation.HostEmulationManagerProto* com.android.nfc.x.@0
+rule com.android.nfc.cardemulation.HostNfcFEmulationManagerProto* com.android.nfc.x.@0
+rule com.android.nfc.NfcServiceDumpProto* com.android.nfc.x.@0
+rule com.android.nfc.DiscoveryParamsProto* com.android.nfc.x.@0
+rule com.android.nfc.NfcDispatcherProto* com.android.nfc.x.@0
+rule android.os.PersistableBundleProto* com.android.nfc.x.@0
+
+# Core utils available for modules
+rule com.android.modules.utils.** com.android.nfc.x.@0
diff --git a/lint-baseline.xml b/lint-baseline.xml
new file mode 100644
index 0000000..0f1aefa
--- /dev/null
+++ b/lint-baseline.xml
@@ -0,0 +1,1742 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01">
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getUid`"
+        errorLine1="                UserHandle.getUserHandleForUid(info.serviceInfo.getUid()).getIdentifier(),"
+        errorLine2="                                                                ~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/AppChooserActivity.java"
+            line="181"
+            column="65"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getComponent`"
+        errorLine1="                info.serviceInfo.getComponent());"
+        errorLine2="                                 ~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/AppChooserActivity.java"
+            line="182"
+            column="34"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Cast from `ApduServiceInfo` to `Parcelable` requires API level 35 (current min is 34)"
+        errorLine1="        dialogIntent.putExtra(TapAgainDialog.EXTRA_APDU_SERVICE, info.serviceInfo);"
+        errorLine2="                                                                 ~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/AppChooserActivity.java"
+            line="185"
+            column="66"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getDescription`"
+        errorLine1="                CharSequence label = service.getDescription();"
+        errorLine2="                                             ~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/AppChooserActivity.java"
+            line="217"
+            column="46"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#loadLabel`"
+        errorLine1="                if (label == null) label = service.loadLabel(pm);"
+        errorLine2="                                                   ~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/AppChooserActivity.java"
+            line="218"
+            column="52"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#loadIcon`"
+        errorLine1="                Drawable icon = pm.getUserBadgedIcon(service.loadIcon(pm),"
+        errorLine2="                                                             ~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/AppChooserActivity.java"
+            line="220"
+            column="62"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getUid`"
+        errorLine1="                        UserHandle.getUserHandleForUid(service.getUid()));"
+        errorLine2="                                                               ~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/AppChooserActivity.java"
+            line="221"
+            column="64"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#loadBanner`"
+        errorLine1="                    banner = service.loadBanner(pm);"
+        errorLine2="                                     ~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/AppChooserActivity.java"
+            line="225"
+            column="38"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#hasCategory`"
+        errorLine1="                if (service.hasCategory(CardEmulation.CATEGORY_PAYMENT)"
+        errorLine2="                            ~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/CardEmulationManager.java"
+            line="372"
+            column="29"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getComponent`"
+        errorLine1="                            &amp;&amp; wasServicePreInstalled(pm, service.getComponent())) {"
+        errorLine2="                                                                  ~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/CardEmulationManager.java"
+            line="373"
+            column="67"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getComponent`"
+        errorLine1="                    lastFoundPaymentService = service.getComponent();"
+        errorLine2="                                                      ~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/CardEmulationManager.java"
+            line="375"
+            column="55"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.NfcFServiceInfo#getSystemCode`"
+        errorLine1='                if (serviceInfo.getSystemCode().equalsIgnoreCase("NULL") ||'
+        errorLine2="                                ~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/EnabledNfcFServices.java"
+            line="130"
+            column="33"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.NfcFServiceInfo#getNfcid2`"
+        errorLine1='                        serviceInfo.getNfcid2().equalsIgnoreCase("NULL") ||'
+        errorLine2="                                    ~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/EnabledNfcFServices.java"
+            line="131"
+            column="37"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.NfcFServiceInfo#getT3tPmm`"
+        errorLine1='                        serviceInfo.getT3tPmm().equalsIgnoreCase("NULL")) {'
+        errorLine2="                                    ~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/EnabledNfcFServices.java"
+            line="132"
+            column="37"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getUid`"
+        errorLine1="                        mStatsdUtils.setCardEmulationEventUid(defaultServiceInfo.getUid());"
+        errorLine2="                                                                                 ~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/HostEmulationManager.java"
+            line="247"
+            column="82"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#requiresUnlock`"
+        errorLine1="                    if ((defaultServiceInfo.requiresUnlock()"
+        errorLine2="                                            ~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/HostEmulationManager.java"
+            line="250"
+            column="45"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#requiresScreenOn`"
+        errorLine1="                    if (defaultServiceInfo.requiresScreenOn() &amp;&amp; !mPowerManager.isScreenOn()) {"
+        errorLine2="                                           ~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/HostEmulationManager.java"
+            line="262"
+            column="44"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#isOnHost`"
+        errorLine1="                    if (!defaultServiceInfo.isOnHost()) {"
+        errorLine2="                                            ~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/HostEmulationManager.java"
+            line="272"
+            column="45"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getComponent`"
+        errorLine1="                    resolvedService = defaultServiceInfo.getComponent();"
+        errorLine2="                                                         ~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/HostEmulationManager.java"
+            line="281"
+            column="58"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getComponent`"
+        errorLine1="                        if (mActiveServiceName.equals(serviceInfo.getComponent())) {"
+        errorLine2="                                                                  ~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/HostEmulationManager.java"
+            line="285"
+            column="67"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getUid`"
+        errorLine1="                        int uid = resolvedServiceInfo.getUid();"
+        errorLine2="                                                      ~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/HostEmulationManager.java"
+            line="310"
+            column="55"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getUid`"
+        errorLine1="                                UserHandle.getUserHandleForUid(resolvedServiceInfo.getUid());"
+        errorLine2="                                                                                   ~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/HostEmulationManager.java"
+            line="354"
+            column="84"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Cast from `ApduServiceInfo` to `Parcelable` requires API level 35 (current min is 34)"
+        errorLine1="        dialogIntent.putExtra(TapAgainDialog.EXTRA_APDU_SERVICE, service);"
+        errorLine2="                                                                 ~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/HostEmulationManager.java"
+            line="557"
+            column="66"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getUid`"
+        errorLine1="                UserHandle.getUserHandleForUid(service.getUid()));"
+        errorLine2="                                                       ~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/HostEmulationManager.java"
+            line="560"
+            column="56"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.NfcFServiceInfo#getComponent`"
+        errorLine1="                    resolvedServiceName = resolvedService.getComponent();"
+        errorLine2="                                                          ~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/HostNfcFEmulationManager.java"
+            line="124"
+            column="59"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.NfcFServiceInfo#getUid`"
+        errorLine1="                    int uid = resolvedService != null ? resolvedService.getUid() : -1;"
+        errorLine2="                                                                        ~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/HostNfcFEmulationManager.java"
+            line="146"
+            column="73"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.service.chooser.CustomChoosers#createNfcResolverIntent`"
+        errorLine1="                                CustomChoosers.createNfcResolverIntent(intent, null, filtered);"
+        errorLine2="                                               ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/NfcDispatcher.java"
+            line="312"
+            column="48"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.service.chooser.CustomChoosers#createNfcResolverIntent`"
+        errorLine1="                intent = CustomChoosers.createNfcResolverIntent("
+        errorLine2="                                        ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/NfcDispatcher.java"
+            line="921"
+            column="41"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.se.omapi.SeFrameworkInitializer#getSeServiceManager`"
+        errorLine1="            SeServiceManager manager = SeFrameworkInitializer.getSeServiceManager();"
+        errorLine2="                                                              ~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/NfcService.java"
+            line="938"
+            column="63"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.se.omapi.SeServiceManager#getSeManagerServiceRegisterer`"
+        errorLine1="                    manager.getSeManagerServiceRegisterer().get());"
+        errorLine2="                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/NfcService.java"
+            line="944"
+            column="29"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.se.omapi.SeServiceManager.ServiceRegisterer#get`"
+        errorLine1="                    manager.getSeManagerServiceRegisterer().get());"
+        errorLine2="                                                            ~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/NfcService.java"
+            line="944"
+            column="61"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#hasCategory`"
+        errorLine1="            if (serviceInfo.hasCategory(CardEmulation.CATEGORY_PAYMENT)) {"
+        errorLine2="                            ~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/PreferredServices.java"
+            line="301"
+            column="29"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getAids`"
+        errorLine1="            final List&lt;String&gt; otherAids = serviceInfo.getAids();"
+        errorLine2="                                                       ~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/PreferredServices.java"
+            line="311"
+            column="56"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getComponent`"
+        errorLine1='                    "service=" + service.getComponent() +'
+        errorLine2="                                         ~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredAidCache.java"
+            line="92"
+            column="42"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getComponent`"
+        errorLine1="            return service.equals(resolveInfo.defaultService.getComponent());"
+        errorLine2="                                                             ~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredAidCache.java"
+            line="249"
+            column="62"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getComponent`"
+        errorLine1="            return service.equals(resolveInfo.services.get(0).getComponent());"
+        errorLine2="                                                              ~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredAidCache.java"
+            line="251"
+            column="63"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getUid`"
+        errorLine1="            int userId = UserHandle.getUserHandleForUid(serviceAidInfo.service.getUid())"
+        errorLine2="                                                                               ~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredAidCache.java"
+            line="292"
+            column="80"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getComponent`"
+        errorLine1="            ComponentName componentName = serviceAidInfo.service.getComponent();"
+        errorLine2="                                                                 ~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredAidCache.java"
+            line="294"
+            column="66"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getComponent`"
+        errorLine1="                        serviceAidInfo.service.getComponent()"
+        errorLine2="                                               ~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredAidCache.java"
+            line="307"
+            column="48"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getComponent`"
+        errorLine1="                            serviceAidInfo.service.getComponent() +"
+        errorLine2="                                                   ~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredAidCache.java"
+            line="325"
+            column="52"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#isCategoryOtherServiceEnabled`"
+        errorLine1="                    if (serviceAidInfo.service.isCategoryOtherServiceEnabled()) {"
+        errorLine2="                                               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredAidCache.java"
+            line="328"
+            column="48"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getComponent`"
+        errorLine1="                        if (DBG) Log.d(TAG, serviceAidInfo.service.getComponent() +"
+        errorLine2="                                                                   ~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredAidCache.java"
+            line="329"
+            column="68"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getComponent`"
+        errorLine1='                                defaultWalletServices.get(0).getComponent() + " default.");'
+        errorLine2="                                                             ~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredAidCache.java"
+            line="350"
+            column="62"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getComponent`"
+        errorLine1='                        resolveInfo.services.get(0).getComponent() + " default.");'
+        errorLine2="                                                    ~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredAidCache.java"
+            line="362"
+            column="53"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getUid`"
+        errorLine1="            int userId = UserHandle.getUserHandleForUid(serviceAidInfo.service.getUid())"
+        errorLine2="                                                                               ~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredAidCache.java"
+            line="384"
+            column="80"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getComponent`"
+        errorLine1="            ComponentName componentName = serviceAidInfo.service.getComponent();"
+        errorLine2="                                                                 ~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredAidCache.java"
+            line="386"
+            column="66"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getComponent`"
+        errorLine1="                    serviceAidInfo.service.getComponent()"
+        errorLine2="                                           ~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredAidCache.java"
+            line="392"
+            column="44"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getComponent`"
+        errorLine1='                    aidDefaultInfo.foregroundDefault.service.getComponent() + " has foreground" +'
+        errorLine2="                                                             ~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredAidCache.java"
+            line="419"
+            column="62"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getComponent`"
+        errorLine1='                    aidDefaultInfo.paymentDefault.service.getComponent() + " is payment" +'
+        errorLine2="                                                          ~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredAidCache.java"
+            line="458"
+            column="59"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getComponent`"
+        errorLine1='                if (DBG) Log.d(TAG, "generateServiceMap component: " + service.getComponent());'
+        errorLine2="                                                                               ~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredAidCache.java"
+            line="526"
+            column="80"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getPrefixAids`"
+        errorLine1="                List&lt;String&gt; prefixAids = service.getPrefixAids();"
+        errorLine2="                                                  ~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredAidCache.java"
+            line="527"
+            column="51"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getSubsetAids`"
+        errorLine1="                List&lt;String&gt; subSetAids = service.getSubsetAids();"
+        errorLine2="                                                  ~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredAidCache.java"
+            line="528"
+            column="51"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getAids`"
+        errorLine1="                for (String aid : service.getAids()) {"
+        errorLine2="                                          ~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredAidCache.java"
+            line="530"
+            column="43"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getCategoryForAid`"
+        errorLine1="                    serviceAidInfo.category = service.getCategoryForAid(aid);"
+        errorLine2="                                                      ~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredAidCache.java"
+            line="580"
+            column="55"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getPrefixAids`"
+        errorLine1="            for (String prefixAid : service.getPrefixAids()) {"
+        errorLine2="                                            ~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredAidCache.java"
+            line="632"
+            column="45"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getUid`"
+        errorLine1="                       int userId = UserHandle.getUserHandleForUid(service.getUid())"
+        errorLine2="                                                                           ~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredAidCache.java"
+            line="637"
+            column="76"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getCategoryForAid`"
+        errorLine1="                               .equals(service.getCategoryForAid(prefixAid)) ||"
+        errorLine2="                                               ~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredAidCache.java"
+            line="640"
+            column="48"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getComponent`"
+        errorLine1="                               (service.getComponent().equals(mPreferredForegroundService) &amp;&amp;"
+        errorLine2="                                        ~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredAidCache.java"
+            line="641"
+            column="41"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getSubsetAids`"
+        errorLine1="                    for (String aid : resolveInfo.defaultService.getSubsetAids()) {"
+        errorLine2="                                                                 ~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredAidCache.java"
+            line="756"
+            column="66"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getUid`"
+        errorLine1="                                    getUserHandleForUid(resolveInfo.defaultService.getUid())."
+        errorLine2="                                                                                   ~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredAidCache.java"
+            line="759"
+            column="84"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getCategoryForAid`"
+        errorLine1="                                  equals(resolveInfo.defaultService.getCategoryForAid(aid))) ||"
+        errorLine2="                                                                    ~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredAidCache.java"
+            line="762"
+            column="69"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getComponent`"
+        errorLine1="                                    (resolveInfo.defaultService.getComponent()."
+        errorLine2="                                                                ~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredAidCache.java"
+            line="763"
+            column="65"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#isOnHost`"
+        errorLine1="                aidType.isOnHost = resolveInfo.defaultService.isOnHost();"
+        errorLine2="                                                              ~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredAidCache.java"
+            line="982"
+            column="63"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getOffHostSecureElement`"
+        errorLine1="                            resolveInfo.defaultService.getOffHostSecureElement();"
+        errorLine2="                                                       ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredAidCache.java"
+            line="985"
+            column="56"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#requiresUnlock`"
+        errorLine1="                boolean requiresUnlock = resolveInfo.defaultService.requiresUnlock();"
+        errorLine2="                                                                    ~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredAidCache.java"
+            line="988"
+            column="69"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#requiresScreenOn`"
+        errorLine1="                boolean requiresScreenOn = resolveInfo.defaultService.requiresScreenOn();"
+        errorLine2="                                                                      ~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredAidCache.java"
+            line="989"
+            column="71"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#isOnHost`"
+        errorLine1="                    aidType.isOnHost = resolveInfo.services.get(0).isOnHost();"
+        errorLine2="                                                                   ~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredAidCache.java"
+            line="1002"
+            column="68"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getOffHostSecureElement`"
+        errorLine1="                                resolveInfo.services.get(0).getOffHostSecureElement();"
+        errorLine2="                                                            ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredAidCache.java"
+            line="1005"
+            column="61"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#requiresUnlock`"
+        errorLine1="                boolean requiresUnlock = resolveInfo.services.get(0).requiresUnlock();"
+        errorLine2="                                                                     ~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredAidCache.java"
+            line="1009"
+            column="70"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#requiresScreenOn`"
+        errorLine1="                boolean requiresScreenOn = resolveInfo.services.get(0).requiresScreenOn();"
+        errorLine2="                                                                       ~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredAidCache.java"
+            line="1010"
+            column="72"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#isOnHost`"
+        errorLine1="                    onHost |= service.isOnHost();"
+        errorLine2="                                      ~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredAidCache.java"
+            line="1026"
+            column="39"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getOffHostSecureElement`"
+        errorLine1="                            offHostSE = service.getOffHostSecureElement();"
+        errorLine2="                                                ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredAidCache.java"
+            line="1029"
+            column="49"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#requiresUnlock`"
+        errorLine1="                            requiresUnlock = service.requiresUnlock();"
+        errorLine2="                                                     ~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredAidCache.java"
+            line="1030"
+            column="54"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#requiresScreenOn`"
+        errorLine1="                            requiresScreenOn = service.requiresScreenOn();"
+        errorLine2="                                                       ~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredAidCache.java"
+            line="1031"
+            column="56"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getOffHostSecureElement`"
+        errorLine1="                                service.getOffHostSecureElement())) {"
+        errorLine2="                                        ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredAidCache.java"
+            line="1033"
+            column="41"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#requiresUnlock`"
+        errorLine1="                        } else if (requiresUnlock != service.requiresUnlock()"
+        errorLine2="                                                             ~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredAidCache.java"
+            line="1041"
+            column="62"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#requiresScreenOn`"
+        errorLine1="                                || requiresScreenOn != service.requiresScreenOn()) {"
+        errorLine2="                                                               ~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredAidCache.java"
+            line="1042"
+            column="64"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#requiresScreenOn`"
+        errorLine1="                    requiresScreenOnServiceExist |= service.requiresScreenOn();"
+        errorLine2="                                                            ~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredAidCache.java"
+            line="1053"
+            column="61"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getComponent`"
+        errorLine1="                defaultServiceInfo.getComponent() : null;"
+        errorLine2="                                   ~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredAidCache.java"
+            line="1148"
+            column="36"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getDescription`"
+        errorLine1='                    " (Description: " + serviceInfo.getDescription() + ")\n");'
+        errorLine2="                                                    ~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredAidCache.java"
+            line="1156"
+            column="53"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getComponent`"
+        errorLine1="                    defaultServiceInfo.getComponent() : null;"
+        errorLine2="                                       ~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredAidCache.java"
+            line="1191"
+            column="40"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#dumpDebug`"
+        errorLine1="                serviceInfo.dumpDebug(proto);"
+        errorLine2="                            ~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredAidCache.java"
+            line="1199"
+            column="29"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.NfcFServiceInfo#getComponent`"
+        errorLine1="            if (service.getComponent().equals(componentName)) return true;"
+        errorLine2="                        ~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredNfcFServicesCache.java"
+            line="209"
+            column="25"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `new android.nfc.cardemulation.NfcFServiceInfo`"
+        errorLine1="                NfcFServiceInfo service = new NfcFServiceInfo(pm, resolvedService);"
+        errorLine2="                                          ~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredNfcFServicesCache.java"
+            line="270"
+            column="43"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.NfcFServiceInfo#getComponent`"
+        errorLine1="                userServices.services.put(service.getComponent(), service);"
+        errorLine2="                                                  ~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredNfcFServicesCache.java"
+            line="334"
+            column="51"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.NfcFServiceInfo#getComponent`"
+        errorLine1='                if (DBG) Log.d(TAG, "Added service: " + service.getComponent());'
+        errorLine2="                                                                ~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredNfcFServicesCache.java"
+            line="335"
+            column="65"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.NfcFServiceInfo#getComponent`"
+        errorLine1="                userServices.services.remove(service.getComponent());"
+        errorLine2="                                                     ~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredNfcFServicesCache.java"
+            line="338"
+            column="54"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.NfcFServiceInfo#getComponent`"
+        errorLine1='                if (DBG) Log.d(TAG, "Removed service: " + service.getComponent());'
+        errorLine2="                                                                  ~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredNfcFServicesCache.java"
+            line="339"
+            column="67"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.NfcFServiceInfo#getUid`"
+        errorLine1="                if (service == null || (service.getUid() != dynamicSystemCode.uid)) {"
+        errorLine2="                                                ~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredNfcFServicesCache.java"
+            line="350"
+            column="49"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.NfcFServiceInfo#setDynamicSystemCode`"
+        errorLine1="                    service.setDynamicSystemCode(dynamicSystemCode.systemCode);"
+        errorLine2="                            ~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredNfcFServicesCache.java"
+            line="354"
+            column="29"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.NfcFServiceInfo#getUid`"
+        errorLine1="                if (service == null || (service.getUid() != dynamicNfcid2.uid)) {"
+        errorLine2="                                                ~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredNfcFServicesCache.java"
+            line="366"
+            column="49"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.NfcFServiceInfo#setDynamicNfcid2`"
+        errorLine1="                    service.setDynamicNfcid2(dynamicNfcid2.nfcid2);"
+        errorLine2="                            ~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredNfcFServicesCache.java"
+            line="370"
+            column="29"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.NfcFServiceInfo#getNfcid2`"
+        errorLine1='                if (service.getNfcid2().equalsIgnoreCase("RANDOM")) {'
+        errorLine2="                            ~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredNfcFServicesCache.java"
+            line="388"
+            column="29"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.NfcFServiceInfo#setDynamicNfcid2`"
+        errorLine1="                    service.setDynamicNfcid2(randomNfcid2);"
+        errorLine2="                            ~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredNfcFServicesCache.java"
+            line="390"
+            column="29"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.NfcFServiceInfo#getUid`"
+        errorLine1="                            new DynamicNfcid2(service.getUid(), randomNfcid2);"
+        errorLine2="                                                      ~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredNfcFServicesCache.java"
+            line="392"
+            column="55"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.NfcFServiceInfo#getUid`"
+        errorLine1="            if (service.getUid() != uid) {"
+        errorLine2="                        ~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredNfcFServicesCache.java"
+            line="564"
+            column="25"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.NfcFServiceInfo#setDynamicSystemCode`"
+        errorLine1="                service.setDynamicSystemCode(systemCode);"
+        errorLine2="                        ~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredNfcFServicesCache.java"
+            line="585"
+            column="25"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.NfcFServiceInfo#getUid`"
+        errorLine1="            if (service.getUid() != uid) {"
+        errorLine2="                        ~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredNfcFServicesCache.java"
+            line="608"
+            column="25"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.NfcFServiceInfo#getSystemCode`"
+        errorLine1="            return service.getSystemCode();"
+        errorLine2="                           ~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredNfcFServicesCache.java"
+            line="612"
+            column="28"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.NfcFServiceInfo#getUid`"
+        errorLine1="            if (service.getUid() != uid) {"
+        errorLine2="                        ~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredNfcFServicesCache.java"
+            line="641"
+            column="25"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.NfcFServiceInfo#setDynamicNfcid2`"
+        errorLine1="                service.setDynamicNfcid2(nfcid2);"
+        errorLine2="                        ~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredNfcFServicesCache.java"
+            line="660"
+            column="25"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.NfcFServiceInfo#getUid`"
+        errorLine1="            if (service.getUid() != uid) {"
+        errorLine2="                        ~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredNfcFServicesCache.java"
+            line="683"
+            column="25"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.NfcFServiceInfo#getNfcid2`"
+        errorLine1="            return service.getNfcid2();"
+        errorLine2="                           ~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredNfcFServicesCache.java"
+            line="687"
+            column="28"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.NfcFServiceInfo#dump`"
+        errorLine1="                        service.dump(pFd, pw, args);"
+        errorLine2="                                ~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredNfcFServicesCache.java"
+            line="765"
+            column="33"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.NfcFServiceInfo#dumpDebug`"
+        errorLine1="                service.dumpDebug(proto);"
+        errorLine2="                        ~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredNfcFServicesCache.java"
+            line="791"
+            column="25"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getComponent`"
+        errorLine1="            if (service.getComponent().equals(serviceName)) return true;"
+        errorLine2="                        ~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredServicesCache.java"
+            line="261"
+            column="25"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#hasCategory`"
+        errorLine1="                if (service.hasCategory(category)) services.add(service);"
+        errorLine2="                            ~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredServicesCache.java"
+            line="291"
+            column="29"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `new android.nfc.cardemulation.ApduServiceInfo`"
+        errorLine1="                ApduServiceInfo service = new ApduServiceInfo(pm, resolvedService, onHost);"
+        errorLine2="                                          ~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredServicesCache.java"
+            line="344"
+            column="43"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getComponent`"
+        errorLine1='                if (DEBUG) Log.d(TAG, "Adding service: " + service.getComponent() +'
+        errorLine2="                                                                   ~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredServicesCache.java"
+            line="381"
+            column="68"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getAids`"
+        errorLine1='                        " AIDs: " + service.getAids());'
+        errorLine2="                                            ~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredServicesCache.java"
+            line="382"
+            column="45"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getComponent`"
+        errorLine1="                userServices.services.put(service.getComponent(), service);"
+        errorLine2="                                                  ~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredServicesCache.java"
+            line="383"
+            column="51"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getUid`"
+        errorLine1="                if (serviceInfo == null || (serviceInfo.getUid() != dynamicSettings.uid)) {"
+        errorLine2="                                                        ~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredServicesCache.java"
+            line="394"
+            column="57"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#setDynamicAidGroup`"
+        errorLine1="                        serviceInfo.setDynamicAidGroup(group);"
+        errorLine2="                                    ~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredServicesCache.java"
+            line="399"
+            column="37"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#setOffHostSecureElement`"
+        errorLine1="                        serviceInfo.setOffHostSecureElement(dynamicSettings.offHostSE);"
+        errorLine2="                                    ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredServicesCache.java"
+            line="402"
+            column="37"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getComponent`"
+        errorLine1='                Log.d(TAG, "update valid otherService: " + service.getComponent()'
+        errorLine2="                                                                   ~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredServicesCache.java"
+            line="455"
+            column="68"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getAids`"
+        errorLine1='                        + " AIDs: " + service.getAids());'
+        errorLine2="                                              ~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredServicesCache.java"
+            line="456"
+            column="47"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#hasCategory`"
+        errorLine1="                if (!service.hasCategory(CardEmulation.CATEGORY_OTHER)) {"
+        errorLine2="                             ~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredServicesCache.java"
+            line="457"
+            column="30"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getComponent`"
+        errorLine1="                ComponentName component = service.getComponent();"
+        errorLine2="                                                  ~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredServicesCache.java"
+            line="462"
+            column="51"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getUid`"
+        errorLine1="                    status = new OtherServiceStatus(service.getUid(), isChecked);"
+        errorLine2="                                                            ~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredServicesCache.java"
+            line="467"
+            column="61"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#setCategoryOtherServiceEnabled`"
+        errorLine1="                service.setCategoryOtherServiceEnabled(status.checked);"
+        errorLine2="                        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredServicesCache.java"
+            line="472"
+            column="25"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.AidGroup#createFromXml`"
+        errorLine1="                            AidGroup group = AidGroup.createFromXml(parser);"
+        errorLine2="                                                      ~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredServicesCache.java"
+            line="545"
+            column="55"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.AidGroup#getCategory`"
+        errorLine1="                                    dynSettings.aidGroups.put(group.getCategory(), group);"
+        errorLine2="                                                                    ~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredServicesCache.java"
+            line="561"
+            column="69"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.AidGroup#writeAsXml`"
+        errorLine1="                        group.writeAsXml(out);"
+        errorLine2="                              ~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredServicesCache.java"
+            line="690"
+            column="31"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getUid`"
+        errorLine1="            if (serviceInfo.getUid() != uid) {"
+        errorLine2="                            ~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredServicesCache.java"
+            line="778"
+            column="29"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#isOnHost`"
+        errorLine1="            if (offHostSE == null || serviceInfo.isOnHost()) {"
+        errorLine2="                                                 ~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredServicesCache.java"
+            line="786"
+            column="50"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#setOffHostSecureElement`"
+        errorLine1="            serviceInfo.setOffHostSecureElement(offHostSE);"
+        errorLine2="                        ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredServicesCache.java"
+            line="803"
+            column="25"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getUid`"
+        errorLine1="            if (serviceInfo.getUid() != uid) {"
+        errorLine2="                            ~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredServicesCache.java"
+            line="821"
+            column="29"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getOffHostSecureElement`"
+        errorLine1="            if (serviceInfo.isOnHost() || serviceInfo.getOffHostSecureElement() == null) {"
+        errorLine2="                                                      ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredServicesCache.java"
+            line="829"
+            column="55"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#isOnHost`"
+        errorLine1="            if (serviceInfo.isOnHost() || serviceInfo.getOffHostSecureElement() == null) {"
+        errorLine2="                            ~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredServicesCache.java"
+            line="829"
+            column="29"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#resetOffHostSecureElement`"
+        errorLine1="            serviceInfo.resetOffHostSecureElement();"
+        errorLine2="                        ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredServicesCache.java"
+            line="844"
+            column="25"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getUid`"
+        errorLine1="            if (serviceInfo.getUid() != uid) {"
+        errorLine2="                            ~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredServicesCache.java"
+            line="862"
+            column="29"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getUid`"
+        errorLine1="            if (serviceInfo.getUid() != uid) {"
+        errorLine2="                            ~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredServicesCache.java"
+            line="893"
+            column="29"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.AidGroup#getAids`"
+        errorLine1="            List&lt;String&gt; aids = aidGroup.getAids();"
+        errorLine2="                                         ~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredServicesCache.java"
+            line="903"
+            column="42"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#setDynamicAidGroup`"
+        errorLine1="            serviceInfo.setDynamicAidGroup(aidGroup);"
+        errorLine2="                        ~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredServicesCache.java"
+            line="910"
+            column="25"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.AidGroup#getCategory`"
+        errorLine1="            dynSettings.aidGroups.put(aidGroup.getCategory(), aidGroup);"
+        errorLine2="                                               ~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredServicesCache.java"
+            line="917"
+            column="48"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.AidGroup#getCategory`"
+        errorLine1="                dynSettings.aidGroups.remove(aidGroup.getCategory());"
+        errorLine2="                                                      ~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredServicesCache.java"
+            line="925"
+            column="55"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getUid`"
+        errorLine1="            if (serviceInfo.getUid() != uid) {"
+        errorLine2="                            ~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredServicesCache.java"
+            line="974"
+            column="29"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getDynamicAidGroupForCategory`"
+        errorLine1="            return serviceInfo.getDynamicAidGroupForCategory(category);"
+        errorLine2="                               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredServicesCache.java"
+            line="978"
+            column="32"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getUid`"
+        errorLine1="                if (serviceInfo.getUid() != uid) {"
+        errorLine2="                                ~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredServicesCache.java"
+            line="993"
+            column="33"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#removeDynamicAidGroupForCategory`"
+        errorLine1="                if (!serviceInfo.removeDynamicAidGroupForCategory(category)) {"
+        errorLine2="                                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredServicesCache.java"
+            line="998"
+            column="34"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getComponent`"
+        errorLine1="        OtherServiceStatus status = userServices.others.get(service.getComponent());"
+        errorLine2="                                                                    ~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredServicesCache.java"
+            line="1036"
+            column="69"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getComponent`"
+        errorLine1='            Log.d(TAG, service.getComponent() + " status is could not be null");'
+        errorLine2="                               ~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredServicesCache.java"
+            line="1039"
+            column="32"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#isCategoryOtherServiceEnabled`"
+        errorLine1="        if (service.isCategoryOtherServiceEnabled() == checked) {"
+        errorLine2="                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredServicesCache.java"
+            line="1043"
+            column="21"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#setCategoryOtherServiceEnabled`"
+        errorLine1="        service.setCategoryOtherServiceEnabled(checked);"
+        errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredServicesCache.java"
+            line="1048"
+            column="17"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#dump`"
+        errorLine1="                        service.dump(pFd, pw, args);"
+        errorLine2="                                ~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredServicesCache.java"
+            line="1066"
+            column="33"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#dumpDebug`"
+        errorLine1="            service.dumpDebug(proto);"
+        errorLine2="                    ~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredServicesCache.java"
+            line="1091"
+            column="21"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.NfcFServiceInfo#getComponent`"
+        errorLine1="                if (mEnabledForegroundService.equals(service.getComponent())) {"
+        errorLine2="                                                             ~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredT3tIdentifiersCache.java"
+            line="126"
+            column="62"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.NfcFServiceInfo#getSystemCode`"
+        errorLine1='                    if (!service.getSystemCode().equalsIgnoreCase("NULL") &amp;&amp;'
+        errorLine2="                                 ~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredT3tIdentifiersCache.java"
+            line="127"
+            column="34"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.NfcFServiceInfo#getNfcid2`"
+        errorLine1='                            !service.getNfcid2().equalsIgnoreCase("NULL")) {'
+        errorLine2="                                     ~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredT3tIdentifiersCache.java"
+            line="128"
+            column="38"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.NfcFServiceInfo#getNfcid2`"
+        errorLine1="                        mForegroundT3tIdentifiersCache.put(service.getNfcid2(), service);"
+        errorLine2="                                                                   ~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredT3tIdentifiersCache.java"
+            line="129"
+            column="68"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.NfcFServiceInfo#getComponent`"
+        errorLine1='                        "/" + entry.getValue().getComponent().toString());'
+        errorLine2="                                               ~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredT3tIdentifiersCache.java"
+            line="142"
+            column="48"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.NfcFServiceInfo#getNfcid2`"
+        errorLine1="                    entry.getValue().getSystemCode(), entry.getValue().getNfcid2(), entry.getValue().getT3tPmm()));"
+        errorLine2="                                                                       ~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredT3tIdentifiersCache.java"
+            line="169"
+            column="72"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.NfcFServiceInfo#getSystemCode`"
+        errorLine1="                    entry.getValue().getSystemCode(), entry.getValue().getNfcid2(), entry.getValue().getT3tPmm()));"
+        errorLine2="                                     ~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredT3tIdentifiersCache.java"
+            line="169"
+            column="38"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.NfcFServiceInfo#getT3tPmm`"
+        errorLine1="                    entry.getValue().getSystemCode(), entry.getValue().getNfcid2(), entry.getValue().getT3tPmm()));"
+        errorLine2="                                                                                                     ~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredT3tIdentifiersCache.java"
+            line="169"
+            column="102"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.NfcFServiceInfo#dump`"
+        errorLine1="                entry.getValue().dump(pFd, pw, args);"
+        errorLine2="                                 ~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredT3tIdentifiersCache.java"
+            line="245"
+            column="34"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.NfcFServiceInfo#dumpDebug`"
+        errorLine1="            serviceInfo.dumpDebug(proto);"
+        errorLine2="                        ~~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/RegisteredT3tIdentifiersCache.java"
+            line="269"
+            column="25"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#loadIcon`"
+        errorLine1="        Drawable icon = pm.getUserBadgedIcon(serviceInfo.loadIcon(pm),"
+        errorLine2="                                                         ~~~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/TapAgainDialog.java"
+            line="85"
+            column="58"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getUid`"
+        errorLine1="                UserHandle.getUserHandleForUid(serviceInfo.getUid()));"
+        errorLine2="                                                           ~~~~~~">
+        <location
+            file="packages/apps/Nfc/src/com/android/nfc/cardemulation/TapAgainDialog.java"
+            line="86"
+            column="60"/>
+    </issue>
+
+</issues>
\ No newline at end of file
diff --git a/nci/jni/Android.bp b/nci/jni/Android.bp
index 88d38a2..ab3e5e9 100644
--- a/nci/jni/Android.bp
+++ b/nci/jni/Android.bp
@@ -1,4 +1,5 @@
 package {
+    default_team: "trendy_team_fwk_nfc",
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
@@ -59,7 +60,7 @@
         debuggable: {
             cflags: [
                 "-DDCHECK_ALWAYS_ON",
-                "-DDTA_ENABLED"
+                "-DDTA_ENABLED",
             ],
         },
     },
@@ -70,8 +71,8 @@
     },
     stl: "libc++_static",
     apex_available: [
-         "//apex_available:platform",
-         "com.android.nfcservices"
+        "//apex_available:platform",
+        "com.android.nfcservices",
     ],
     required: [
         // Provide a default libnfc-nci.conf in /system/etc for devices that
@@ -97,7 +98,7 @@
     ],
 
     header_libs: [
-        "jni_headers"
+        "jni_headers",
     ],
 
     include_dirs: [
diff --git a/nci/jni/JavaClassConstants.h b/nci/jni/JavaClassConstants.h
index b619e74..dfe7687 100644
--- a/nci/jni/JavaClassConstants.h
+++ b/nci/jni/JavaClassConstants.h
@@ -29,7 +29,8 @@
 
 extern jmethodID gCachedNfcManagerNotifyEeUpdated;
 
-extern const char* gNativeP2pDeviceClassName;
+extern jmethodID gCachedNfcManagerNotifyWlcStopped;
+
 extern const char* gNativeNfcTagClassName;
 extern const char* gNativeNfcManagerClassName;
 }  // namespace android
diff --git a/nci/jni/NativeNfcManager.cpp b/nci/jni/NativeNfcManager.cpp
index c5478d8..932faa6 100644
--- a/nci/jni/NativeNfcManager.cpp
+++ b/nci/jni/NativeNfcManager.cpp
@@ -26,6 +26,7 @@
 
 #include "HciEventManager.h"
 #include "JavaClassConstants.h"
+#include "NativeWlcManager.h"
 #include "NfcAdaptation.h"
 #ifdef DTA_ENABLED
 #include "NfcDta.h"
@@ -96,10 +97,9 @@
 jmethodID gCachedNfcManagerNotifyEeUpdated;
 jmethodID gCachedNfcManagerNotifyHwErrorReported;
 jmethodID gCachedNfcManagerNotifyPollingLoopFrame;
+jmethodID gCachedNfcManagerNotifyWlcStopped;
 jmethodID gCachedNfcManagerNotifyVendorSpecificEvent;
 jmethodID gCachedNfcManagerNotifyCommandTimeout;
-const char* gNativeP2pDeviceClassName =
-    "com/android/nfc/dhimpl/NativeP2pDevice";
 const char* gNativeNfcTagClassName = "com/android/nfc/dhimpl/NativeNfcTag";
 const char* gNativeNfcManagerClassName =
     "com/android/nfc/dhimpl/NativeNfcManager";
@@ -138,7 +138,9 @@
 static jint sLfT3tMax = 0;
 static bool sRoutingInitialized = false;
 static bool sIsRecovering = false;
+static bool sIsAlwaysPolling = false;
 static std::vector<uint8_t> sRawVendorCmdResponse;
+static bool sEnableVendorNciNotifications = false;
 
 #define CONFIG_UPDATE_TECH_MASK (1 << 1)
 #define DEFAULT_TECH_MASK                                                  \
@@ -157,17 +159,21 @@
 static tNFA_STATUS startPolling_rfDiscoveryDisabled(
     tNFA_TECHNOLOGY_MASK tech_mask);
 static void nfcManager_doSetScreenState(JNIEnv* e, jobject o,
-                                        jint screen_state_mask);
+                                        jint screen_state_mask,
+                                        jboolean alwaysPoll);
 static jboolean nfcManager_doSetPowerSavingMode(JNIEnv* e, jobject o,
                                                 bool flag);
 static void sendRawVsCmdCallback(uint8_t event, uint16_t param_len,
                                  uint8_t* p_param);
+static jbyteArray nfcManager_getProprietaryCaps(JNIEnv* e, jobject o);
 tNFA_STATUS gVSCmdStatus = NFA_STATUS_OK;
 uint16_t gCurrentConfigLen;
 uint8_t gConfig[256];
+std::vector<uint8_t> gCaps(0);
 static int prevScreenState = NFA_SCREEN_STATE_OFF_LOCKED;
 static int NFA_SCREEN_POLLING_TAG_MASK = 0x10;
 static bool gIsDtaEnabled = false;
+static bool gObserveModeEnabled = false;
 /////////////////////////////////////////////////////////////
 /////////////////////////////////////////////////////////////
 
@@ -175,7 +181,7 @@
 void initializeGlobalDebugEnabledFlag() {
   bool nfc_debug_enabled =
       (NfcConfig::getUnsigned(NAME_NFC_DEBUG_ENABLED, 1) != 0) ||
-      property_get_bool("persist.nfc.debug_enabled", false);
+      property_get_bool("persist.nfc.debug_enabled", true);
 
   android::base::SetMinimumLogSeverity(nfc_debug_enabled ? android::base::DEBUG
                                                          : android::base::INFO);
@@ -380,7 +386,9 @@
       if (!isListenMode(eventData->activated) &&
           (prevScreenState == NFA_SCREEN_STATE_OFF_LOCKED ||
            prevScreenState == NFA_SCREEN_STATE_OFF_UNLOCKED)) {
-        NFA_Deactivate(FALSE);
+        if (!sIsAlwaysPolling) {
+          NFA_Deactivate(FALSE);
+        }
       }
 
       NfcTag::getInstance().connectionEventHandler(connEvent, eventData);
@@ -606,9 +614,13 @@
 
   gCachedNfcManagerNotifyPollingLoopFrame =
       e->GetMethodID(cls.get(), "notifyPollingLoopFrame", "(I[B)V");
+
   gCachedNfcManagerNotifyVendorSpecificEvent =
       e->GetMethodID(cls.get(), "notifyVendorSpecificEvent", "(II[B)V");
 
+  gCachedNfcManagerNotifyWlcStopped =
+      e->GetMethodID(cls.get(), "notifyWlcStopped", "(I)V");
+
   gCachedNfcManagerNotifyCommandTimeout =
       e->GetMethodID(cls.get(), "notifyCommandTimeout", "()V");
 
@@ -618,12 +630,6 @@
     return JNI_FALSE;
   }
 
-  if (nfc_jni_cache_object(e, gNativeP2pDeviceClassName,
-                           &(nat->cached_P2pDevice)) == -1) {
-    LOG(ERROR) << StringPrintf("%s: fail cache NativeP2pDevice", __func__);
-    return JNI_FALSE;
-  }
-
   LOG(DEBUG) << StringPrintf("%s: exit", __func__);
   return JNI_TRUE;
 }
@@ -723,13 +729,13 @@
                                    __func__);
 
       struct nfc_jni_native_data* nat = getNative(NULL, NULL);
-      JNIEnv* e = NULL;
-      ScopedAttach attach(nat->vm, &e);
-      if (e == NULL) {
-        LOG(ERROR) << StringPrintf("jni env is null");
-        return;
-      }
       if (recovery_option && nat != NULL) {
+        JNIEnv* e = NULL;
+        ScopedAttach attach(nat->vm, &e);
+        if (e == NULL) {
+          LOG(ERROR) << StringPrintf("jni env is null");
+          return;
+        }
         LOG(ERROR) << StringPrintf("%s: toggle NFC state to recovery nfc",
                                    __func__);
         sIsRecovering = true;
@@ -804,8 +810,14 @@
         }
         PowerSwitch::getInstance().initialize(PowerSwitch::UNKNOWN_LEVEL);
         LOG(ERROR) << StringPrintf("%s: crash NFC service", __func__);
-        e->CallVoidMethod(nat->manager,
-                          android::gCachedNfcManagerNotifyCommandTimeout);
+        if (nat != NULL) {
+          JNIEnv* e = NULL;
+          ScopedAttach attach(nat->vm, &e);
+          if (e != NULL) {
+            e->CallVoidMethod(nat->manager,
+                              android::gCachedNfcManagerNotifyCommandTimeout);
+          }
+        }
         //////////////////////////////////////////////
         // crash the NFC service process so it can restart automatically
         abort();
@@ -940,6 +952,27 @@
     case NCI_MSG_PROP_ANDROID: {
       uint8_t android_sub_opcode = p_param[3];
       switch (android_sub_opcode) {
+        case NCI_QUERY_ANDROID_PASSIVE_OBSERVE: {
+          gObserveModeEnabled = p_param[5];
+          LOG(INFO) << StringPrintf("Query Observe mode state is %s",
+                                    gObserveModeEnabled ? "TRUE" : "FALSE");
+        }
+          FALLTHROUGH_INTENDED;
+        case NCI_ANDROID_PASSIVE_OBSERVE: {
+          gVSCmdStatus = p_param[4];
+          LOG(INFO) << StringPrintf("Observe mode RSP: status: %x",
+                                    gVSCmdStatus);
+          SyncEventGuard guard(gNfaVsCommand);
+          gNfaVsCommand.notifyOne();
+        } break;
+        case NCI_ANDROID_GET_CAPS: {
+          gVSCmdStatus = p_param[4];
+          SyncEventGuard guard(gNfaVsCommand);
+          u_int16_t android_version = *(u_int16_t*)&p_param[5];
+          u_int8_t len = p_param[7];
+          gCaps.assign(p_param + 8, p_param + 8 + len);
+          gNfaVsCommand.notifyOne();
+        } break;
         case NCI_ANDROID_POLLING_FRAME_NTF: {
           struct nfc_jni_native_data* nat = getNative(NULL, NULL);
           if (!nat) {
@@ -975,34 +1008,73 @@
       }
     } break;
     default: {
-      struct nfc_jni_native_data* nat = getNative(NULL, NULL);
-      if (!nat) {
-        LOG(ERROR) << StringPrintf("%s: cached nat is null", __FUNCTION__);
-        return;
+      if (sEnableVendorNciNotifications) {
+        struct nfc_jni_native_data* nat = getNative(NULL, NULL);
+        if (!nat) {
+          LOG(ERROR) << StringPrintf("%s: cached nat is null", __FUNCTION__);
+          return;
+        }
+        JNIEnv* e = NULL;
+        ScopedAttach attach(nat->vm, &e);
+        if (e == NULL) {
+          LOG(ERROR) << StringPrintf("%s: jni env is null", __FUNCTION__);
+          return;
+        }
+        ScopedLocalRef<jobject> dataJavaArray(e, e->NewByteArray(param_len));
+        if (dataJavaArray.get() == NULL) {
+          LOG(ERROR) << StringPrintf("%s: fail allocate array", __FUNCTION__);
+          return;
+        }
+        e->SetByteArrayRegion((jbyteArray)dataJavaArray.get(), 0, param_len,
+                              (jbyte*)(p_param));
+        if (e->ExceptionCheck()) {
+          e->ExceptionClear();
+          LOG(ERROR) << StringPrintf("%s failed to fill array", __FUNCTION__);
+          return;
+        }
+        e->CallVoidMethod(nat->manager,
+                          android::gCachedNfcManagerNotifyVendorSpecificEvent,
+                          (jint)event, (jint)param_len, dataJavaArray.get());
       }
-      JNIEnv* e = NULL;
-      ScopedAttach attach(nat->vm, &e);
-      if (e == NULL) {
-        LOG(ERROR) << StringPrintf("%s: jni env is null", __FUNCTION__);
-      return;
-      }
-      ScopedLocalRef<jobject> dataJavaArray(e, e->NewByteArray(param_len));
-      if (dataJavaArray.get() == NULL) {
-        LOG(ERROR) << StringPrintf("%s: fail allocate array", __FUNCTION__);
-        return;
-      }
-      e->SetByteArrayRegion((jbyteArray)dataJavaArray.get(), 0, param_len, (jbyte*)(p_param));
-      if (e->ExceptionCheck()) {
-        e->ExceptionClear();
-        LOG(ERROR) << StringPrintf("%s failed to fill array", __FUNCTION__);
-        return;
-      }
-      e->CallVoidMethod(nat->manager, android::gCachedNfcManagerNotifyVendorSpecificEvent,
-                        (jint)event, (jint)param_len, dataJavaArray.get());
     } break;
   }
 }
 
+static jboolean isObserveModeSupported(JNIEnv* e, jobject o) {
+  ScopedLocalRef<jclass> cls(e, e->GetObjectClass(o));
+  jmethodID isSupported =
+      e->GetMethodID(cls.get(), "isObserveModeSupported", "()Z");
+  return e->CallBooleanMethod(o, isSupported);
+}
+
+static jboolean nfcManager_isObserveModeEnabled(JNIEnv* e, jobject o) {
+  if (isObserveModeSupported(e, o) == JNI_FALSE) {
+    return false;
+  }
+
+  uint8_t cmd[] = {(NCI_MT_CMD << NCI_MT_SHIFT) | NCI_GID_PROP,
+                   NCI_MSG_PROP_ANDROID,
+                   NCI_QUERY_ANDROID_PASSIVE_OBSERVE_PARAM_SIZE,
+                   NCI_QUERY_ANDROID_PASSIVE_OBSERVE};
+  SyncEventGuard guard(gNfaVsCommand);
+  tNFA_STATUS status = NFA_SendRawVsCommand(sizeof(cmd), cmd, nfaVSCallback);
+
+  if (status == NFA_STATUS_OK) {
+    if (!gNfaVsCommand.wait(1000)) {
+      LOG(ERROR) << StringPrintf(
+          "%s: Timed out waiting for a response to get observe mode ",
+          __FUNCTION__);
+      gVSCmdStatus = NFA_STATUS_FAILED;
+    }
+  } else {
+    LOG(DEBUG) << StringPrintf("%s: Failed to get observe mode ", __FUNCTION__);
+  }
+  LOG(DEBUG) << StringPrintf(
+      "%s: returning %s", __FUNCTION__,
+      (gObserveModeEnabled != JNI_FALSE ? "TRUE" : "FALSE"));
+  return gObserveModeEnabled;
+}
+
 static void nfaSendRawVsCmdCallback(uint8_t event, uint16_t param_len,
                                     uint8_t* p_param) {
   if (param_len == 5) {
@@ -1014,7 +1086,21 @@
   gNfaVsCommand.notifyOne();
 }
 
-static jboolean nfcManager_setObserveMode(JNIEnv* e, jobject, jboolean enable) {
+static jboolean nfcManager_setObserveMode(JNIEnv* e, jobject o,
+                                          jboolean enable) {
+  if (isObserveModeSupported(e, o) == JNI_FALSE) {
+    return false;
+  }
+
+  if ((gObserveModeEnabled == enable) &&
+      ((enable != JNI_FALSE) ==
+       (nfcManager_isObserveModeEnabled(e, o) != JNI_FALSE))) {
+    LOG(DEBUG) << StringPrintf(
+        "%s: called with %s but it is already %s, returning early",
+        __FUNCTION__, (enable != JNI_FALSE ? "TRUE" : "FALSE"),
+        (gObserveModeEnabled != JNI_FALSE ? "TRUE" : "FALSE"));
+    return true;
+  }
   bool reenbleDiscovery = false;
   if (sRfEnabled) {
     startRfDiscovery(false);
@@ -1022,24 +1108,42 @@
   }
   uint8_t cmd[] = {
       (NCI_MT_CMD << NCI_MT_SHIFT) | NCI_GID_PROP, NCI_MSG_PROP_ANDROID,
-      NCI_ANDROID_PASSIVE_OBSERVER_PARAM_SIZE, NCI_ANDROID_PASSIVE_OBSERVER,
+      NCI_ANDROID_PASSIVE_OBSERVE_PARAM_SIZE, NCI_ANDROID_PASSIVE_OBSERVE,
       static_cast<uint8_t>(enable != JNI_FALSE
-                               ? NCI_ANDROID_PASSIVE_OBSERVER_PARAM_ENABLE
-                               : NCI_ANDROID_PASSIVE_OBSERVER_PARAM_DISABLE)};
+                               ? NCI_ANDROID_PASSIVE_OBSERVE_PARAM_ENABLE
+                               : NCI_ANDROID_PASSIVE_OBSERVE_PARAM_DISABLE)};
+  {
+    SyncEventGuard guard(gNfaVsCommand);
+    tNFA_STATUS status = NFA_SendRawVsCommand(sizeof(cmd), cmd, nfaVSCallback);
 
-  tNFA_STATUS status =
-      NFA_SendRawVsCommand(sizeof(cmd), cmd, nfaSendRawVsCmdCallback);
-
-  if (status == NFA_STATUS_OK) {
-    gNfaVsCommand.wait();
-  } else {
-    LOG(DEBUG) << StringPrintf("%s: Failed to set observe mode ", __FUNCTION__);
-    gVSCmdStatus = NFA_STATUS_FAILED;
+    if (status == NFA_STATUS_OK) {
+      if (!gNfaVsCommand.wait(1000)) {
+        LOG(ERROR) << StringPrintf(
+            "%s: Timed out waiting for a response to set observe mode ",
+            __FUNCTION__);
+        gVSCmdStatus = NFA_STATUS_FAILED;
+      }
+    } else {
+      LOG(DEBUG) << StringPrintf("%s: Failed to set observe mode ",
+                                 __FUNCTION__);
+      gVSCmdStatus = NFA_STATUS_FAILED;
+    }
   }
   if (reenbleDiscovery) {
     startRfDiscovery(true);
   }
-  return gVSCmdStatus == NFA_STATUS_OK;
+
+  if (gVSCmdStatus == NFA_STATUS_OK) {
+    gObserveModeEnabled = enable;
+  } else {
+    gObserveModeEnabled = nfcManager_isObserveModeEnabled(e, o);
+  }
+
+  LOG(DEBUG) << StringPrintf(
+      "%s: Set observe mode to %s with result %x, observe mode is now %s.",
+      __FUNCTION__, (enable != JNI_FALSE ? "TRUE" : "FALSE"), gVSCmdStatus,
+      (gObserveModeEnabled ? "enabled" : "disabled"));
+  return gObserveModeEnabled == enable;
 }
 
 /*******************************************************************************
@@ -1163,6 +1267,7 @@
         nativeNfcTag_registerNdefTypeHandler();
         NfcTag::getInstance().initialize(getNative(e, o));
         HciEventManager::getInstance().initialize(getNative(e, o));
+        NativeWlcManager::getInstance().initialize(getNative(e, o));
 
         /////////////////////////////////////////////////////////////////////////////////
         // Add extra configuration here (work-arounds, etc.)
@@ -1622,11 +1727,13 @@
 }
 
 static void nfcManager_doSetScreenState(JNIEnv* e, jobject o,
-                                        jint screen_state_mask) {
+                                        jint screen_state_mask,
+                                        jboolean alwaysPoll) {
   tNFA_STATUS status = NFA_STATUS_OK;
   uint8_t state = (screen_state_mask & NFA_SCREEN_STATE_MASK);
   uint8_t discovry_param =
       NCI_LISTEN_DH_NFCEE_ENABLE_MASK | NCI_POLLING_DH_ENABLE_MASK;
+  sIsAlwaysPolling = alwaysPoll;
 
   LOG(DEBUG) << StringPrintf(
       "%s: state = %d prevScreenState= %d, discovry_param = %d", __FUNCTION__,
@@ -1691,17 +1798,18 @@
         NCI_LISTEN_DH_NFCEE_ENABLE_MASK | NCI_POLLING_DH_ENABLE_MASK;
   }
 
-  SyncEventGuard guard(gNfaSetConfigEvent);
-  status = NFA_SetConfig(NCI_PARAM_ID_CON_DISCOVERY_PARAM,
-                         NCI_PARAM_LEN_CON_DISCOVERY_PARAM, &discovry_param);
-  if (status == NFA_STATUS_OK) {
-    gNfaSetConfigEvent.wait();
-  } else {
-    LOG(ERROR) << StringPrintf("%s: Failed to update CON_DISCOVER_PARAM",
-                               __FUNCTION__);
-    return;
+  if (!sIsAlwaysPolling) {
+    SyncEventGuard guard(gNfaSetConfigEvent);
+    status = NFA_SetConfig(NCI_PARAM_ID_CON_DISCOVERY_PARAM,
+                           NCI_PARAM_LEN_CON_DISCOVERY_PARAM, &discovry_param);
+    if (status == NFA_STATUS_OK) {
+      gNfaSetConfigEvent.wait();
+    } else {
+      LOG(ERROR) << StringPrintf("%s: Failed to update CON_DISCOVER_PARAM",
+                                 __FUNCTION__);
+      return;
+    }
   }
-
   // skip remaining SetScreenState tasks when trying to silent recover NFCC
   if (recovery_option && sIsRecovering) {
     prevScreenState = state;
@@ -1737,16 +1845,6 @@
   prevScreenState = state;
 }
 
-static void nfcManager_doEnableScreenOffSuspend(JNIEnv* e, jobject o) {
-  PowerSwitch::getInstance().setScreenOffPowerState(
-      PowerSwitch::POWER_STATE_FULL);
-}
-
-static void nfcManager_doDisableScreenOffSuspend(JNIEnv* e, jobject o) {
-  PowerSwitch::getInstance().setScreenOffPowerState(
-      PowerSwitch::POWER_STATE_OFF);
-}
-
 /*******************************************************************************
 **
 ** Function:        nfcManager_getIsoDepMaxTransceiveLength
@@ -1781,6 +1879,26 @@
 
 /*******************************************************************************
 **
+** Function:        nfcManager_IsMultiTag
+**
+** Description:     Check if it a multi tag case.
+**                  e: JVM environment.
+**                  o: Java object.
+**
+** Returns:         None.
+**
+*******************************************************************************/
+static bool nfcManager_isMultiTag() {
+  LOG(DEBUG) << StringPrintf("%s: enter mNumRfDiscId = %d", __func__,
+                             NfcTag::getInstance().mNumRfDiscId);
+  bool status = false;
+  if (NfcTag::getInstance().mNumRfDiscId > 1) status = true;
+  LOG(DEBUG) << StringPrintf("isMultiTag = %d", status);
+  return status;
+}
+
+/*******************************************************************************
+**
 ** Function:        nfcManager_doStartStopPolling
 **
 ** Description:     Start or stop NFC RF polling
@@ -1955,6 +2073,13 @@
   }
   nativeNfcTag_releaseRfInterfaceMutexLock();
 }
+
+static void ncfManager_nativeEnableVendorNciNotifications(JNIEnv* env,
+                                                          jobject o,
+                                                          jboolean enable) {
+  sEnableVendorNciNotifications = (enable == JNI_TRUE);
+}
+
 static jobject nfcManager_nativeSendRawVendorCmd(JNIEnv* env, jobject o,
                                                  jint mt, jint gid, jint oid,
                                                  jbyteArray payload) {
@@ -2070,13 +2195,7 @@
 
     {"doAbort", "(Ljava/lang/String;)V", (void*)nfcManager_doAbort},
 
-    {"doEnableScreenOffSuspend", "()V",
-     (void*)nfcManager_doEnableScreenOffSuspend},
-
-    {"doSetScreenState", "(I)V", (void*)nfcManager_doSetScreenState},
-
-    {"doDisableScreenOffSuspend", "()V",
-     (void*)nfcManager_doDisableScreenOffSuspend},
+    {"doSetScreenState", "(IZ)V", (void*)nfcManager_doSetScreenState},
 
     {"doDump", "(Ljava/io/FileDescriptor;)V", (void*)nfcManager_doDump},
 
@@ -2105,6 +2224,10 @@
 
     {"setObserveMode", "(Z)Z", (void*)nfcManager_setObserveMode},
 
+    {"isObserveModeEnabled", "()Z", (void*)nfcManager_isObserveModeEnabled},
+
+    {"isMultiTag", "()Z", (void*)nfcManager_isMultiTag},
+
     {"clearRoutingEntry", "(I)V", (void*)nfcManager_clearRoutingEntry},
 
     {"setIsoDepProtocolRoute", "(I)V",
@@ -2117,6 +2240,10 @@
     {"resetDiscoveryTech", "()V", (void*)nfcManager_resetDiscoveryTech},
     {"nativeSendRawVendorCmd", "(III[B)Lcom/android/nfc/NfcVendorNciResponse;",
      (void*)nfcManager_nativeSendRawVendorCmd},
+
+    {"getProprietaryCaps", "()[B", (void*)nfcManager_getProprietaryCaps},
+    {"enableVendorNciNotifications", "(Z)V",
+     (void*)ncfManager_nativeEnableVendorNciNotifications},
 };
 
 /*******************************************************************************
@@ -2331,4 +2458,26 @@
   return gVSCmdStatus == NFA_STATUS_OK;
 }
 
+static jbyteArray nfcManager_getProprietaryCaps(JNIEnv* e, jobject o) {
+  LOG(DEBUG) << StringPrintf("%s: enter; ", __func__);
+  uint8_t cmd[] = {(NCI_MT_CMD << NCI_MT_SHIFT) | NCI_GID_PROP,
+                   NCI_MSG_PROP_ANDROID, NCI_ANDROID_GET_CAPS_PARAM_SIZE,
+                   NCI_ANDROID_GET_CAPS};
+  SyncEventGuard guard(gNfaVsCommand);
+
+  tNFA_STATUS status =
+      NFA_SendRawVsCommand(sizeof(cmd), cmd, nfaSendRawVsCmdCallback);
+  if (status == NFA_STATUS_OK) {
+    gNfaVsCommand.wait();
+  } else {
+    LOG(ERROR) << StringPrintf("%s: Failed to get caps", __func__);
+    gVSCmdStatus = NFA_STATUS_FAILED;
+  }
+  CHECK(e);
+  jbyteArray rtJavaArray = e->NewByteArray(gCaps.size());
+  CHECK(rtJavaArray);
+  e->SetByteArrayRegion(rtJavaArray, 0, gCaps.size(), (jbyte*)gCaps.data());
+  return rtJavaArray;
+}
+
 } /* namespace android */
diff --git a/nci/jni/NativeP2pDevice.cpp b/nci/jni/NativeP2pDevice.cpp
deleted file mode 100644
index efb8252..0000000
--- a/nci/jni/NativeP2pDevice.cpp
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2012 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.
- */
-
-#include <android-base/logging.h>
-#include <android-base/stringprintf.h>
-#include <log/log.h>
-#include <nativehelper/JNIHelp.h>
-
-#include "JavaClassConstants.h"
-#include "NfcJniUtil.h"
-
-using android::base::StringPrintf;
-
-namespace android {
-
-static jboolean nativeP2pDeviceDoConnect(JNIEnv*, jobject) {
-  LOG(DEBUG) << StringPrintf("%s", __func__);
-  return JNI_TRUE;
-}
-
-static jboolean nativeP2pDeviceDoDisconnect(JNIEnv*, jobject) {
-  LOG(DEBUG) << StringPrintf("%s", __func__);
-  return JNI_TRUE;
-}
-
-static jbyteArray nativeP2pDeviceDoTransceive(JNIEnv*, jobject, jbyteArray) {
-  LOG(DEBUG) << StringPrintf("%s", __func__);
-  return NULL;
-}
-
-static jbyteArray nativeP2pDeviceDoReceive(JNIEnv*, jobject) {
-  LOG(DEBUG) << StringPrintf("%s", __func__);
-  return NULL;
-}
-
-static jboolean nativeP2pDeviceDoSend(JNIEnv*, jobject, jbyteArray) {
-  LOG(DEBUG) << StringPrintf("%s", __func__);
-  return JNI_TRUE;
-}
-
-/*****************************************************************************
-**
-** Description:     JNI functions
-**
-*****************************************************************************/
-static JNINativeMethod gMethods[] = {
-    {"doConnect", "()Z", (void*)nativeP2pDeviceDoConnect},
-    {"doDisconnect", "()Z", (void*)nativeP2pDeviceDoDisconnect},
-    {"doTransceive", "([B)[B", (void*)nativeP2pDeviceDoTransceive},
-    {"doReceive", "()[B", (void*)nativeP2pDeviceDoReceive},
-    {"doSend", "([B)Z", (void*)nativeP2pDeviceDoSend},
-};
-
-/*******************************************************************************
-**
-** Function:        register_com_android_nfc_NativeP2pDevice
-**
-** Description:     Regisgter JNI functions with Java Virtual Machine.
-**                  e: Environment of JVM.
-**
-** Returns:         Status of registration.
-**
-*******************************************************************************/
-int register_com_android_nfc_NativeP2pDevice(JNIEnv* e) {
-  return jniRegisterNativeMethods(e, gNativeP2pDeviceClassName, gMethods,
-                                  NELEM(gMethods));
-}
-
-}  // namespace android
diff --git a/nci/jni/NativeWlcManager.cpp b/nci/jni/NativeWlcManager.cpp
new file mode 100644
index 0000000..eb5e140
--- /dev/null
+++ b/nci/jni/NativeWlcManager.cpp
@@ -0,0 +1,308 @@
+/*
+ *  Copyright (C) 2023 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.
+ */
+
+#include "NativeWlcManager.h"
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <cutils/properties.h>
+#include <errno.h>
+#include <nativehelper/JNIPlatformHelp.h>
+#include <nativehelper/ScopedLocalRef.h>
+#include <nativehelper/ScopedPrimitiveArray.h>
+#include <nativehelper/ScopedUtfChars.h>
+#include <semaphore.h>
+
+#include "JavaClassConstants.h"
+#include "NfcJniUtil.h"
+#include "SyncEvent.h"
+#include "nfa_api.h"
+
+using android::base::StringPrintf;
+
+/*****************************************************************************
+**
+** private variables and functions
+**
+*****************************************************************************/
+
+static void nfaWlcManagementCallback(tNFA_WLC_EVT wlcEvent,
+                                     tNFA_WLC_EVT_DATA* eventData);
+
+static SyncEvent sNfaWlcEnableEvent;  // event for NFA_WlcStart()
+static SyncEvent sNfaWlcEvent;        // event for NFA_Wlc...()
+
+static bool sIsWlcpStarted = false;
+
+Mutex gMutexWlc;
+
+const JNINativeMethod NativeWlcManager::sMethods[] = {
+
+    {"startWlcPowerTransfer", "(II)Z",
+     (void*)NativeWlcManager::com_android_nfc_wlc_chargeWlcListener},
+    {"enableWlc", "(I)Z",
+     (void*)NativeWlcManager::com_android_nfc_wlc_startWlcP},
+
+};
+
+/*******************************************************************************
+**
+** Function:        NativeWlcManager
+**
+** Description:     Initialize member variables.
+**
+** Returns:         None
+**
+*******************************************************************************/
+NativeWlcManager::NativeWlcManager()
+    : mNativeData(NULL), mIsWlcEnabled(false) {}
+
+/*******************************************************************************
+**
+** Function:        ~NativeWlcManager
+**
+** Description:     Release all resources.
+**
+** Returns:         None
+**
+*******************************************************************************/
+NativeWlcManager::~NativeWlcManager() {}
+
+/*******************************************************************************
+**
+** Function:        getInstance
+**
+** Description:     Get a reference to the singleton NativeWlcManager object.
+**
+** Returns:         Reference to NativeWlcManager object.
+**
+*******************************************************************************/
+NativeWlcManager& NativeWlcManager::getInstance() {
+  static NativeWlcManager manager;
+  return manager;
+}
+
+/*******************************************************************************
+**
+** Function:        initialize
+**
+** Description:     Reset member variables.
+**                  native: Native data.
+**
+** Returns:         None
+**
+*******************************************************************************/
+void NativeWlcManager::initialize(nfc_jni_native_data* native) {
+  tNFA_STATUS stat = NFA_STATUS_FAILED;
+
+  LOG(DEBUG) << StringPrintf("%s: enter", __func__);
+
+  mNativeData = native;
+  mIsWlcEnabled = false;
+
+  SyncEventGuard g(sNfaWlcEnableEvent);
+  // TODO: only do it once at NfcManager init if WLC allowed
+  stat = NFA_WlcEnable(nfaWlcManagementCallback);
+
+  if (stat == NFA_STATUS_OK) {
+    // TODO: get enable result to stop directly if failed
+    sNfaWlcEnableEvent.wait();
+    LOG(DEBUG) << StringPrintf("%s: enable Wlc module success", __func__);
+  } else {
+    LOG(ERROR) << StringPrintf("%s: fail enable Wlc module; error=0x%X",
+                               __func__, stat);
+  }
+}
+
+/*******************************************************************************
+**
+** Function:        notifyWlcCompletion
+**
+** Description:     Notify end of WLC procedure.
+**                  wpt_end_condition: End condition from NFCC.
+**
+** Returns:         None
+**
+*******************************************************************************/
+void NativeWlcManager::notifyWlcCompletion(uint8_t wpt_end_condition) {
+  JNIEnv* e = NULL;
+  ScopedAttach attach(mNativeData->vm, &e);
+  if (e == NULL) {
+    LOG(ERROR) << "jni env is null";
+    return;
+  }
+
+  LOG(DEBUG) << StringPrintf("%s: ", __func__);
+
+  e->CallVoidMethod(mNativeData->manager,
+                    android::gCachedNfcManagerNotifyWlcStopped,
+                    (int)wpt_end_condition);
+  if (e->ExceptionCheck()) {
+    e->ExceptionClear();
+    LOG(ERROR) << StringPrintf("fail notify");
+  }
+}
+
+/*******************************************************************************
+**
+** Function:        nfaWlcManagementCallback
+**
+** Description:     Receive Wlc management events from stack.
+**                  wlcEvent: Wlc-management event ID.
+**                  eventData: Data associated with event ID.
+**
+** Returns:         None
+**
+*******************************************************************************/
+void nfaWlcManagementCallback(tNFA_WLC_EVT wlcEvent,
+                              tNFA_WLC_EVT_DATA* eventData) {
+  LOG(DEBUG) << StringPrintf("%s: enter; event=0x%X", __func__, wlcEvent);
+
+  switch (wlcEvent) {
+    case NFA_WLC_ENABLE_RESULT_EVT:  // whether WLC module enabled
+    {
+      LOG(DEBUG) << StringPrintf("%s: NFA_WLC_ENABLE_RESULT_EVT: status = %u",
+                                 __func__, eventData->status);
+
+      SyncEventGuard guard(sNfaWlcEnableEvent);
+      sNfaWlcEnableEvent.notifyOne();
+    } break;
+
+    case NFA_WLC_START_RESULT_EVT:  // whether WLCP successfully started
+    {
+      LOG(DEBUG) << StringPrintf("%s: NFA_WLC_START_RESULT_EVT: status = %u",
+                                 __func__, eventData->status);
+
+      sIsWlcpStarted = eventData->status == NFA_STATUS_OK;
+      SyncEventGuard guard(sNfaWlcEvent);
+      sNfaWlcEvent.notifyOne();
+    } break;
+
+    case NFA_WLC_START_WPT_RESULT_EVT:  // whether WLC Power Transfer
+                                        // successfully started
+    {
+      LOG(DEBUG) << StringPrintf(
+          "%s: NFA_WLC_START_WPT_RESULT_EVT: status = %u", __func__,
+          eventData->status);
+
+      SyncEventGuard guard(sNfaWlcEvent);
+      sNfaWlcEvent.notifyOne();
+    } break;
+
+    case NFA_WLC_CHARGING_RESULT_EVT:  // notify completion of power transfer
+                                       // phase
+    {
+      LOG(DEBUG) << StringPrintf(
+          "%s: NFA_WLC_CHARGING_RESULT_EVT: End Condition = 0x%x", __func__,
+          eventData->wpt_end_cdt);
+
+      /* Return WPT end condition to service */
+      NativeWlcManager::getInstance().notifyWlcCompletion(
+          eventData->wpt_end_cdt);
+    } break;
+
+    default:
+      LOG(DEBUG) << StringPrintf("%s: unhandled event", __func__);
+      break;
+  }
+}
+
+/*******************************************************************************
+**
+** Function:        com_android_nfc_wlc_startWlcP
+**
+** Description:     Start WLC Poller
+**                  e: JVM environment.
+**                  mode: WLC mode
+**
+** Returns:         True if WLCP started done
+**
+*******************************************************************************/
+jboolean NativeWlcManager::com_android_nfc_wlc_startWlcP(JNIEnv* e, jobject,
+                                                         jint mode) {
+  tNFA_STATUS stat = NFA_STATUS_FAILED;
+
+  LOG(DEBUG) << StringPrintf("%s: enter", __func__);
+
+  gMutexWlc.lock();
+  SyncEventGuard g(sNfaWlcEvent);
+  stat = NFA_WlcStart(mode);
+
+  if (stat == NFA_STATUS_OK) {
+    LOG(DEBUG) << StringPrintf(
+        "%s: start Wlc Poller, wait for success confirmation", __func__);
+    sNfaWlcEvent.wait();
+  } else {
+    LOG(ERROR) << StringPrintf("%s: fail start WlcPoller; error=0x%X", __func__,
+                               stat);
+  }
+  gMutexWlc.unlock();
+  return sIsWlcpStarted ? JNI_TRUE : JNI_FALSE;
+}
+
+/*******************************************************************************
+**
+** Function:        com_android_nfc_wlc_chargeWlcListener
+**
+** Description:     Start charging WLC Listener
+**                  e: JVM environment.
+**                  power_adj_req:
+**                  wpt_time_int:
+**
+** Returns:         True if WLCL charging started properly
+**
+*******************************************************************************/
+jboolean NativeWlcManager::com_android_nfc_wlc_chargeWlcListener(
+    JNIEnv* e, jobject, jint power_adj_req, jint wpt_time_int) {
+  tNFA_STATUS stat = NFA_STATUS_FAILED;
+
+  LOG(DEBUG) << StringPrintf("%s: wpt_time_int = %d", __func__, wpt_time_int);
+
+  gMutexWlc.lock();
+  SyncEventGuard g(sNfaWlcEvent);
+  // TODO: condition call to sIsWlcpStarted
+  // TODO: limit the min of wpt_time_int
+  stat = NFA_WlcStartWPT((uint16_t)(power_adj_req & 0xFFFF), wpt_time_int);
+  if (stat == NFA_STATUS_OK) {
+    LOG(DEBUG) << StringPrintf(
+        "%s: charge Wlc Listener, wait for success confirmation", __func__);
+    sNfaWlcEvent.wait();
+  } else {
+    LOG(ERROR) << StringPrintf("%s: fail charge Wlc Listener; error=0x%X",
+                               __func__, stat);
+    gMutexWlc.unlock();
+    return false;
+  }
+  gMutexWlc.unlock();
+  return true;
+}
+
+/*******************************************************************************
+**
+** Function:        registerJniFunctions
+**
+** Description:     Register WLC feature JNI functions
+**                  e: JVM environment.
+**
+** Returns:         -1 if JNI register error
+**
+*******************************************************************************/
+int NativeWlcManager::registerJniFunctions(JNIEnv* e) {
+  static const char fn[] = "NativeWlcManager::registerJniFunctions";
+  LOG(DEBUG) << StringPrintf("%s", fn);
+  return jniRegisterNativeMethods(e, "com/android/nfc/wlc/NfcCharging",
+                                  sMethods, NELEM(sMethods));
+}
diff --git a/nci/jni/NativeWlcManager.h b/nci/jni/NativeWlcManager.h
new file mode 100644
index 0000000..57897c9
--- /dev/null
+++ b/nci/jni/NativeWlcManager.h
@@ -0,0 +1,124 @@
+/*
+ *  Copyright (C) 2023 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.
+ */
+
+#pragma once
+#include "NfcJniUtil.h"
+#include "nfa_wlc_api.h"
+
+/*****************************************************************************
+**
+**  Name:           NativeWlcManager
+**
+**  Description:    Manage Wlc activities at stack level.
+**
+*****************************************************************************/
+class NativeWlcManager {
+ public:
+  /*******************************************************************************
+  **
+  ** Function:        getInstance
+  **
+  ** Description:     Get the singleton of this object.
+  **
+  ** Returns:         Reference to this object.
+  **
+  *******************************************************************************/
+  static NativeWlcManager& getInstance();
+
+  /*******************************************************************************
+  **
+  ** Function:        initialize
+  **
+  ** Description:     Reset member variables.
+  **                  native: Native data.
+  **
+  ** Returns:         None
+  **
+  *******************************************************************************/
+  void initialize(nfc_jni_native_data* native);
+
+  /*******************************************************************************
+  **
+  ** Function:        registerJniFunctions
+  **
+  ** Description:     Register WLC JNI functions.
+  **
+  ** Returns:         None
+  **
+  *******************************************************************************/
+  int registerJniFunctions(JNIEnv* e);
+
+  /*******************************************************************************
+  **
+  ** Function:        notifyWlcCompletion
+  **
+  ** Description:     Notify end of WLC procedure.
+  **                  wpt_end_condition: End condition from NFCC.
+  **
+  ** Returns:         None
+  **
+  *******************************************************************************/
+  void notifyWlcCompletion(uint8_t wpt_end_condition);
+
+ private:
+  // Fields below are final after initialize()
+  nfc_jni_native_data* mNativeData;
+
+  bool mIsWlcEnabled = false;
+
+  /*******************************************************************************
+  **
+  ** Function:        NativeWlcManager
+  **
+  ** Description:     Initialize member variables.
+  **
+  ** Returns:         None
+  **
+  *******************************************************************************/
+  NativeWlcManager();
+
+  /*******************************************************************************
+  **
+  ** Function:        ~NativeWlcManager
+  **
+  ** Description:     Release all resources.
+  **
+  ** Returns:         None
+  **
+  *******************************************************************************/
+  ~NativeWlcManager();
+
+  /*******************************************************************************
+  **
+  ** Function:        wlcManagementCallback
+  **
+  ** Description:     Callback function for the stack.
+  **                  event: event ID.
+  **                  eventData: event's data.
+  **
+  ** Returns:         None
+  **
+  *******************************************************************************/
+  static void wlcManagementCallback(tNFA_WLC_EVT wlcEvent,
+                                    tNFA_WLC_EVT_DATA* eventData);
+
+  static const JNINativeMethod sMethods[];
+
+  static jboolean com_android_nfc_wlc_startWlcP(JNIEnv* e, jobject, jint mode);
+  static jboolean com_android_nfc_wlc_chargeWlcListener(JNIEnv* e, jobject,
+                                                        jint power_adj_req,
+                                                        jint wpt_time_int);
+};
diff --git a/nci/jni/NfcJniUtil.cpp b/nci/jni/NfcJniUtil.cpp
index 10e1b5e..dbba6c5 100644
--- a/nci/jni/NfcJniUtil.cpp
+++ b/nci/jni/NfcJniUtil.cpp
@@ -23,6 +23,7 @@
 #include <nativehelper/JNIHelp.h>
 #include <nativehelper/ScopedLocalRef.h>
 
+#include "NativeWlcManager.h"
 #include "RoutingManager.h"
 
 using android::base::StringPrintf;
@@ -52,7 +53,8 @@
   if (android::register_com_android_nfc_NativeNfcTag(e) == -1) return JNI_ERR;
   if (RoutingManager::getInstance().registerJniFunctions(e) == -1)
     return JNI_ERR;
-
+  if (NativeWlcManager::getInstance().registerJniFunctions(e) == -1)
+    return JNI_ERR;
   LOG(DEBUG) << StringPrintf("%s: exit", __func__);
   return JNI_VERSION_1_6;
 }
diff --git a/nci/jni/NfcJniUtil.h b/nci/jni/NfcJniUtil.h
index 6e09c7f..b1bedfa 100644
--- a/nci/jni/NfcJniUtil.h
+++ b/nci/jni/NfcJniUtil.h
@@ -105,7 +105,6 @@
 
   /* Cached objects */
   jobject cached_NfcTag;
-  jobject cached_P2pDevice;
 
   /* Secure Element selected */
   int seId;
@@ -143,5 +142,4 @@
 struct nfc_jni_native_data* nfc_jni_get_nat(JNIEnv* e, jobject o);
 int register_com_android_nfc_NativeNfcManager(JNIEnv* e);
 int register_com_android_nfc_NativeNfcTag(JNIEnv* e);
-int register_com_android_nfc_NativeP2pDevice(JNIEnv* e);
 }  // namespace android
diff --git a/nci/jni/NfcTag.cpp b/nci/jni/NfcTag.cpp
index 77b915f..6fa0281 100755
--- a/nci/jni/NfcTag.cpp
+++ b/nci/jni/NfcTag.cpp
@@ -49,6 +49,7 @@
 *******************************************************************************/
 NfcTag::NfcTag()
     : mNumTechList(0),
+      mNumRfDiscId(0),
       mTechnologyTimeoutsTable(MAX_NUM_TECHNOLOGY),
       mNativeData(NULL),
       mIsActivated(false),
@@ -103,6 +104,7 @@
   mIsActivated = false;
   mActivationState = Idle;
   mProtocol = NFC_PROTOCOL_UNKNOWN;
+  mNumRfDiscId = 0;
   mtT1tMaxMessageSize = 0;
   mReadCompletedStatus = NFA_STATUS_OK;
   resetTechnologies();
@@ -457,7 +459,7 @@
     }
   }
   LOG(DEBUG) << StringPrintf("%s; mNumDiscTechList=%x", fn, mNumDiscTechList);
-
+  mNumRfDiscId = discovery_ntf.rf_disc_id;
 TheEnd:
   LOG(DEBUG) << StringPrintf("%s: exit", fn);
 }
diff --git a/nci/jni/NfcTag.h b/nci/jni/NfcTag.h
index 698db4c..310df2b 100644
--- a/nci/jni/NfcTag.h
+++ b/nci/jni/NfcTag.h
@@ -46,6 +46,7 @@
                                              // service received from
                                              // RF_INTF_ACTIVATED NTF
   int mNumTechList;  // current number of NFC technologies in the list
+  int mNumRfDiscId;
 
   /*******************************************************************************
   **
diff --git a/nci/src/com/android/nfc/dhimpl/NativeNfcManager.java b/nci/src/com/android/nfc/dhimpl/NativeNfcManager.java
index 67099d4..2182640 100755
--- a/nci/src/com/android/nfc/dhimpl/NativeNfcManager.java
+++ b/nci/src/com/android/nfc/dhimpl/NativeNfcManager.java
@@ -16,21 +16,28 @@
 
 package com.android.nfc.dhimpl;
 
+import static com.android.nfc.NfcStatsLog.NFC_PROPRIETARY_CAPABILITIES_REPORTED__PASSIVE_OBSERVE_MODE__MODE_UNKNOWN;
+import static com.android.nfc.NfcStatsLog.NFC_PROPRIETARY_CAPABILITIES_REPORTED__PASSIVE_OBSERVE_MODE__SUPPORT_WITHOUT_RF_DEACTIVATION;
+import static com.android.nfc.NfcStatsLog.NFC_PROPRIETARY_CAPABILITIES_REPORTED__PASSIVE_OBSERVE_MODE__SUPPORT_WITH_RF_DEACTIVATION;
+
 import android.content.Context;
-import android.nfc.cardemulation.HostApduService;
+import android.nfc.cardemulation.PollingFrame;
 import android.nfc.tech.Ndef;
 import android.nfc.tech.TagTechnology;
 import android.os.Bundle;
+import android.os.Trace;
 import android.util.Log;
 
 import com.android.nfc.DeviceHost;
 import com.android.nfc.NfcDiscoveryParameters;
 import com.android.nfc.NfcService;
+import com.android.nfc.NfcStatsLog;
 import com.android.nfc.NfcVendorNciResponse;
-
+import com.android.nfc.NfcProprietaryCaps;
 import java.io.FileDescriptor;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Iterator;
@@ -42,10 +49,6 @@
 
     static final String DRIVER_NAME = "android-nci";
 
-    static {
-        System.loadLibrary("nfc_nci_jni");
-    }
-
     /* Native structure */
     private long mNative;
 
@@ -55,7 +58,7 @@
 
     private final Object mLock = new Object();
     private final HashMap<Integer, byte[]> mT3tIdentifiers = new HashMap<Integer, byte[]>();
-
+    private NfcProprietaryCaps mProprietaryCaps = null;
     private static final int MIN_POLLING_FRAME_TLV_SIZE = 5;
     private static final int TAG_FIELD_CHANGE = 0;
     private static final int TAG_NFC_A = 1;
@@ -67,8 +70,13 @@
     private static final int NCI_OID_INDEX = 1;
     private static final int OP_CODE_INDEX = 3;
 
+    private void loadLibrary() {
+        System.loadLibrary("nfc_nci_jni");
+    }
+
     public NativeNfcManager(Context context, DeviceHostListener listener) {
         mListener = listener;
+        loadLibrary();
         initializeNativeStructure();
         mContext = context;
     }
@@ -89,6 +97,11 @@
     @Override
     public boolean initialize() {
         boolean ret = doInitialize();
+        if (mContext.getResources().getBoolean(
+                com.android.nfc.R.bool.nfc_proprietary_getcaps_supported)) {
+            mProprietaryCaps = NfcProprietaryCaps.createFromByteArray(getProprietaryCaps());
+            logProprietaryCaps(mProprietaryCaps);
+        }
         mIsoDepMaxTransceiveLength = getIsoDepMaxTransceiveLength();
         return ret;
     }
@@ -162,13 +175,16 @@
         }
 
         return mContext.getResources().getBoolean(
-            com.android.nfc.R.bool.nfc_observe_mode_supported);
+                com.android.nfc.R.bool.nfc_observe_mode_supported);
     }
 
     @Override
     public native boolean setObserveMode(boolean enabled);
 
     @Override
+    public native boolean isObserveModeEnabled();
+
+    @Override
     public void registerT3tIdentifier(byte[] t3tIdentifier) {
         synchronized (mLock) {
             int handle = doRegisterT3tIdentifier(t3tIdentifier);
@@ -207,7 +223,7 @@
     public native int getLfT3tMax();
 
     @Override
-    public native void doSetScreenState(int screen_state_mask);
+    public native void doSetScreenState(int screen_state_mask, boolean alwaysPoll);
 
     @Override
     public native int getNciVersion();
@@ -300,22 +316,6 @@
         doDump(fd);
     }
 
-    private native void doEnableScreenOffSuspend();
-
-    @Override
-    public boolean enableScreenOffSuspend() {
-        doEnableScreenOffSuspend();
-        return true;
-    }
-
-    private native void doDisableScreenOffSuspend();
-
-    @Override
-    public boolean disableScreenOffSuspend() {
-        doDisableScreenOffSuspend();
-        return true;
-    }
-
     private native boolean doSetNfcSecure(boolean enable);
 
     @Override
@@ -343,6 +343,7 @@
     @Override
     public native int getMaxRoutingTableSize();
 
+    public native boolean isMultiTag();
 
     private native NfcVendorNciResponse nativeSendRawVendorCmd(
             int mt, int gid, int oid, byte[] payload);
@@ -390,71 +391,84 @@
         mListener.onHwErrorReported();
     }
 
-    private void notifyPollingLoopFrame(int data_len, byte[] p_data) {
+    public void notifyPollingLoopFrame(int data_len, byte[] p_data) {
         if (data_len < MIN_POLLING_FRAME_TLV_SIZE) {
             return;
         }
-        Bundle frame = new Bundle();
-        final int header_len = 2;
+        Trace.beginSection("notifyPollingLoopFrame");
+        final int header_len = 4;
         int pos = header_len;
-        final int TLV_len_offset = 0;
-        final int TLV_type_offset = 2;
+        final int TLV_header_len = 3;
+        final int TLV_type_offset = 0;
+        final int TLV_len_offset = 2;
         final int TLV_timestamp_offset = 3;
         final int TLV_gain_offset = 7;
         final int TLV_data_offset = 8;
+        ArrayList<PollingFrame> frames = new ArrayList<PollingFrame>();
         while (pos + TLV_len_offset < data_len) {
-        int type = p_data[pos + TLV_type_offset];
-        int length = p_data[pos + TLV_len_offset];
-        if (pos + length + 1 > data_len) {
-            // Frame is bigger than buffer.
-            Log.e(TAG, "Polling frame data is longer than buffer data length.");
-            break;
-        }
-        switch (type) {
-            case TAG_FIELD_CHANGE:
-                frame.putChar(
-                    HostApduService.POLLING_LOOP_TYPE_KEY,
-                    p_data[pos + TLV_data_offset] != 0x00
-                        ? HostApduService.POLLING_LOOP_TYPE_ON
-                        : HostApduService.POLLING_LOOP_TYPE_OFF);
+            @PollingFrame.PollingFrameType int frameType;
+            Bundle frame = new Bundle();
+            int type = p_data[pos + TLV_type_offset];
+            int length = p_data[pos + TLV_len_offset];
+            if (TLV_len_offset + length < TLV_gain_offset ) {
+                Log.e(TAG, "Length (" + length + ") is less than a polling frame, dropping.");
                 break;
-            case TAG_NFC_A:
-                frame.putChar(HostApduService.POLLING_LOOP_TYPE_KEY,
-                    HostApduService.POLLING_LOOP_TYPE_A);
+            }
+            if (pos + TLV_header_len + length > data_len) {
+                // Frame is bigger than buffer.
+                Log.e(TAG, "Polling frame data ("+ pos + ", " + length
+                        + ") is longer than buffer data length (" + data_len + ").");
                 break;
-            case TAG_NFC_B:
-                frame.putChar(HostApduService.POLLING_LOOP_TYPE_KEY,
-                    HostApduService.POLLING_LOOP_TYPE_B);
-                break;
-            case TAG_NFC_F:
-                frame.putChar(HostApduService.POLLING_LOOP_TYPE_KEY,
-                    HostApduService.POLLING_LOOP_TYPE_F);
-                break;
-            case TAG_NFC_UNKNOWN:
-                frame.putChar(
-                    HostApduService.POLLING_LOOP_TYPE_KEY,
-                    HostApduService.POLLING_LOOP_TYPE_UNKNOWN);
-                frame.putByteArray(
-                    HostApduService.POLLING_LOOP_DATA_KEY,
-                    Arrays.copyOfRange(
-                        p_data, pos + TLV_data_offset, pos + TLV_timestamp_offset + length));
-                break;
-            default:
-                Log.e(TAG, "Unknown polling loop tag type.");
+            }
+            switch (type) {
+                case TAG_FIELD_CHANGE:
+                    frameType = p_data[pos + TLV_data_offset] != 0x00
+                                    ? PollingFrame.POLLING_LOOP_TYPE_ON
+                                    : PollingFrame.POLLING_LOOP_TYPE_OFF;
+                    break;
+                case TAG_NFC_A:
+                    frameType = PollingFrame.POLLING_LOOP_TYPE_A;
+                    break;
+                case TAG_NFC_B:
+                    frameType = PollingFrame.POLLING_LOOP_TYPE_B;
+                    break;
+                case TAG_NFC_F:
+                    frameType = PollingFrame.POLLING_LOOP_TYPE_F;
+                    break;
+                case TAG_NFC_UNKNOWN:
+                    frameType = PollingFrame.POLLING_LOOP_TYPE_UNKNOWN;
+                    break;
+                default:
+                    Log.e(TAG, "Unknown polling loop tag type.");
+                    return;
+            }
+            byte[] frameData = null;
+            if (pos + TLV_header_len + length <= data_len) {
+                frameData = Arrays.copyOfRange(p_data, pos + TLV_data_offset,
+                    pos + TLV_header_len + length);
+            }
+            int gain = -1;
+            if (pos + TLV_gain_offset <= data_len) {
+                gain = Byte.toUnsignedInt(p_data[pos + TLV_gain_offset]);
+                if (gain == 0XFF) {
+                    gain = -1;
+                }
+            }
+            long timestamp = 0;
+            if (pos + TLV_timestamp_offset + 3 < data_len) {
+                timestamp = Integer.toUnsignedLong(ByteBuffer.wrap(p_data,
+                        pos + TLV_timestamp_offset, 4).order(ByteOrder.BIG_ENDIAN).getInt());
+            }
+            pos += (TLV_header_len + length);
+            frames.add(new PollingFrame(frameType, frameData, gain, timestamp, false));
         }
-        if (pos + TLV_gain_offset <= data_len) {
-            byte gain = p_data[pos + TLV_gain_offset];
-            frame.putByte(HostApduService.POLLING_LOOP_GAIN_KEY, gain);
-        }
-        if (pos + TLV_timestamp_offset + 3 < data_len) {
-            int timestamp = ByteBuffer.wrap(p_data, pos + TLV_timestamp_offset, 4).order(ByteOrder.LITTLE_ENDIAN).getInt();
-            frame.putInt(HostApduService.POLLING_LOOP_TIMESTAMP_KEY, timestamp);
-        }
-        pos += (length + 2);
-        }
-        mListener.onPollingLoopDetected(frame);
+        mListener.onPollingLoopDetected(frames);
+        Trace.endSection();
     }
 
+    private void notifyWlcStopped(int wpt_end_condition) {
+        mListener.onWlcStopped(wpt_end_condition);
+    }
     private void notifyVendorSpecificEvent(int event, int dataLen, byte[] pData) {
         if (pData.length < NCI_HEADER_MIN_LEN || dataLen != pData.length) {
             Log.e(TAG, "Invalid data");
@@ -481,7 +495,42 @@
     @Override
     public native void setTechnologyABRoute(int route);
 
+    private native byte[] getProprietaryCaps();
+
+    @Override
+    public native void enableVendorNciNotifications(boolean enabled);
+
     private void notifyCommandTimeout() {
         NfcService.getInstance().storeNativeCrashLogs();
     }
+
+    /** wrappers for values */
+    private final int CAPS_OBSERVE_MODE_UNKNOWN =
+            NFC_PROPRIETARY_CAPABILITIES_REPORTED__PASSIVE_OBSERVE_MODE__MODE_UNKNOWN;
+    private final int CAPS_OBSERVE_MODE_SUPPORT_WITH_RF_DEACTIVATION =
+          NFC_PROPRIETARY_CAPABILITIES_REPORTED__PASSIVE_OBSERVE_MODE__SUPPORT_WITH_RF_DEACTIVATION;
+    private final int CAPS_OBSERVE_MODE_SUPPORT_WITHOUT_RF_DEACTIVATION =
+       NFC_PROPRIETARY_CAPABILITIES_REPORTED__PASSIVE_OBSERVE_MODE__SUPPORT_WITHOUT_RF_DEACTIVATION;
+    private final int CAPS_OBSERVE_MODE_NOT_SUPPORTED =
+            NfcStatsLog.NFC_PROPRIETARY_CAPABILITIES_REPORTED__PASSIVE_OBSERVE_MODE__NOT_SUPPORTED;
+
+    private void logProprietaryCaps(NfcProprietaryCaps proprietaryCaps) {
+        int observeModeStatsd = CAPS_OBSERVE_MODE_UNKNOWN;
+
+        NfcProprietaryCaps.PassiveObserveMode mode = proprietaryCaps.getPassiveObserveMode();
+
+        if (mode == NfcProprietaryCaps.PassiveObserveMode.SUPPORT_WITH_RF_DEACTIVATION) {
+            observeModeStatsd = CAPS_OBSERVE_MODE_SUPPORT_WITH_RF_DEACTIVATION;
+        } else if (mode == NfcProprietaryCaps.PassiveObserveMode.SUPPORT_WITHOUT_RF_DEACTIVATION) {
+            observeModeStatsd = CAPS_OBSERVE_MODE_SUPPORT_WITHOUT_RF_DEACTIVATION;
+        } else if (mode == NfcProprietaryCaps.PassiveObserveMode.NOT_SUPPORTED) {
+            observeModeStatsd = CAPS_OBSERVE_MODE_NOT_SUPPORTED;
+        }
+
+        NfcStatsLog.write(NfcStatsLog.NFC_PROPRIETARY_CAPABILITIES_REPORTED,
+                observeModeStatsd,
+                proprietaryCaps.isPollingFrameNotificationSupported(),
+                proprietaryCaps.isPowerSavingModeSupported(),
+                proprietaryCaps.isAutotransactPollingLoopFilterSupported());
+    }
 }
diff --git a/nci/src/com/android/nfc/dhimpl/NativeNfcTag.java b/nci/src/com/android/nfc/dhimpl/NativeNfcTag.java
index e50acbc..30a826d 100755
--- a/nci/src/com/android/nfc/dhimpl/NativeNfcTag.java
+++ b/nci/src/com/android/nfc/dhimpl/NativeNfcTag.java
@@ -922,4 +922,32 @@
             }
         }
     }
+
+    @Override
+    public NdefMessage getNdef() {
+        Log.d(TAG, "getNdef: Searching for NfcCharging information");
+        int[] ndefinfo = new int[2];
+        int status;
+        NdefMessage ndefMsg = null;
+        status = checkNdefWithStatus(ndefinfo);
+        if (status != 0) {
+            Log.d(TAG, "Check NDEF Failed - status = " + status);
+            return ndefMsg;
+        }
+
+        byte[] buff = readNdef();
+        if (buff != null && buff.length > 0) {
+            try {
+                ndefMsg = new NdefMessage(buff);
+            } catch (FormatException e) {
+                // Create an intent anyway, without NDEF messages
+                ndefMsg = null;
+            }
+        } else if (buff != null) {
+            // Empty buffer, unformatted tags fall into this case
+            ndefMsg = null;
+        }
+
+        return ndefMsg;
+    }
 }
diff --git a/nci/src/com/android/nfc/dhimpl/NativeP2pDevice.java b/nci/src/com/android/nfc/dhimpl/NativeP2pDevice.java
deleted file mode 100755
index 1b1359b..0000000
--- a/nci/src/com/android/nfc/dhimpl/NativeP2pDevice.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.nfc.dhimpl;
-
-import com.android.nfc.DeviceHost.NfcDepEndpoint;
-
-/**
- * Native interface to the P2P Initiator functions
- */
-public class NativeP2pDevice implements NfcDepEndpoint {
-
-    private int mHandle;
-
-    private int mMode;
-
-    private byte[] mGeneralBytes;
-
-    private native byte[] doReceive();
-    @Override
-    public byte[] receive() {
-        return doReceive();
-    }
-
-    private native boolean doSend(byte[] data);
-    @Override
-    public boolean send(byte[] data) {
-        return doSend(data);
-    }
-
-    private native boolean doConnect();
-    @Override
-    public boolean connect() {
-        return doConnect();
-    }
-
-    private native boolean doDisconnect();
-    @Override
-    public boolean disconnect() {
-        return doDisconnect();
-    }
-
-    public native byte[] doTransceive(byte[] data);
-    @Override
-    public byte[] transceive(byte[] data) {
-        return doTransceive(data);
-    }
-
-    @Override
-    public int getHandle() {
-        return mHandle;
-    }
-
-    @Override
-    public int getMode() {
-        return mMode;
-    }
-
-    @Override
-    public byte[] getGeneralBytes() {
-        return mGeneralBytes;
-    }
-}
diff --git a/proguard.flags b/proguard.flags
index 226fd0a..652fb18 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -1,3 +1,6 @@
 # Keep all impl classes referenced via JNI.
 -keep class com.android.nfc.dhimpl**.Native* { *; }
+# Keep NfcCharging class since it's usage is behind a trunk stable flag (which is disabled in `-next` config).
+-keep class com.android.nfc.wlc.NfcCharging { *; }
 -keep class com.android.nfc.NfcVendorNciResponse { *; }
+-keep class com.android.nfc.proto.** { *; }
diff --git a/proto/Android.bp b/proto/Android.bp
new file mode 100644
index 0000000..6bb73db
--- /dev/null
+++ b/proto/Android.bp
@@ -0,0 +1,18 @@
+package {
+    default_team: "trendy_team_fwk_nfc",
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_library {
+    name: "nfc-event-log-proto",
+    proto: {
+        type: "lite",
+    },
+    sdk_version: "system_current",
+    min_sdk_version: "35",
+    srcs: ["event.proto"],
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.nfcservices",
+    ],
+}
diff --git a/proto/event.proto b/proto/event.proto
new file mode 100644
index 0000000..3cd0ab9
--- /dev/null
+++ b/proto/event.proto
@@ -0,0 +1,65 @@
+//
+// Copyright (C) 2024 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.
+//
+
+syntax = "proto2";
+
+package com_android_nfc;
+
+option java_package = "com.android.nfc.proto";
+option java_outer_classname = "NfcEventProto";
+
+// Proto used to format NFC event logs.
+message EventList {
+  repeated Event events = 1;
+}
+
+message Event {
+  required string timestamp = 1;
+  required EventType event_type = 2;
+}
+
+// Union of events.
+message EventType {
+  oneof _EventType {
+    NfcBootupState bootup_state = 1;
+    NfcStateChange state_change = 2;
+    NfcReaderModeChange reader_mode_change = 3;
+    NfcCeUnroutableAid ce_unroutable_aid = 4;
+  }
+}
+
+message NfcBootupState {
+  required bool enabled = 1;
+}
+
+message NfcAppInfo {
+  optional string package_name = 1;
+  optional int32 uid = 2;
+}
+
+message NfcStateChange {
+  required NfcAppInfo app_info = 1;
+  required bool enabled = 2;
+}
+
+message NfcReaderModeChange {
+  optional NfcAppInfo app_info = 1;
+  required int32 flags = 2;
+}
+
+message NfcCeUnroutableAid {
+  required string aid = 1;
+}
diff --git a/res/layout/url_open_confirmation_tablet.xml b/res/layout/url_open_confirmation_tablet.xml
new file mode 100644
index 0000000..b3994ec
--- /dev/null
+++ b/res/layout/url_open_confirmation_tablet.xml
@@ -0,0 +1,35 @@
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:gravity="start"
+    android:layout_width="wrap_content"
+    android:layout_height="400dp"
+    android:orientation="vertical"
+    android:paddingTop="24dp"
+    android:paddingStart="24dp"
+    android:paddingEnd="24dp">
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAlignment="viewStart"
+        android:paddingBottom="24dp"
+        android:text="@string/summary_confirm_url_open_tablet"
+        android:textColor="?android:attr/textColorPrimaryInverse"/>
+    <TextView
+        android:id="@+id/url_open_confirmation_link"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAlignment="viewStart"
+        android:textColor="?android:attr/colorAccent"/>
+    <FrameLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingTop="24dp"
+        android:paddingBottom="24dp">
+        <ImageView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:src="@drawable/ic_weblink_nfc"/>
+    </FrameLayout>
+
+</LinearLayout>
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index c149b7b..b4b4d03 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Straal"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"Die program het nie toestemming vir eksterne berging nie. Dit word vereis om hierdie lêer te straal"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"Maak skakel oop?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"Jou tablet het \'n skakel deur NFC ontvang:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"Jou foon het \'n skakel deur NFC ontvang:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"Jou foon het \'n skakel deur NFC ontvang:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"Jou tablet het ’n skakel deur NFC ontvang:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Maak skakel oop"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"NFC-leesfout. Probeer weer."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"Geen gesteunde program vir hierdie NFC-merker nie"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Tik om uit te vind hoe om dit reg te stel."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"NFC-data word tans opgeneem"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Tik om opname te stop."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"Laat <xliff:g id="PKG">%1$s</xliff:g> toe om NFC te aktiveer?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Ja"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"Nee"</string>
 </resources>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index 18b427b..171fbaf 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"መተግበሪያ የውጫዊ ማከማቻ ፈቃድ የለውም። ይሄ ይህን ፋይል በሞገድ ለመውሰድ ያስፈልጋል"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"አገናኝ ይከፈት?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"የእርስዎ ጡባዊ በNFC በኩል አገናኝ ተቀብሏል፦"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"የእርስዎ ስልክ በNFC በኩል አገናኝ ተቀብሏል፦"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"የእርስዎ ስልክ በNFC በኩል አገናኝ ተቀብሏል፦"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"የእርስዎ ጡባዊ በኤንኤፍሲ በኩል አገናኝ ተቀብሏል፦"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"አገናኝ ክፈት"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"የNFC ማንበብ ስህተት። እንደገና ይሞክሩ።"</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"ለዚህ የNFC መለያ የሚደገፍ መተግበሪያ የለም"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"እንዴት ማስተካከል እንደሚቻል ለማወቅ መታ ያድርጉ።"</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"የኤንኤፍሲ ውሂብ እየተቀዳ ነው"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"መቅዳት ለማቆም መታ ያድርጉ።"</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"<xliff:g id="PKG">%1$s</xliff:g> ኤንኤፍሲን እንዲያነቃ ይፈቀድ?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"አዎ"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"አይ"</string>
 </resources>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 3af3fcd..f6cc465 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"‏شعاع Android"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"لا يحتوي التطبيق على إذن مساحة تخزين خارجية، إلا أنه مطلوب لإرسال هذا الملف"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"هل تريد فتح الرابط؟"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"‏تلقى جهازك اللوحي رابطًا عبر ميزة NFC:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"‏تلقى هاتفك رابطًا عبر ميزة NFC:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"‏تلقى هاتفك رابطًا عبر ميزة NFC:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"‏تلقّى جهازك اللوحي رابطًا من خلال تقنية NFC:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"فتح الرابط"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"‏حدث خطأ NFC. يُرجى إعادة المحاولة."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"‏ليس هناك تطبيق متوافق مع علامة NFC هذه."</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"انقر لمعرفة كيف يمكنك حلّ هذه المشكلة."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"‏يتم حاليًا تسجيل بيانات الاتصال القصير المدى (NFC)"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"انقر لإيقاف التسجيل."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"‏هل تريد السماح لحِزمة \"<xliff:g id="PKG">%1$s</xliff:g>\" بتفعيل NFC؟"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"نعم"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"لا"</string>
 </resources>
diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml
index ef6c32f..2721aa3 100644
--- a/res/values-as/strings.xml
+++ b/res/values-as/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android বীম"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"এপ্লিকেশ্বনটোৰ বাহ্যিক ষ্ট’ৰেজ ব্যৱহাৰ কৰাৰ অনুমতি নাই৷ এই ফাইলটো বীম কৰিবলৈ এয়া প্ৰয়োজন।"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"লিংক খুলিবনে?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"আপোনাৰ টেবলেটে NFC যোগে এই লিংক পাইছে:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"আপোনাৰ ফ\'নে NFC যোগে এই লিংক পাইছে:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"আপোনাৰ ফ\'নে NFC যোগে এই লিংক পাইছে:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"আপোনাৰ টেবলেটে NFCৰ জৰিয়তে এই লিংক পাইছে:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"লিংক খোলক"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"NFC পঢ়াত আসোঁৱাহ। আকৌ চেষ্টা কৰক।"</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"এই NFC টেগৰ বাবে কোনো সমৰ্থিত এপ্লিকেশ্বন নাই"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"কেনেকৈ সমাধান কৰিব লাগে সেয়া জানিবলৈ টিপক।"</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"NFC ডেটা ৰেকৰ্ড কৰি থকা হৈছে"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"ৰেকৰ্ড কৰা বন্ধ কৰিবলৈ টিপক।"</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"<xliff:g id="PKG">%1$s</xliff:g>ক NFC সক্ষম কৰিবলৈ দিবনে?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"হয়"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"নহয়"</string>
 </resources>
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
index 162e6c8..890f651 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"Tətbiqdə Xarici Yaddaş İcazəsi yoxdur. Faylı Köçürmək tələb olunur"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"Link açılsın?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"Planşet NFC ilə link əldə etdi:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"Telefon NFC ilə link əldə etdi:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"Telefon NFC ilə link əldə etdi:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"Planşetə NFC ilə keçid göndərilib:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Linki açın"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"NFC oxuma xətası. Yenidən cəhd edin."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"NFC Teq üçün dəstəklənən tətbiq yoxdur"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Necə düzəldəcəyinizi öyrənmək üçün toxunun."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"NFC datası qeydə alınır"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Qeydə almanı dayandırmaq üçün toxunun."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"<xliff:g id="PKG">%1$s</xliff:g> üçün NFC-ni aktivləşdirmək icazəsi verilsin?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Bəli"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"Xeyr"</string>
 </resources>
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index b5b25cf..830ba36 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android prebacivanje"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"Aplikacija nema dozvolu za spoljnu memoriju. To je potrebno za prebacivanje ove datoteke"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"Želite li da otvorite link?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"Tablet je primio link preko NFC-a:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"Telefon je primio link preko NFC-a:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"Telefon je primio link preko NFC-a:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"Tablet je primio link preko NFC-a:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Otvori link"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"Došlo je do greške u čitanju NFC oznake. Probajte ponovo."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"Nijedna aplikacija ne podržava ovu NFC oznaku"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Dodirnite da biste saznali kako da rešite problem."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"NFC podaci se snimaju"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Dodirnite da biste zaustavili snimanje."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"Želite da dozvolite da <xliff:g id="PKG">%1$s</xliff:g> omogući NFC?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Da"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"Ne"</string>
 </resources>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index df7ff99..c222dbf 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"У праграмы няма дазволу на доступ да знешняга сховішча. Гэты дазвол патрабуецца, каб перадаць дадзены файл"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"Адкрыць спасылку?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"На планшэт праз NFC прыйшла спасылка:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"На тэлефон праз NFC прыйшла спасылка:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"На тэлефон праз NFC прыйшла спасылка:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"На планшэт па NFC прыйшла спасылка:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Адкрыць спасылку"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"Памылка чытання NFC. Паўтарыце спробу."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"Няма праграм, якія падтрымліваюцца для гэтага цэтліка NFC"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Націсніце, каб даведацца, як вырашыць праблему."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"Даныя NFC запісваюцца"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Націсніце, каб спыніць запіс."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"Даць пакету \"<xliff:g id="PKG">%1$s</xliff:g>\" дазвол уключыць NFC?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Так"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"Не"</string>
 </resources>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index aa4cb42..a05e65c 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Лъч"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"Приложението няма разрешение за външно хранилище. То се изисква за излъчването на този файл"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"Да се отвори ли връзката?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"Таблетът ви получи връзка през NFC:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"Телефонът ви получи връзка през NFC:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"Телефонът ви получи връзка през NFC:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"Таблетът ви получи връзка през NFC:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Отваряне на връзката"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"Грешка при четене за NFC. Опитайте отново."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"Няма приложение, което поддържа този маркер за NFC"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Докоснете, за да научите как да отстраните проблема."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"Записват се данни посредством NFC"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Докоснете за спиране на записването."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"Да се разреши ли на <xliff:g id="PKG">%1$s</xliff:g> да активира NFC?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Да"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"Не"</string>
 </resources>
diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml
index d7a0ba7..2715531 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android বীম"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"ডিভাইসের বাইরের মেমরি ব্যবহার করার অনুমতি এই অ্যাপ্লিকেশানটিকে দেওয়া হয়নি। এই ফাইলটি বিম করার জন্য অনুমতিটি প্রয়োজন"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"লিঙ্কটি খুলবেন?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"আপনার ট্যাবলেট NFC এর মাধ্যমে একটি লিঙ্ক পেয়েছে:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"আপনার ফোন NFC এর মাধ্যমে একটি লিঙ্ক পেয়েছে:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"আপনার ফোন NFC এর মাধ্যমে একটি লিঙ্ক পেয়েছে:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"আপনার ট্যাবলেট NFC-এর মাধ্যমে একটি লিঙ্ক পেয়েছে:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"লিঙ্ক খুলুন"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"NFC পড়তে সমস্যা হয়েছে। আবার চেষ্টা করুন।"</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"এই NFC ট্যাগ ব্যবহার করার মতো কোনও অ্যাপ্লিকেশন নেই"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"কীভাবে সমস্যার সমাধান করবেন তা জানতে ট্যাপ করুন।"</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"NFC ডেটা রেকর্ড করা হচ্ছে"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"রেকর্ডিং বন্ধ করতে ট্যাপ করুন।"</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"NFC চালু করার জন্য <xliff:g id="PKG">%1$s</xliff:g>-কে অনুমতি দেবেন?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"হ্যাঁ"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"না"</string>
 </resources>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index 42e1418..6b2c690 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"Aplikacija nema odobrenje za vanjsku pohranu. Ovo je potrebno kako biste ovaj fajl mogli prenijeti koristeći funkciju Beam."</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"Otvoriti link?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"Vaš tablet je primio link preko NFC-a:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"Vaš telefon je primio link preko NFC-a:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"Vaš telefon je primio link preko NFC-a:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"Tablet je primio link putem NFC-a:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Otvori link"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"Greška prilikom čitanja NFC-a. Pokušajte ponovo."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"Nema podržane aplikacije za ovu NFC oznaku"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Dodirnite da saznate kako ispraviti problem."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"NFC podaci se trenutno bilježe"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Dodirnite da zaustavite bilježenje."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"Dozvoliti da <xliff:g id="PKG">%1$s</xliff:g> omogući NFC?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Da"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"Ne"</string>
 </resources>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 2992f52..a91703f 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"L\'aplicació no té permís d\'emmagatzematge extern. Aquest permís és necessari per compartir el fitxer."</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"Vols obrir l\'enllaç?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"Has rebut un enllaç a la tauleta per NFC:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"Has rebut un enllaç al telèfon per NFC:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"Has rebut un enllaç al telèfon per NFC:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"Has rebut un enllaç a la tauleta per NFC:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Obre l\'enllaç"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"Hi ha hagut un error en llegir l\'etiqueta NFC. Torna-ho a provar."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"No hi ha cap aplicació compatible amb aquesta etiqueta NFC"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Toca per obtenir informació sobre com pots resoldre-ho."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"S\'estan gravant les dades de l\'NFC"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Toca per aturar la gravació."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"Vols permetre que <xliff:g id="PKG">%1$s</xliff:g> activi la funció NFC?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Sí"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"No"</string>
 </resources>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 4ff0739..c525c33 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Teleport Android"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"Aplikace nemá přístup k externímu úložišti. Přístup je vyžadován k odeslání souboru pomocí funkce Beam."</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"Otevřít odkaz?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"Tablet obdržel odkaz přes NFC:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"Telefon obdržel odkaz přes NFC:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"Telefon obdržel odkaz přes NFC:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"Tablet obdržel odkaz přes NFC:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Otevřít odkaz"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"Při čtení NFC došlo k chybě. Zkuste to znovu."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"Pro tuto značku NFC není k dispozici žádná podporovaná aplikace"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Klepnutím zjistíte, jak problém vyřešit."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"Data funkce NFC jsou zaznamenávána"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Klepnutím ukončíte nahrávání."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"Povolit aplikaci <xliff:g id="PKG">%1$s</xliff:g> zapnout NFC?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Ano"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"Ne"</string>
 </resources>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index dfec0eb..15907cb 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"Appen har ikke adgang til det eksterne lager. Adgangstilladelse er påkrævet for at overføre denne fil."</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"Vil du åbne linket?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"Du har modtaget et link på din tablet via NFC:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"Du har modtaget et link på din telefon via NFC:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"Du har modtaget et link på din telefon via NFC:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"Du har modtaget et link på din tablet via NFC:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Åbn link"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"NFC-læsefejl. Prøv igen."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"Der er ingen understøttede apps til dette NFC-tag"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Tryk for at se, hvordan du løser problemet."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"NFC-data registreres"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Tryk for at stoppe optagelsen."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"Vil du give <xliff:g id="PKG">%1$s</xliff:g> tilladelse til at aktivere NFC?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Ja"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"Nej"</string>
 </resources>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index da1efb7..d8a95d1 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"Die App ist nicht berechtigt, auf externe Speicher zuzugreifen. Dies ist jedoch zum Beamen der Datei erforderlich."</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"Link öffnen?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"Du hast auf dem Tablet via NFC einen Link erhalten:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"Du hast auf dem Smartphone via NFC einen Link erhalten:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"Du hast auf dem Smartphone via NFC einen Link erhalten:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"Du hast auf dem Tablet via NFC einen Link erhalten:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Link öffnen"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"NFC-Lesefehler. Wiederholen."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"Keine unterstützte Anwendung für dieses NFC-Tag"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Tippen, um das Problem zu beheben"</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"NFC-Daten werden aufgezeichnet"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Tippen, um die Aufzeichnung zu beenden"</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"Zulassen, dass <xliff:g id="PKG">%1$s</xliff:g> NFC aktiviert?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Ja"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"Nein"</string>
 </resources>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index bd5c54b..20b85dd 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"Η εφαρμογή δεν διαθέτει Άδεια εξωτερικού αποθηκευτικού χώρου. Αυτή η άδεια απαιτείται για την ζεύξη αυτού του αρχείου"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"Να ανοίξει ο σύνδεσμος;"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"Το tablet έλαβε έναν σύνδεσμο μέσω NFC:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"Το τηλέφωνό σας έλαβε έναν σύνδεσμο μέσω NFC:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"Το τηλέφωνό σας έλαβε έναν σύνδεσμο μέσω NFC:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"Το tablet έλαβε έναν σύνδεσμο μέσω NFC:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Άνοιγμα συνδέσμου"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"Σφάλμα ανάγνωσης NFC. Δοκιμάστε ξανά."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"Δεν υπάρχει υποστηριζόμενη εφαρμογή για αυτή την ετικέτα NFC"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Πατήστε για να μάθετε πώς μπορείτε να διορθώσετε το πρόβλημα."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"Τα δεδομένα NFC εγγράφονται"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Πατήστε για διακοπή της εγγραφής."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"Να επιτρέπεται στο <xliff:g id="PKG">%1$s</xliff:g> να ενεργοποιεί το NFC;"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Ναι"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"Όχι"</string>
 </resources>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index 16ef46e..dac0cbd 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"Application does not have External Storage Permission. This is required to Beam this file"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"Open link?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"Your tablet received a link through NFC:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"Your phone received a link through NFC:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"Your phone received a link through NFC:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"Your tablet received a link through NFC:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Open link"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"NFC read error. Try again."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"No supported application for this NFC Tag"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Tap to learn how to fix."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"NFC data is being recorded"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Tap to stop recording."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"Allow <xliff:g id="PKG">%1$s</xliff:g> to enable NFC?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Yes"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"No"</string>
 </resources>
diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml
index 16ef46e..f34dc73 100644
--- a/res/values-en-rCA/strings.xml
+++ b/res/values-en-rCA/strings.xml
@@ -46,8 +46,9 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"Application does not have External Storage Permission. This is required to Beam this file"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"Open link?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"Your tablet received a link through NFC:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"Your phone received a link through NFC:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"Your phone received a link through NFC:"</string>
+    <!-- no translation found for summary_confirm_url_open_tablet (771152442325809851) -->
+    <skip />
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Open link"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"NFC read error. Try again."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"No supported application for this NFC Tag"</string>
@@ -55,4 +56,10 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Tap to learn how to fix."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"NFC data is being recorded"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Tap to stop recording."</string>
+    <!-- no translation found for title_package_enabling_nfc (5736481508428918024) -->
+    <skip />
+    <!-- no translation found for enable_nfc_yes (694867197186062792) -->
+    <skip />
+    <!-- no translation found for enable_nfc_no (6549033065900624599) -->
+    <skip />
 </resources>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index 16ef46e..dac0cbd 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"Application does not have External Storage Permission. This is required to Beam this file"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"Open link?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"Your tablet received a link through NFC:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"Your phone received a link through NFC:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"Your phone received a link through NFC:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"Your tablet received a link through NFC:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Open link"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"NFC read error. Try again."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"No supported application for this NFC Tag"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Tap to learn how to fix."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"NFC data is being recorded"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Tap to stop recording."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"Allow <xliff:g id="PKG">%1$s</xliff:g> to enable NFC?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Yes"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"No"</string>
 </resources>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index 16ef46e..dac0cbd 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"Application does not have External Storage Permission. This is required to Beam this file"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"Open link?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"Your tablet received a link through NFC:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"Your phone received a link through NFC:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"Your phone received a link through NFC:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"Your tablet received a link through NFC:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Open link"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"NFC read error. Try again."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"No supported application for this NFC Tag"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Tap to learn how to fix."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"NFC data is being recorded"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Tap to stop recording."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"Allow <xliff:g id="PKG">%1$s</xliff:g> to enable NFC?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Yes"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"No"</string>
 </resources>
diff --git a/res/values-en-rXC/strings.xml b/res/values-en-rXC/strings.xml
index e6b80dc..e345240 100644
--- a/res/values-en-rXC/strings.xml
+++ b/res/values-en-rXC/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‏‎‎‏‎‏‏‎‏‏‏‏‎‏‏‎‏‏‏‎‎‏‎‎‎‎‎‎‏‏‎‏‎‎‎‎‎‏‎‏‏‎‏‏‎‎‎‏‏‏‎‎‏‏‎‏‏‏‎‎‏‎‎‏‎‎‎‏‎‎‏‏‎‏‏‎Android Beam‎‏‎‎‏‎"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‏‎‎‏‎‏‏‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‎‏‏‎‎‏‏‏‏‏‏‎‎‏‎‏‏‎‎‎‎‏‏‏‎‏‏‎‎‏‏‎‎‎‏‏‏‏‏‏‏‎‏‏‎‏‏‎‏‏‎‏‎‏‎Application does not have External Storage Permission. This is required to Beam this file‎‏‎‎‏‎"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‏‎‎‏‎‏‏‎‏‏‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‏‏‎‎‏‎‎‏‎‎‏‏‏‏‏‏‎‎‎‎‏‎‏‎‏‏‎‎‏‏‎‎‏‏‎‏‎‏‎‏‏‏‎‎‏‏‎‏‏‏‎‎Open link?‎‏‎‎‏‎"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‏‎‎‏‎‏‏‎‏‏‏‏‏‎‏‎‏‏‏‎‏‎‎‎‏‎‏‎‎‎‎‎‏‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‎‎‏‎‏‏‏‎‏‏‎‏‎‎‎‏‎‎‏‎‎‎‏‏‎‏‏‏‎Your tablet received a link through NFC:‎‏‎‎‏‎"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‏‎‎‏‎‏‏‎‏‏‏‏‎‏‏‎‎‎‏‎‏‎‎‏‏‎‎‎‎‎‏‏‎‎‎‏‏‎‎‎‎‎‎‏‎‎‎‏‏‏‏‏‎‎‏‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‎‏‎‏‎‎Your phone received a link through NFC:‎‏‎‎‏‎"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‏‎‎‏‎‏‏‎‏‏‏‏‎‏‏‎‎‎‏‎‏‎‎‏‏‎‎‎‎‎‏‏‎‎‎‏‏‎‎‎‎‎‎‏‎‎‎‏‏‏‏‏‎‎‏‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‎‏‎‏‎‎Your phone received a link through NFC:‎‏‎‎‏‎"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‏‎‎‏‎‏‏‎‏‏‏‏‎‎‏‎‏‎‏‎‏‏‎‎‏‏‏‎‏‎‏‏‏‏‎‎‎‎‏‏‏‏‏‎‎‎‎‎‎‎‏‎‎‎‎‎‎‏‎‎‏‏‏‎‏‎‏‎‏‏‏‎‏‏‎Your tablet received a link through NFC:‎‏‎‎‏‎"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‏‎‎‏‎‏‏‎‏‏‏‏‏‎‏‎‏‏‏‏‏‏‏‏‏‏‏‎‎‏‏‎‏‏‏‎‎‎‏‏‎‏‎‏‎‎‏‏‏‏‎‏‏‎‎‏‏‎‎‏‏‎‏‎‎‏‎‏‎‏‏‎‎‏‎‏‎Open link‎‏‎‎‏‎"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‏‎‎‏‎‏‏‎‏‏‏‏‏‎‏‎‎‎‏‎‎‏‏‏‏‏‎‏‎‏‏‏‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‎‏‏‎‏‏‎‏‏‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‎‏‏‎NFC read error. Try again.‎‏‎‎‏‎"</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‏‎‎‏‎‏‏‎‏‏‏‏‏‎‏‏‎‎‎‏‎‏‏‏‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‎‎‏‏‎‏‏‏‎‎‏‏‏‏‏‏‏‎‎‏‎‎‏‎‎‎‎‏‎‏‎‎‎‎‎‎No supported application for this NFC Tag‎‏‎‎‏‎"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‏‎‎‏‎‏‏‎‏‏‏‏‏‏‏‏‎‎‎‎‏‎‎‏‏‎‎‎‎‎‎‏‏‎‏‎‏‎‏‎‏‎‎‏‏‎‏‎‏‎‏‎‎‎‎‎‏‎‎‎‏‎‏‏‎‏‏‏‎‎‏‏‎‎‎‏‎‎Tap to learn how to fix.‎‏‎‎‏‎"</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‏‎‎‏‎‏‏‎‏‏‏‏‏‏‏‎‎‏‎‎‏‏‎‎‏‎‎‎‎‎‏‏‏‎‏‏‎‎‏‏‏‏‎‏‏‏‎‎‏‏‎‏‎‏‎‎‎‏‎‏‏‎‎‏‏‎‏‎‎‏‎‎‎‏‎‏‏‎NFC data is being recorded‎‏‎‎‏‎"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‏‎‎‏‎‏‏‎‏‏‏‏‎‏‏‎‏‎‏‏‎‎‎‎‎‏‏‎‏‎‏‏‏‏‏‎‎‎‎‏‎‎‎‎‎‎‎‎‏‏‎‏‏‏‎‎‎‏‎‏‎‎‎‎‏‎‎‎‎‏‎‎‏‏‎‎Tap to stop recording.‎‏‎‎‏‎"</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‏‎‎‏‎‏‏‎‏‏‏‏‏‏‏‎‎‏‏‏‏‏‎‎‏‏‏‎‎‎‎‎‏‎‎‏‏‏‎‎‎‏‎‎‏‏‏‏‎‏‎‏‎‏‎‎‎‎‎‎‎‏‎‏‎‏‏‎‏‎‎‎‎‏‎‎‎‎Allow ‎‏‎‎‏‏‎<xliff:g id="PKG">%1$s</xliff:g>‎‏‎‎‏‏‏‎ to enable NFC?‎‏‎‎‏‎"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‏‎‎‏‎‏‏‎‏‏‏‏‎‎‏‎‎‏‏‎‏‎‎‏‎‎‏‎‏‎‏‎‏‎‎‎‎‎‏‎‎‎‏‏‎‎‎‏‏‎‎‏‎‏‏‎‎‎‏‎‏‏‏‎‎‏‏‏‎‎‏‎‎‎‎Yes‎‏‎‎‏‎"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‏‎‎‏‎‏‏‎‏‏‏‏‏‏‏‎‏‏‎‏‎‏‏‏‎‎‎‏‎‏‏‎‏‎‏‏‎‏‏‏‎‎‎‎‎‎‎‎‎‏‎‏‏‎‏‎‎‏‎‏‏‎‏‎‎‏‎‏‎‏‏‎‏‎‏‏‏‎No‎‏‎‎‏‎"</string>
 </resources>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index e912e7c..9f90229 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"La aplicación no tiene permiso de almacenamiento externo, lo que es necesario para transmitir este archivo"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"¿Abrir vínculo?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"Tu tablet recibió un vínculo mediante NFC:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"Tu teléfono recibió un vínculo mediante NFC:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"Tu teléfono recibió un vínculo mediante NFC:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"La tablet recibió un vínculo a través de NFC:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Abrir vínculo"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"Error de lectura de NFC. Vuelve a intentarlo."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"No hay apps compatibles con esta etiqueta NFC"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Presiona para ver cómo corregir el problema."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"Los datos de NFC se están registrando"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Presiona para dejar de registrarlos."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"¿Quieres permitir que <xliff:g id="PKG">%1$s</xliff:g> habilite la NFC?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Sí"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"No"</string>
 </resources>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index fa5f9fd..3e61a2f 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"La aplicación no tiene permiso de almacenamiento externo, necesario para compartir este archivo"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"¿Quieres abrir el enlace?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"Has recibido un enlace en tu tablet por NFC:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"Has recibido un enlace en tu teléfono por NFC:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"Has recibido un enlace en tu teléfono por NFC:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"Has recibido un enlace en tu tablet por NFC:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Abrir enlace"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"Error de lectura de NFC. Inténtalo de nuevo."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"No hay ninguna aplicación compatible con esta etiqueta NFC"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Toca para saber cómo solucionarlo."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"Los datos de NFC se están registrando"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Toca para detener el registro."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"¿Permitir que <xliff:g id="PKG">%1$s</xliff:g> habilite la opción NFC?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Sí"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"No"</string>
 </resources>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index a57709f..657ba0a 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"Rakendusel pole luba välise salvestusruumi kasutamiseks. See on vajalik faili kiirega edastamiseks"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"Kas avada link?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"Teie tahvelarvuti sai lingi NFC kaudu:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"Teie telefon sai lingi NFC kaudu:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"Teie telefon sai lingi NFC kaudu:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"Teie tahvelarvuti sai lingi NFC kaudu:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Ava link"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"NFC-kiibi lugemise viga. Proovige uuesti."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"Selle NFC-kiibi puhul toetatud rakendus puudub"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Puudutage, et hankida parandamise kohta lisateavet."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"NFC andmeid salvestatakse"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Puudutage salvestamise alustamiseks."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"Kas lubada paketil <xliff:g id="PKG">%1$s</xliff:g> võimaldada NFC kasutamine?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Jah"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"Ei"</string>
 </resources>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index 753d742..d66f296 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -46,13 +46,16 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"Aplikazioak ez dauka kanpoko memoria atzitzeko baimenik. Fitxategia NFC bidez partekatzeko da beharrezkoa baimen hori."</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"Esteka ireki nahi duzu?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"Esteka bat jaso duzu tabletan NFC bidez:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"Esteka bat jaso duzu telefonoan NFC bidez:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"Esteka bat jaso duzu telefonoan NFC bidez:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"Esteka bat jaso duzu tabletan NFC bidez:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Ireki esteka"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"Errore bat gertatu da NFC etiketa irakurtzean. Saiatu berriro."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"Ez dago NFC etiketa hau onartzen duen aplikaziorik"</string>
     <string name="nfc_blocking_alert_title" msgid="1086172436984457085">"Baliteke NFC bidezko konexioa blokeatzea"</string>
-    <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Sakatu hau konponbideei buruzko informazioa lortzeko."</string>
+    <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Sakatu hau arazoa konpontzeko argibideak lortzeko."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"NFCari buruzko datuak erregistratzen"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Sakatu hau grabatzeari uzteko."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"NFC gaitzeko baimena eman nahi diozu <xliff:g id="PKG">%1$s</xliff:g> zerbitzuari?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Bai"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"Ez"</string>
 </resources>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 187ad3d..24eb963 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"برنامه،ِ مجوز فضای ذخیره‌سازی خارجی را ندارد. این مجوز برای «پرتوی» فایل لازم است"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"پیوند باز شود؟"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"‏رایانه لوحی‌تان پیوندی ازطریق NFC دریافت کرد:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"‏تلفنتان پیوندی ازطریق NFC دریافت کرد:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"‏تلفنتان پیوندی ازطریق NFC دریافت کرد:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"‏رایانه لوحی‌تان پیوندی ازطریق NFC دریافت کرد:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"باز کردن پیوند"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"‏خطای خواندن «ارتباطات میدان‌نزدیک» (NFC). دوباره امتحان کنید."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"‏برنامه پشتیبانی‌شده‌ای برای نشان NFC وجود ندارد"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"برای آشنایی با نحوه برطرف کردن مشکل، ضربه بزنید."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"‏داده‌های NFC ضبط می‌شود"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"برای توقف ضبط، ضربه بزنید."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"‏به <xliff:g id="PKG">%1$s</xliff:g> اجازه می‌دهید NFC را فعال کند؟"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"بله"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"نه"</string>
 </resources>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index fe32f2f..5f110d0 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"Sovelluksella ei ole ulkoisen tallennustilan käyttöoikeutta. Se tarvitaan, jotta tämän sovelluksen sisältöä voidaan jakaa."</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"Avataanko linkki?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"Tablettisi vastaanotti linkin NFC:n kautta:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"Puhelimesi vastaanotti linkin NFC:n kautta:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"Puhelimesi vastaanotti linkin NFC:n kautta:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"Tablettisi vastaanotti linkin NFC:n kautta:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Avaa linkki"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"NFC-lukuvirhe. Yritä uudelleen."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"Ei NFC-tagia tukevia sovelluksia"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Napauta, jos haluat oppia korjaamaan ongelman."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"NFC-dataa tallennetaan"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Lopeta tallennus napauttamalla."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"Saako <xliff:g id="PKG">%1$s</xliff:g> ottaa NFC:n käyttöön?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Kyllä"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"Ei"</string>
 </resources>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index 62191b0..860520b 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"L\'application ne possède pas l\'autorisation d\'accès aux dispositifs de stockage externes. Cela est nécessaire pour partager ce fichier."</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"Ouvrir le lien?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"Votre tablette a reçu un lien par CCP :"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"Votre téléphone a reçu un lien par CCP :"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"Votre téléphone a reçu un lien par CCP :"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"Votre tablette a reçu un lien par CCP :"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Ouvrir le lien"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"Erreur de lecture CCP. Réessayez."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"Aucune application prise en charge pour cette balise CCP"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Touchez pour apprendre comment la débloquer."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"Les données CCP sont enregistrées"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Touchez pour cesser l\'enregistrement."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"Autoriser <xliff:g id="PKG">%1$s</xliff:g> à activer la CCP?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Oui"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"Non"</string>
 </resources>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 71a06b2..38bf2d7 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"L\'application n\'est pas autorisée à accéder à l\'espace de stockage externe. Or, cette autorisation est requise pour partager ce fichier."</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"Ouvrir le lien ?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"Votre tablette a reçu un lien via NFC :"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"Votre téléphone a reçu un lien via NFC :"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"Votre téléphone a reçu un lien via NFC :"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"Votre tablette a reçu un lien via NFC :"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Ouvrir le lien"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"Erreur de lecture NFC. Veuillez réessayer."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"Aucune application compatible avec ce tag NFC"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Appuyez pour savoir comment y remédier."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"Enregistrement de données NFC…"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Appuyez pour arrêter l\'enregistrement."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"Autoriser <xliff:g id="PKG">%1$s</xliff:g> à activer le NFC ?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Oui"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"Non"</string>
 </resources>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index becfae4..32baef4 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"A aplicación non ten permiso de acceso ao almacenamento externo, pero é necesario para transferir este ficheiro"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"Queres abrir a ligazón?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"A túa tableta recibiu unha ligazón mediante NFC:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"O teu teléfono recibiu unha ligazón mediante NFC:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"O teu teléfono recibiu unha ligazón mediante NFC:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"A túa tableta recibiu unha ligazón mediante NFC:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Abrir ligazón"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"Produciuse un erro de lectura da etiqueta NFC. Téntao de novo."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"Non hai ningunha aplicación compatible para esta etiqueta NFC"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Toca para ver como podes solucionalo."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"Estanse gravando os datos de NFC"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Toca para deter a gravación."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"Queres permitir que <xliff:g id="PKG">%1$s</xliff:g> active a NFC?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Si"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"Non"</string>
 </resources>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index c63d029..ad869f2 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android બીમ"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"આ ઍપ્લિકેશન પાસે બાહ્ય સ્ટોરેજની પરવાનગી નથી. આ ફાઇલને બીમ કરવા માટે એ જરૂરી છે"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"લિંક ખોલીએ?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"તમારા ટૅબ્લેટને NFC મારફતે એક લિંક પ્રાપ્ત થઈ છે:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"તમારા ફોનને NFC મારફતે એક લિંક પ્રાપ્ત થઈ છે:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"તમારા ફોનને NFC મારફતે એક લિંક પ્રાપ્ત થઈ છે:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"તમારા ટૅબ્લેટને NFC મારફતે એક લિંક પ્રાપ્ત થઈ છે:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"લિંક ખોલો"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"NFC વાંચનમાં ભૂલ. ફરી પ્રયાસ કરો."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"આ NFC ટૅગ માટે કોઈ સહાયક ઍપ્લિકેશન નથી"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"સુધારવાની રીતે વિશે જાણવા માટે ટૅપ કરો."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"NFC ડેટા રેકોર્ડ કરવામાં આવી રહ્યો છે"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"રેકોર્ડિંગ રોકવા માટે ટૅપ કરો."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"શું <xliff:g id="PKG">%1$s</xliff:g>ને NFC ચાલુ કરવાની મંજૂરી આપીએ?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"હા"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"ના"</string>
 </resources>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 5682f13..94e6726 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android बीम"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"ऐप्लिकेशन में बाहरी जगह की अनुमति नहीं है. इस फ़ाइल को बीम करने के लिए यह अनुमति ज़रूरी है"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"लिंक को खोलें?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"आपके टैबलेट पर NFC के ज़रिए एक लिंक आया है:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"आपके फ़ोन पर NFC के ज़रिए एक लिंक आया है:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"आपके फ़ोन पर NFC के ज़रिए एक लिंक आया है:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"आपके टैबलेट पर एनएफ़सी के ज़रिए एक लिंक आया है:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"लिंक को खोलें"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"NFC की जानकारी नहीं पता की जा सकी. कृपया फिर से कोशिश करें."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"ऐसा कोई ऐप्लिकेशन मौजूद नहीं है, जिससे यह एनएफ़सी टैग इस्तेमाल किया जा सके"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"इस समस्या को ठीक करने का तरीका जानने के लिए टैप करें."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"एनएफ़सी का डेटा रिकाॅर्ड किया जा रहा है"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"रिकॉर्डिंग बंद करने के लिए टैप करें."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"क्या एनएफ़सी को चालू करने के लिए <xliff:g id="PKG">%1$s</xliff:g> को अनुमति देनी है?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"हां"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"नहीं"</string>
 </resources>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index 02389c6..e8d8225 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"Aplikacija nema dopuštenje za vanjsku pohranu. To je potrebno za emitiranje datoteke"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"Želite li otvoriti vezu?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"Vaš je tablet primio vezu NFC-om:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"Vaš je telefon primio vezu NFC-om:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"Vaš je telefon primio vezu NFC-om:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"Vaš je tablet primio vezu NFC-om:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Otvori vezu"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"Pogreška pri čitanju NFC-a. Pokušajte ponovo."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"Nema podržane aplikacije za ovu oznaku NFC"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Dodirnite da biste saznali kako to ispraviti."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"Snimaju se podaci NFC-a"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Dodirnite da biste zaustavili snimanje."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"Želite li paketu <xliff:g id="PKG">%1$s</xliff:g> dopustiti da omogući NFC?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Da"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"Ne"</string>
 </resources>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index 7c0b28e..f3ba677 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"Az alkalmazás nem rendelkezik a fájl sugárzásához szükséges Külső tárolási engedéllyel."</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"Megnyitja a linket?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"Táblagépe linket kapott NFC-n keresztül:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"Telefonja linket kapott NFC-n keresztül:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"Telefonja linket kapott NFC-n keresztül:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"Táblagépe linket kapott NFC-n keresztül:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Link megnyitása"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"NFC-olvasási hiba. Próbálja újra."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"Nem található támogatott alkalmazás ehhez az NFC-címkéhez"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Koppintással további információkhoz juthat a probléma elhárításáról."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"A rendszer rögzíti az NFC-adatokat"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Koppintson a rögzítés leállításához."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"Engedélyezi a(z) <xliff:g id="PKG">%1$s</xliff:g> számára az NFC bekapcsolását?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Igen"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"Nem"</string>
 </resources>
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
index a06697c..daad903 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"Հավելվածը չունի արտաքին հիշողություն օգտագործելու թույլտվություն։ Այն անհրաժեշտ է ֆայլը փոխանցելու համար"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"Բացե՞լ հղումը"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"Ձեր պլանշետին ուղարկվել է հղում NFC-ի միջոցով:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"Ձեր հեռախոսին ուղարկվել է հղում NFC-ի միջոցով:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"Ձեր հեռախոսին ուղարկվել է հղում NFC-ի միջոցով:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"Ձեր պլանշետին ուղարկվել է հղում NFC-ի միջոցով"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Բացել հղումը"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"Չհաջողվեց կարդալ NFC պիտակը։ Նորից փորձեք:"</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"Այս NFC պիտակի համար աջակցվող հավելված չկա"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Հպեք և իմացեք՝ ինչպես այն շտկել։"</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"NFC-ի տվյալները գրանցվում են"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Հպեք՝ տվյալների գրանցումը կանգնեցնելու համար։"</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"Թույլատրե՞լ <xliff:g id="PKG">%1$s</xliff:g>-ին միացնել NFC-ն"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Այո"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"Ոչ"</string>
 </resources>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index e9bdc63..a91533b 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"Aplikasi tidak memiliki Izin Penyimpanan Eksternal. Diperlukan untuk melakukan beam file ini"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"Buka link?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"Tablet Anda menerima link melalui NFC:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"Ponsel Anda menerima link melalui NFC:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"Ponsel Anda menerima link melalui NFC:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"Tablet Anda menerima link melalui NFC:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Buka link"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"Pembacaan NFC error. Coba lagi."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"Aplikasi tidak didukung untuk Tag NFC ini"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Ketuk untuk mempelajari cara memperbaikinya."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"Data NFC sedang direkam"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Ketuk untuk menghentikan perekaman."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"Izinkan <xliff:g id="PKG">%1$s</xliff:g> untuk mengaktifkan NFC?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Ya"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"Tidak"</string>
 </resources>
diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml
index 76d00a7..0096620 100644
--- a/res/values-is/strings.xml
+++ b/res/values-is/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"Forritið er ekki með heimild til ytri geymslu. Hana þarf til að hægt sé að senda þessa skrá"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"Opna tengil?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"Spjaldtölvan þín fékk tengil um NFC:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"Síminn þinn fékk tengil um NFC:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"Síminn þinn fékk tengil um NFC:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"Spjaldtölvan þín fékk tengil í gegnum NFC:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Opna tengil"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"Villa í lestri NFC-merkis. Reyndu aftur."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"Ekkert stutt forrit fyrir þetta NFC-merki"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Ýttu til að kynna þér hvernig má lagfæra þetta."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"NFC-gögn eru skráð"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Ýttu til að hætta upptöku."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"Leyfa <xliff:g id="PKG">%1$s</xliff:g> að kveikja á NFC?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Já"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"Nei"</string>
 </resources>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 3a47efe..915c5ab 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"L\'applicazione non presenta l\'autorizzazione per lo spazio di archiviazione esterno che è obbligatoria per trasmettere questo file."</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"Aprire il link?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"Hai ricevuto un link tramite NFC sul tablet:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"Hai ricevuto un link tramite NFC sul telefono:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"Hai ricevuto un link tramite NFC sul telefono:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"Hai ricevuto un link tramite NFC sul tablet:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Apri il link"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"Errore durante la lettura del tag NFC. Riprova."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"Nessuna app supportata per questo tag NFC"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Tocca per scoprire come risolvere il problema."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"È in corso la registrazione dei dati NFC"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Tocca per interrompere la registrazione."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"Consentire a <xliff:g id="PKG">%1$s</xliff:g> di attivare NFC?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Sì"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"No"</string>
 </resources>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 6bfd088..21cd0c1 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"לאפליקציה אין הרשאת גישה לאחסון החיצוני. ההרשאה הזו נדרשת כדי להעביר את הקובץ"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"לפתוח את הקישור?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"‏הטאבלט קיבל קישור דרך NFC:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"‏הטלפון קיבל קישור דרך NFC:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"‏הטלפון קיבל קישור דרך NFC:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"‏התקבל קישור בטאבלט דרך NFC:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"לפתיחת הקישור"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"‏אירעה שגיאת קריאה של NFC. יש לנסות שוב."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"‏אין אפליקציה תומכת לתג NFC זה"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"יש להקיש כדי לראות איך לפתור את הבעיה."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"‏נתוני ה-NFC מתועדים"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"יש להקיש כדי להפסיק את ההקלטה."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"‏להרשות ל-<xliff:g id="PKG">%1$s</xliff:g> להפעיל NFC?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"כן"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"לא"</string>
 </resources>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 5faeca5..953fe19 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Androidビーム"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"アプリに外部ストレージへのアクセス権限が付与されていません。このファイルをビームするには、権限を付与してください"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"リンクを開きますか?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"タブレットで NFC を使用してリンクを受信しました。"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"スマートフォンで NFC を使用してリンクを受信しました。"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"スマートフォンで NFC を使用してリンクを受信しました。"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"タブレットで NFC を使用してリンクを受信しました。"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"リンクを開く"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"NFC 読み取りエラーが発生しました。もう一度お試しください。"</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"この NFC タグをサポートしているアプリはありません"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"タップして修正方法をご確認ください。"</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"NFC データを記録しています"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"タップすると記録を停止します。"</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"<xliff:g id="PKG">%1$s</xliff:g> が NFC を有効にすることを許可しますか?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"はい"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"いいえ"</string>
 </resources>
diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml
index f38b171..a49f6ff 100644
--- a/res/values-ka/strings.xml
+++ b/res/values-ka/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android სხივი"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"აპლიკაციისთვის გარე მეხსიერების ნებართვა მინიჭებული არაა. ამ ფაილის სხივით გადასაცემად, ეს აუცილებელია"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"გსურთ ბმულის გახსნა?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"თქვენმა ტაბლეტმა მიიღო ბმული NFC-ს მეშვეობით:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"თქვენმა ტელეფონმა მიიღო ბმული NFC-ს მეშვეობით:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"თქვენმა ტელეფონმა მიიღო ბმული NFC-ს მეშვეობით:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"თქვენმა ტაბლეტმა მიიღო ბმული NFC-ის მეშვეობით:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"ბმულის გახსნა"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"NFC-ს წაკითხვის შეცდომა. ცადეთ ისევ."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"ამ NFC ტეგისთვის არცერთი მხარდაჭერილი აპლიკაცია არ მოიძებნა"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"შეეხეთ იმის შესატყობად, თუ როგორ უნდა მოაგვაროთ ეს."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"მიმდინარეობს NFC მონაცემების ჩაწერა"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"შეეხეთ ჩაწერის შესაწყვეტად."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"მიეცეს <xliff:g id="PKG">%1$s</xliff:g> პაკეტს NFC-ს ჩართვის უფლება?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"დიახ"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"არა"</string>
 </resources>
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
index 35a65d4..2f0bd57 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"Қолданбаның сыртқы жад рұқсаты жоқ. Ол осы файлды таратуға қажет"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"Сілтемені ашу керек пе?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"Планшетіңіз NFC арқылы сілтеме алды:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"Телефоныңыз NFC арқылы сілтеме алды:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"Телефоныңыз NFC арқылы сілтеме алды:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"Планшетіңіз NFC арқылы сілтеме алды:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Сілтемені ашу"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"NFC оқу қатесі шықты. Қайталап көріңіз."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"Осы NFC картасы үшін бірде-бір қолдау көрсетілетін қолданба жоқ"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Қалай түзетуге болатынын білу үшін түртіңіз."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"NFC деректері жазылып жатыр"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Жазуды тоқтату үшін түртіңіз."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"<xliff:g id="PKG">%1$s</xliff:g> қызметіне NFC технологиясын қосуға рұқсат етілсін бе?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Иә"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"Жоқ"</string>
 </resources>
diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml
index a4dcdef..601618b 100644
--- a/res/values-km/strings.xml
+++ b/res/values-km/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"កម្មវិធីមិនមាន​ការអនុញ្ញាតសម្រាប់ទំហំផ្ទុកខាងក្រៅទេ។ វាត្រូវការ​ការអនុញ្ញាតដើម្បីបញ្ជូនឯកសារនេះ។"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"បើក​តំណ?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"ថេប្លេតរបស់អ្នកទទួលបានតំណមួយតាមរយៈ NFC៖"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"ទូរសព្ទរបស់អ្នកទទួលបានតំណមួយតាមរយៈ NFC៖"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"ទូរសព្ទរបស់អ្នកទទួលបានតំណមួយតាមរយៈ NFC៖"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"ថេប្លេតរបស់អ្នកទទួលបានតំណមួយតាមរយៈ NFC៖"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"បើក​តំណ"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"មាន​បញ្ហា​ក្នុងការអាន NFC។ សូមព្យាយាមម្ដងទៀត។"</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"មិន​មាន​កម្មវិធី​ដែលអាចប្រើ​ស្លាក NFC នេះទេ"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"ចុច ដើម្បី​ស្វែងយល់​អំពីរបៀប​ដោះស្រាយ។"</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"ទិន្នន័យ NFC កំពុងត្រូវបានថត"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"ចុចដើម្បីបញ្ឈប់ការថត។"</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"អនុញ្ញាតឱ្យ <xliff:g id="PKG">%1$s</xliff:g> បើក NFC ឬ?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"បាទ/ចាស"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"ទេ"</string>
 </resources>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index a8b4ddd..39d5887 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"ಅಪ್ಲಿಕೇಶನ್ ಬಾಹ್ಯ ಸಂಗ್ರಹಣಾ ಅನುಮತಿಯನ್ನು ಹೊಂದಿಲ್ಲ. ಈ ಫೈಲ್‌ ಅನ್ನು ಬೀಮ್‌ ಮಾಡಲು ಇದರ ಅಗತ್ಯವಿದೆ"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"ಲಿಂಕ್ ತೆರೆಯುವುದೇ?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"ನಿಮ್ಮ ಟ್ಯಾಬ್ಲೆಟ್ NFC ಮೂಲಕ ಲಿಂಕ್ ಸ್ವೀಕರಿಸಿದೆ:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"ನಿಮ್ಮ ಫೋನ್ NFC ಮೂಲಕ ಲಿಂಕ್ ಸ್ವೀಕರಿಸಿದೆ:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"ನಿಮ್ಮ ಫೋನ್ NFC ಮೂಲಕ ಲಿಂಕ್ ಸ್ವೀಕರಿಸಿದೆ:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"ನಿಮ್ಮ ಟ್ಯಾಬ್ಲೆಟ್ NFC ಮೂಲಕ ಲಿಂಕ್ ಸ್ವೀಕರಿಸಿದೆ:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"ಲಿಂಕ್ ತೆರೆಯಿರಿ"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"NFC ಓದುವಾಗ ದೋಷ ಕಂಡು ಬಂದಿದೆ. ಪುನಃ ಪ್ರಯತ್ನಿಸಿ."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"ಈ NFC ಟ್ಯಾಗ್‌ಗೆ ಯಾವುದೇ ಆ್ಯಪ್‌ ಬೆಂಬಲಿಸುವುದಿಲ್ಲ"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"ಹೇಗೆ ಸರಿಪಡಿಸುವುದು ಎಂಬುದನ್ನು ತಿಳಿಯಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"NFC ಡೇಟಾವನ್ನು ರೆಕಾರ್ಡ್ ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"ರೆಕಾರ್ಡಿಂಗ್ ನಿಲ್ಲಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"NFC ಅನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಲು <xliff:g id="PKG">%1$s</xliff:g> ಅನ್ನು ಅನುಮತಿಸಬೇಕೆ?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"ಹೌದು"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"ಇಲ್ಲ"</string>
 </resources>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index 8e777c0..3ea1570 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"애플리케이션에 외부 저장소 권한이 없습니다. 이 파일을 공유하려면 외부 저장소 권한이 필요합니다."</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"링크를 여시겠습니까?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"태블릿에서 NFC를 통해 링크 수신:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"휴대전화에서 NFC를 통해 링크 수신:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"휴대전화에서 NFC를 통해 링크 수신:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"태블릿에서 NFC를 통해 링크를 수신함:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"링크 열기"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"NFC 읽기 오류입니다. 다시 시도하세요."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"이 NFC 태그에 지원되는 애플리케이션이 없습니다."</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"탭하여 해결 방법을 확인하세요."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"NFC 데이터를 기록 중입니다"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"기록을 중지하려면 탭하세요."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"<xliff:g id="PKG">%1$s</xliff:g>에서 NFC를 사용하도록 허용하시겠습니까?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"예"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"아니요"</string>
 </resources>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index e0cdab3..dfbbe5f 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"Колдонмонун тышкы сактагычты пайдаланууга уруксаты жок. Бул файлды өткөрүү үчүн мындай уруксат берилиши керек"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"Шилтеме ачылсынбы?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"Планшетиңизге NFC аркылуу шилтеме келди:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"Телефонуңузга NFC аркылуу шилтеме келди:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"Телефонуңузга NFC аркылуу шилтеме келди:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"Планшетиңизге NFC аркылуу шилтеме келди:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Шилтемени ачуу"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"NFC\'ни окууда ката кетти. Кайталап көрүңүз."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"Бул NFC энбелгиси үчүн колдоого алынган колдонмо жок"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Кантип оңдоо керектигин билүү үчүн, бул жерди басып коюңуз."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"NFC маалыматы жаздырылууда"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Жаздырууну токтотуу үчүн таптап коюңуз."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"<xliff:g id="PKG">%1$s</xliff:g> кызматына NFC\'ни иштетүүгө уруксат бересизби?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Ооба"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"Жок"</string>
 </resources>
diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml
index 0d03490..d97af90 100644
--- a/res/values-lo/strings.xml
+++ b/res/values-lo/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"ແອັບພລິເຄຊັນບໍ່ມີສິດອະນຸຍາດໃຊ້ບ່ອນຈັດເກັບຂໍ້ມູນພາຍນອກ. ສິດອະນຸຍາດນີ້ຈຳເປັນສຳລັບການສົ່ງໄຟລ໌ນີ້"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"ເປີດລິ້ງ"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"ແທັບເລັດຂອງທ່ານໄດ້ຮັບລິ້ງຜ່ານທາງ NFC:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"ໂທລະສັບຂອງທ່ານໄດ້ຮັບລິ້ງຜ່ານທາງ NFC:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"ໂທລະສັບຂອງທ່ານໄດ້ຮັບລິ້ງຜ່ານທາງ NFC:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"ແທັບເລັດຂອງທ່ານໄດ້ຮັບລິ້ງຜ່ານທາງ NFC:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"ເປີດລິ້ງ"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"ການອ່ານ NFC ຜິດພາດ. ກະລຸນາລອງໃໝ່."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"ບໍ່ມີແອັບພລິເຄຊັນທີ່ຮອງຮັບແທັກ NFC ນີ້"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"ແຕະເພື່ອສຶກສາວິທີແກ້ໄຂ."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"ກຳລັງບັນທຶກຂໍ້ມູນ NFC"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"ແຕະເພື່ອຢຸດການບັນທຶກ."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"ອະນຸຍາດໃຫ້ <xliff:g id="PKG">%1$s</xliff:g> ເປີດການນຳໃຊ້ NFC ບໍ?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"ແມ່ນ"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"ບໍ່"</string>
 </resources>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index 9e3b876..ad1337b 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"„Android“ perdavimas"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"Programai nesuteiktas išorinės saugyklos leidimas. Tai būtina, kad būtų galima perduoti šį failą"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"Atidaryti nuorodą?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"Planšetiniame kompiuteryje gauta nuoroda naudojant ALR:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"Telefone gauta nuoroda naudojant ALR:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"Telefone gauta nuoroda naudojant ALR:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"Planšetiniame kompiuteryje gauta nuoroda naudojant ALR:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Atidaryti nuorodą"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"ALR nuskaitymo klaida. Bandykite dar kartą."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"Nėra šią ALR žymą palaikančių programų"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Palieskite, kad sužinotumėte, kaip sutvarkyti."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"NFC duomenys įrašomi"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Palieskite, kad sustabdytumėte įrašymą."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"Leisti <xliff:g id="PKG">%1$s</xliff:g> norint įgalinti NFC?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Taip"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"Ne"</string>
 </resources>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index 0048799..2752375 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"Lietojumprogrammai nav ārējās krātuves atļaujas. Tā ir nepieciešama, lai varētu kopīgot šo failu, izmantojot funkciju Beam."</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"Vai atvērt saiti?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"Jūsu planšetdatorā tika saņemta saite, izmantojot NFC:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"Jūsu tālrunī tika saņemta saite, izmantojot NFC:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"Jūsu tālrunī tika saņemta saite, izmantojot NFC:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"Jūsu planšetdatorā tika saņemta saite, izmantojot NFC:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Atvērt saiti"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"NFC lasīšanas kļūda. Mēģiniet vēlreiz."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"Šī NFC atzīme netiek atbalstīta nevienā lietojumprogrammā"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Pieskarieties, lai uzzinātu, kā to novērst."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"Notiek NFC datu reģistrēšana"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Lai apturētu reģistrēšanu, pieskarieties."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"Vai atļaut pakotnei <xliff:g id="PKG">%1$s</xliff:g> iespējot NFC?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Jā"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"Nē"</string>
 </resources>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index 4a566b8..7ef5454 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"Апликацијата нема дозвола за надворешната меморија. Тоа е неопходно за пренесување на датотекава"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"Да се отвори линкот?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"Вашиот таблет доби линк преку NFC:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"Вашиот телефон доби линк преку NFC:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"Вашиот телефон доби линк преку NFC:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"Вашиот таблет доби линк преку NFC:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Отвори ја врската"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"Грешка при читањето со NFC. Обидете се повторно."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"Нема поддржана апликација за оваа ознака за NFC"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Допрете за да дознаете како да го решите проблемот."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"Се снимаат податоци од NFC"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Допрете за да престане снимањето."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"Да се дозволи <xliff:g id="PKG">%1$s</xliff:g> да овозможи NFC?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Да"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"Не"</string>
 </resources>
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
index 06a5fa4..3c6e411 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android ബീം"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"ആപ്പിന് എക്‌സ്‌റ്റേണൽ സ്‌റ്റോറേജ് അനുമതി ഇല്ല. ഈ ഫയൽ ബീംചെയ്യാൻ ഇത് ആവശ്യമാണ്"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"ലിങ്ക് തുറക്കണോ?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"നിങ്ങളുടെ ടാബ്‌ലെറ്റിന് NFC വഴി ഒരു ലിങ്ക് ലഭിച്ചു:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"നിങ്ങളുടെ ഫോണിന് NFC വഴി ഒരു ലിങ്ക് ലഭിച്ചു:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"നിങ്ങളുടെ ഫോണിന് NFC വഴി ഒരു ലിങ്ക് ലഭിച്ചു:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"നിങ്ങളുടെ ടാബ്‌ലെറ്റിന് NFC വഴി ഒരു ലിങ്ക് ലഭിച്ചു:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"ലിങ്ക് തുറക്കുക"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"NFC റീഡ് ചെയ്യുന്നതിൽ പിശക്. വീണ്ടും ശ്രമിക്കുക."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"ഈ NFC ടാഗിനെ പിന്തുണയ്ക്കുന്ന ആപ്പുകളൊന്നുമില്ല"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"പരിഹരിക്കുന്നത് എങ്ങനെയെന്ന് മനസ്സിലാക്കാൻ ടാപ്പ് ചെയ്യുക."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"NFC ഡാറ്റ റെക്കോർഡ് ചെയ്യുന്നുണ്ട്"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"റെക്കോർഡ് ചെയ്യുന്നത് നിർത്താൻ ടാപ്പ് ചെയ്യുക."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"NFC പ്രവർത്തനക്ഷമമാക്കാൻ <xliff:g id="PKG">%1$s</xliff:g> എന്നതിനെ അനുവദിക്കണോ?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"ഉവ്വ്"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"ഇല്ല"</string>
 </resources>
diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml
index 546b299..0ef6a20 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Андройд Цацраг"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"Аппад Гадаад сангийн зөвшөөрөл алга. Энэ файлыг дамжуулахад энэ зөвшөөрөл шаардлагатай"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"Холбоосыг нээх үү?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"Таны таблет NFC-р холбоос авсан байна:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"Таны утас NFC-р холбоос авсан байна:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"Таны утас NFC-р холбоос авсан байна:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"Таны таблет NFC-р дамжуулан холбоос хүлээн авсан:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Холбоосыг нээх"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"NFC-г уншихад алдаа гарлаа. Дахин оролдоно уу."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"Энэ NFC Шошгыг зохицуулах боломжтой дэмжигдсэн апп алга"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Хэрхэн засах зааврыг харахын тулд товшино уу."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"NFC өгөгдлийг бичиж байна"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Бичлэгийг зогсоохын тулд товшино уу."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"<xliff:g id="PKG">%1$s</xliff:g>-д NFC-г идэвхжүүлэхийг зөвшөөрөх үү?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Тийм"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"Үгүй"</string>
 </resources>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index a97276f..53acbef 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android बीम"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"ॲप्लिकेशनला बाह्य स्टोरेज परवानगी नाही. ही फाइल बीम करण्यासाठी हे आवश्यक आहे"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"लिंक उघडायची का?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"तुमच्या टॅबलेटला NFC वरून एक लिंक मिळाली आहे:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"तुमच्या फोनला NFC वरून एक लिंक मिळाली आहे:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"तुमच्या फोनला NFC वरून एक लिंक मिळाली आहे:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"तुमच्या टॅबलेटवर NFC द्वारे लिंक मिळाली आहे:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"लिंक उघडा"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"NFC वाचण्यात एरर आली. पुन्हा प्रयत्न करा."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"या NFC टॅग साठी कोणतेही सपोर्ट करणारे ॲप्लिकेशन नाही"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"निराकरण कसे करायचे ते जाणून घेण्यासाठी टॅप करा."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"NFC डेटा रेकॉर्ड केला जात आहे"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"रेकॉर्डिंग थांबवण्यासाठी टॅप करा."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"<xliff:g id="PKG">%1$s</xliff:g> ला NFC सुरू करण्याची अनुमती द्यायची आहे का?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"होय"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"नाही"</string>
 </resources>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index a289ef1..0adedc5 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Pancaran Android"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"Aplikasi tiada Kebenaran Storan Luaran. Kebenaran ini diperlukan untuk Memancarkan fail ini"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"Buka pautan?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"Tablet anda menerima pautan melalui NFC:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"Telefon anda menerima pautan melalui NFC:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"Telefon anda menerima pautan melalui NFC:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"Tablet anda menerima pautan melalui NFC:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Buka pautan"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"Ralat baca NFC. Cuba lagi."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"Tiada aplikasi yang disokong untuk Teg NFC ini"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Ketik untuk mengetahui cara membetulkan perkara ini."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"Data NFC sedang dirakamkan"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Ketik untuk menghentikan rakaman."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"Benarkan <xliff:g id="PKG">%1$s</xliff:g> untuk mendayakan NFC?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Ya"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"Tidak"</string>
 </resources>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index 1e89e3b..005a343 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"အပလီကေးရှင်းတွင် ပြင်ပတွင်သိုလှောင်ခွင့် မပါဝင်ပါ။ ဤဖိုင်ကို လှိုင်းလွှင့်ရန် ၎င်းလိုအပ်ပါသည်"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"လင့်ခ်ကို ဖွင့်လိုပါသလား။"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"သင့်တက်ဘလက်သည် NFC မှတစ်ဆင့် လင့်ခ်တစ်ခု လက်ခံရရှိထားပါသည်-"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"သင့်ဖုန်းသည် NFC မှတစ်ဆင့် လင့်ခ်တစ်ခု လက်ခံရရှိထားပါသည်-"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"သင့်ဖုန်းသည် NFC မှတစ်ဆင့် လင့်ခ်တစ်ခု လက်ခံရရှိထားပါသည်-"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"သင့်တက်ဘလက်သည် NFC မှတစ်ဆင့် လင့်ခ်တစ်ခု လက်ခံရရှိထားပါသည်-"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"လင့်ခ်ဖွင့်ရန်"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"NFC ဖတ်ရှုမှု အမှားရှိနေသည်။ ထပ်စမ်းကြည့်ပါ။"</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"ဤ \'NFC တဂ်\' အတွက် ပံ့ပိုးထားသော အပလီကေးရှင်း မရှိပါ"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"ပြုပြင်နည်းကို လေ့လာရန် တို့ပါ။"</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"NFC ဒေတာကို မှတ်တမ်းတင်နေသည်"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"မှတ်တမ်းတင်ခြင်းရပ်ရန် တို့ပါ။"</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"NFC ဖွင့်ရန် <xliff:g id="PKG">%1$s</xliff:g> ကို ခွင့်ပြုမလား။"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"ဖွင့်မည်"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"မဖွင့်ပါ"</string>
 </resources>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index 280f2f8..d805843 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"Appen har ikke tillatelse for ekstern lagring. Dette er påkrevd for å beame denne filen."</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"Vil du åpne linken?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"Nettbrettet ditt mottok en link via NFC:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"Telefonen din mottok en link via NFC:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"Telefonen din mottok en link via NFC:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"Nettbrettet mottok en link via NFC:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Åpne link"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"NFC-lesefeil. Prøv på nytt."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"Ingen apper støttes for denne NFC-brikken"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Trykk for å finne ut hvordan du løser dette."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"Det registreres NFC-data"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Trykk for å stoppe registreringen."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"Vil du tillate at <xliff:g id="PKG">%1$s</xliff:g> aktiverer NFC?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Ja"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"Nei"</string>
 </resources>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index 6e0ce9d..b894c81 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -20,7 +20,7 @@
     <string name="connected_peripheral" msgid="20748648543160091">"जडित <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="connect_peripheral_failed" msgid="7925702596242839275">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> जडान गर्न सकेन"</string>
     <string name="disconnecting_peripheral" msgid="1443699384809097200">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> विच्छेदन गर्दै"</string>
-    <string name="disconnected_peripheral" msgid="4470578100296504366">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> विच्छेद गरियो"</string>
+    <string name="disconnected_peripheral" msgid="4470578100296504366">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> डिस्कनेक्ट गरियो"</string>
     <string name="pairing_peripheral" msgid="6983626861540899365">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> जोडा गर्दै"</string>
     <string name="pairing_peripheral_failed" msgid="6087643307743264679">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> जोडा गर्न सकेन"</string>
     <string name="failed_to_enable_bt" msgid="7229153323594758077">"ब्लुटुथ सक्षम पार्न सकेन"</string>
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android बिम"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"एपलाई बाह्य भण्डारणसम्बन्धी अनुमति छैन। यो फाइल बिम गर्न अनुमति अनिवार्य छ"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"लिंक खोल्ने हो?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"तपाईंको ट्याब्लेटमा NFC मार्फत एउटा लिंक आयो:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"तपाईंको फोनमा NFC मार्फत एउटा लिंक आयो:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"तपाईंको फोनमा NFC मार्फत एउटा लिंक आयो:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"तपाईंको ट्याब्लेटमा NFC मार्फत एउटा लिंक आयो:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"लिंक खोल्नुहोस्"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"NFC रिड गर्दा त्रुटि भयो। फेरि प्रयास गर्नुहोस्‌।"</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"यो NFC ट्यागको कुनै पनि समर्थित एप छैन"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"यो ब्लक हटाउने तरिका जान्न ट्याप गर्नुहोस्।"</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"NFC डेटा रेकर्ड गरिँदै छ"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"रेकर्ड गर्न छाड्न ट्याप गर्नुहोस्।"</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"<xliff:g id="PKG">%1$s</xliff:g> लाई NFC अन गर्न दिने हो?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"अँ"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"अहँ"</string>
 </resources>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 4aa99fe..ec039b1 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"De app heeft geen rechten voor externe opslag. Dit recht is nodig om het bestand te beamen."</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"Link openen?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"Je tablet heeft een link ontvangen via NFC:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"Je telefoon heeft een link ontvangen via NFC:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"Je telefoon heeft een link ontvangen via NFC:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"Je tablet heeft een link gekregen via NFC:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Link openen"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"Fout bij lezen NFC. Probeer het opnieuw."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"Geen ondersteunde app voor deze NFC-tag"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Tik voor meer informatie over hoe je dit kunt verhelpen."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"NFC-gegevens worden opgenomen"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Tik om te stoppen met opnemen."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"Toestaan dat <xliff:g id="PKG">%1$s</xliff:g> NFC aanzet?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Ja"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"Nee"</string>
 </resources>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index c843244..a771908 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android ବିମ୍‌"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"ଆପ୍ଲୀକେଶନ୍‌କୁ ଏକ୍ସଟର୍ନଲ୍‌ ଷ୍ଟୋରେଜ୍‌ରେ ଲେଖିବାର ଅନୁମତି ନାହିଁ। ଏହି ଫାଇଲ୍‌କୁ ବିମ୍‌ କରିବା ଲାଗି ଏହାର ଅନୁମତି ଆବଶ୍ୟକ"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"ଲିଙ୍କ ଖୋଲିବେ?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"ଆପଣଙ୍କ ଟାବଲେଟ୍‌କୁ NFC ମାଧ୍ୟମରେ ଏକ ଲିଙ୍କ ଆସିଛି:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"ଆପଣଙ୍କ ଫୋନ୍‌କୁ NFC ମାଧ୍ୟମରେ ଏକ ଲିଙ୍କ ଆସିଛି:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"ଆପଣଙ୍କ ଫୋନ୍‌କୁ NFC ମାଧ୍ୟମରେ ଏକ ଲିଙ୍କ ଆସିଛି:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"ଆପଣଙ୍କ ଟାବଲେଟ୍‌କୁ NFC ମାଧ୍ୟମରେ ଏକ ଲିଙ୍କ ଆସିଛି:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"ଲିଙ୍କ ଖୋଲନ୍ତୁ"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"NFC ରିଡ୍‌ କରିବା ବେଳେ ତ୍ରୁଟି। ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"ଏହି NFC ଟ୍ୟାଗ୍‌ ପାଇଁ କୌଣସି ସାମର୍ଥିତ ଆପ୍ଲିକେସନ୍‌ ନାହିଁ"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"କିପରି ସମାଧାନ କରିବେ ସେ ବିଷୟରେ ଜାଣିବା ପାଇଁ ଟାପ୍ କରନ୍ତୁ।"</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"NFC ଡାଟା ରେକର୍ଡ କରାଯାଉଛି"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"ରେକର୍ଡିଂ ବନ୍ଦ କରିବାକୁ ଟାପ କରନ୍ତୁ।"</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"NFCକୁ ସକ୍ଷମ କରିବା ପାଇଁ <xliff:g id="PKG">%1$s</xliff:g>କୁ ଅନୁମତି ଦେବେ?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"ହଁ"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"ନା"</string>
 </resources>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index 71ca6dc..2f52654 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android ਬੀਮ"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"ਐਪਲੀਕੇਸ਼ਨ ਕੋਲ ਬਾਹਰੀ ਸਟੋਰੇਜ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਹੈ। ਫ਼ਾਈਲ ਬੀਮ ਕਰਨ ਲਈ ਇਸਦੀ ਲੋੜ ਹੈ"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"ਕੀ ਲਿੰਕ ਖੋਲ੍ਹਣਾ ਹੈ?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"ਤੁਹਾਡੇ ਟੈਬਲੈੱਟ ਨੂੰ NFC ਰਾਹੀਂ ਇੱਕ ਲਿੰਕ ਪ੍ਰਾਪਤ ਹੋਇਆ:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"ਤੁਹਾਡੇ ਫ਼ੋਨ ਨੂੰ NFC ਰਾਹੀਂ ਇੱਕ ਲਿੰਕ ਪ੍ਰਾਪਤ ਹੋਇਆ:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"ਤੁਹਾਡੇ ਫ਼ੋਨ ਨੂੰ NFC ਰਾਹੀਂ ਇੱਕ ਲਿੰਕ ਪ੍ਰਾਪਤ ਹੋਇਆ:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"ਤੁਹਾਡੇ ਟੈਬਲੈੱਟ \'ਤੇ NFC ਰਾਹੀਂ ਲਿੰਕ ਆਇਆ ਹੈ:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"ਲਿੰਕ ਖੋਲ੍ਹੋ"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"NFC ਪੜ੍ਹਨ ਵਿੱਚ ਗੜਬੜ ਹੋਈ। ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"ਇਸ NFC ਟੈਗ ਲਈ ਕੋਈ ਸਮਰਥਿਤ ਐਪਲੀਕੇਸ਼ਨ ਨਹੀਂ ਹੈ"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"ਇਸ ਨੂੰ ਠੀਕ ਕਰਨ ਦਾ ਤਰੀਕਾ ਜਾਣਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"NFC ਡਾਟੇ ਨੂੰ ਰਿਕਾਰਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"ਰਿਕਾਰਡਿੰਗ ਬੰਦ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"ਕੀ <xliff:g id="PKG">%1$s</xliff:g> ਨੂੰ NFC ਚਾਲੂ ਕਰਨ ਦੀ ਆਗਿਆ ਦੇਣੀ ਹੈ?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"ਹਾਂ"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"ਨਹੀਂ"</string>
 </resources>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 0ce307d..1593ebc 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"Aplikacja nie ma uprawnień do korzystania z pamięci zewnętrznej. Jest to wymagane, by można było przesłać ten plik zbliżeniowo"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"Otworzyć link?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"Na tablet został przesłany link przez NFC:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"Na telefon został przesłany link przez NFC:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"Na telefon został przesłany link przez NFC:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"Na tablet został przesłany link przez NFC:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Otwórz link"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"Błąd odczytu tagu NFC. Spróbuj ponownie."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"Brak obsługiwanej aplikacji dla tego tagu NFC"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Kliknij, by dowiedzieć się, jak to naprawić."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"Trwa rejestrowanie danych NFC"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Kliknij, aby zatrzymać rejestrowanie."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"Zezwolić pakietowi <xliff:g id="PKG">%1$s</xliff:g> na włączenie NFC?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Tak"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"Nie"</string>
 </resources>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index cd344ec..747bc7b 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"A app não tem autorização de armazenamento externo. Esta é necessária para transmitir este ficheiro."</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"Quer abrir o link?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"O seu tablet recebeu um link através de NFC:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"O seu telemóvel recebeu um link através de NFC:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"O seu telemóvel recebeu um link através de NFC:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"O seu tablet recebeu um link através de NFC:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Abrir link"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"Erro de leitura de NFC. Tente novamente."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"Não existe nenhuma app suportada para esta etiqueta NFC."</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Toque para saber como resolver."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"Os dados de NFC estão a ser gravados"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Toque para parar a gravação."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"Permitir que <xliff:g id="PKG">%1$s</xliff:g> ative o NFC?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Sim"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"Não"</string>
 </resources>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 19dd9dd..552aba4 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"O aplicativo não tem permissão de armazenamento externo. Isso é necessário para enviar este arquivo"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"Abrir link?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"Seu tablet recebeu um link por NFC:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"Seu smartphone recebeu um link por NFC:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"Seu smartphone recebeu um link por NFC:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"Seu tablet recebeu um link por NFC:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Abrir link"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"Erro na leitura da NFC. Tente novamente."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"Nenhum aplicativo compatível com esta etiqueta NFC"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Toque para aprender a resolver o problema."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"Os dados NFC estão sendo gravados"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Toque para parar a gravação."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"Permitir que <xliff:g id="PKG">%1$s</xliff:g> ative a NFC?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Sim"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"Não"</string>
 </resources>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 14b2ea0..030356e 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"Aplicația nu are permisiune de stocare externă. Aceasta este necesară pentru transmiterea fișierului."</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"Accesezi linkul?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"Tableta ta a primit un link prin NFC:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"Telefonul tău a primit un link prin NFC:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"Telefonul tău a primit un link prin NFC:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"Tableta ta a primit un link prin NFC:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Accesează linkul"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"Eroare de citire NFC. Încearcă din nou."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"Nu există aplicații acceptate pentru această etichetă NFC"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Atinge ca să afli cum să remediezi problema."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"Se înregistrează date NFC"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Atinge ca să oprești înregistrarea."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"Permiți ca <xliff:g id="PKG">%1$s</xliff:g> să activeze NFC?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Da"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"Nu"</string>
 </resources>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index ef91295..a5c5609 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"У приложения нет разрешения на использование внешнего хранилища. Без этого передать файл невозможно."</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"Открыть ссылку?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"На ваш планшет по NFC поступила ссылка:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"На ваш телефон по NFC поступила ссылка:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"На ваш телефон по NFC поступила ссылка:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"На ваш планшет пришла по NFC ссылка:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Открыть"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"Ошибка чтения NFC-метки. Повторите попытку."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"Для этой NFC-метки нет поддерживаемых приложений"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Нажмите, чтобы узнать, как устранить эту проблему."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"Выполняется запись данных NFC"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Нажмите, чтобы остановить ее."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"Разрешить пакету \"<xliff:g id="PKG">%1$s</xliff:g>\" включить NFC?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Да"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"Нет"</string>
 </resources>
diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml
index d810706..edfb0b5 100644
--- a/res/values-si/strings.xml
+++ b/res/values-si/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android බීම්"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"යෙදුමට බාහිර ගබඩා අවසරය නොමැත. මෙය මෙම ගොනුව කදම්බ කිරීමට අවශ්‍යයි"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"සබැදිය විවෘත කරන්නද?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"ඔබගේ ටැබ්ලට් පරිගණකයට NFC හරහා සබැඳියක් ලැබුණි:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"ඔබගේ දුරකථනයට NFC හරහා සබැඳියක් ලැබුණි:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"ඔබගේ දුරකථනයට NFC හරහා සබැඳියක් ලැබුණි:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"ඔබේ ටැබ්ලටයට NFC හරහා සබැඳියක් ලැබුණි:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"සබැඳිය විවෘත කරන්න"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"NFC කියවීමේ දෝෂයකි. නැවත උත්සාහ කරන්න."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"මෙම NFC ටැගය සඳහා සහාය දක්වන යෙදුම් නැත"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"නිවැරදි කරන ආකාරය දැන ගැනීමට තට්ටු කරන්න."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"NFC දත්ත වාර්තා කරමින් පවතී"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"පටිගත කිරීම නැවැත්වීමට තට්ටු කරන්න."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"NFC සබල කිරීමට <xliff:g id="PKG">%1$s</xliff:g> හට ඉඩ දෙන්න ද?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"ඔව්"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"නැත"</string>
 </resources>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 2b61c63..d18f20a 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"Aplikácia nemá povolenie používať externé úložisko. Toto povolenie sa vyžaduje, ak chcete daný súbor preniesť pomocou funkcie Beam."</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"Otvoriť odkaz?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"Tablet dostal odkaz cez NFC:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"Telefón dostal odkaz cez NFC:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"Telefón dostal odkaz cez NFC:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"Do tabletu prišiel odkaz cez NFC:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Otvoriť odkaz"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"Pri čítaní štítku NFC sa vyskytla chyba. Skúste to znova."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"Pre túto značku NFC nie je k dispozícii žiadna podporovaná aplikácia"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Klepnutím zistite, ako to opraviť."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"Nahrávajú sa údaje NFC"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Nahrávanie zastavíte klepnutím."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"Chcete povoliť balíku <xliff:g id="PKG">%1$s</xliff:g> aktivovať NFC?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Áno"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"Nie"</string>
 </resources>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index 5a1693d..60675a1 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"Aplikacija nima dovoljenja za zunanjo shrambo. Dovoljenje je obvezno za prenos te datoteke."</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"Želite odpreti povezavo?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"Tablični računalnik je prejel povezavo prek vmesnika NFC:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"Telefon je prejel povezavo prek vmesnika NFC:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"Telefon je prejel povezavo prek vmesnika NFC:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"Tablični računalnik je prejel povezavo prek vmesnika NFC:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Odpri povezavo"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"Napaka pri branju oznake NFC. Poskusite znova."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"Za to oznako NFC ni podprta nobena aplikacija"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Dotaknite se, če želite izvedeti, kako odpravite to težavo."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"Poteka snemanje podatkov NFC"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Za ustavitev snemanja se dotaknite."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"Ali dovolite, da paket <xliff:g id="PKG">%1$s</xliff:g> omogoči NFC?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Da"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"Ne"</string>
 </resources>
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index 07bc96c..2478198 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Dërgimi me rreze i androidit"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"Aplikacioni nuk ka leje për hapësirën ruajtëse të jashtme. Kjo kërkohet për dërgimin e këtij skedari me rreze"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"Të hapet lidhja?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"Tableti yt mori një lidhje nëpërmjet NFC-së:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"Telefoni yt mori një lidhje nëpërmjet NFC-së:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"Telefoni yt mori një lidhje nëpërmjet NFC-së:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"Tableti yt mori një lidhje nëpërmjet NFC-së:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Hap lidhjen"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"Gabim në leximin e NFC-s. Provo përsëri."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"Nuk ka asnjë aplikacion të mbështetur për këtë etiketë NFC"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Trokit për të mësuar se si ta rregullosh."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"Të dhënat e lidhjes NFC po regjistrohen"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Trokit për të ndaluar regjistrimin"</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"Të lejohet që <xliff:g id="PKG">%1$s</xliff:g> të aktivizojë NFC-në?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Po"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"Jo"</string>
 </resources>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index 037c884..b29473c 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android пребацивање"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"Апликација нема дозволу за спољну меморију. То је потребно за пребацивање ове датотеке"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"Желите ли да отворите линк?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"Таблет је примио линк преко NFC-а:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"Телефон је примио линк преко NFC-а:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"Телефон је примио линк преко NFC-а:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"Таблет је примио линк преко NFC-а:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Отвори линк"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"Дошло је до грешке у читању NFC ознаке. Пробајте поново."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"Ниједна апликација не подржава ову NFC ознаку"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Додирните да бисте сазнали како да решите проблем."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"NFC подаци се снимају"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Додирните да бисте зауставили снимање."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"Желите да дозволите да <xliff:g id="PKG">%1$s</xliff:g> омогући NFC?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Да"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"Не"</string>
 </resources>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 661550c..8049670 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"Appen har inte behörighet till externt lagringsutrymme. Detta krävs för att överföra filen trådlöst."</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"Vill du öppna länken?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"En länk har skickats till surfplattan via NFC:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"En länk har skickats till mobilen via NFC:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"En länk har skickats till mobilen via NFC:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"En länk har skickats till surfplattan via NFC:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Öppna länk"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"NFC-läsfel. Försök igen."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"Ingen app har stöd för den här NFC-etiketten"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Tryck här för att ta reda på hur du åtgärdar detta."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"NFC-data spelas in"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Tryck för att sluta inspelningen."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"Vill du tillåta att <xliff:g id="PKG">%1$s</xliff:g> aktiverar NFC?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Ja"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"Nej"</string>
 </resources>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 0a573cf..3718ae7 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"Programu haina Ruhusa ya kutumia Hifadhi ya Nje. Hii inahitajika ili Kusambaza faili hii."</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"Ungependa kufungua kiungo?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"Kompyuta kibao yako imepokea kiungo kupitia NFC:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"Simu yako imepokea kiungo kupitia NFC:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"Simu yako imepokea kiungo kupitia NFC:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"Kishikwambi chako kimepokea kiungo kupitia NFC:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Fungua kiungo"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"Hitilafu ya kusoma NFC. Jaribu tena."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"Hakuna programu inayotumika katika Tagi hii ya NFC"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Gusa ili upate maelezo kuhusu jinsi ya kurekebisha."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"Data ya NFC inarekodiwa"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Gusa ili usitishe kurekodi."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"Ungependa kuruhusu <xliff:g id="PKG">%1$s</xliff:g> iwashe huduma ya NFC?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Ndiyo"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"Hapana"</string>
 </resources>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index b3cbfff..06a5322 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android பீம்"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"வெளிப்புறச் சேமிப்பகத்திற்கான அனுமதி பயன்பாட்டிற்கு இல்லை. ஃபைலை பீம் செய்ய, இந்த அனுமதி தேவை"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"இணைப்பைத் திறக்கவா?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"NFC வழியாக உங்கள் டேப்லெட்டுக்கு, ஒரு இணைப்பு வந்துள்ளது:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"NFC வழியாக உங்கள் ஃபோனுக்கு, ஒரு இணைப்பு வந்துள்ளது:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"NFC வழியாக உங்கள் ஃபோனுக்கு, ஒரு இணைப்பு வந்துள்ளது:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"NFC வழியாக உங்கள் டேப்லெட்டுக்கு, ஒரு இணைப்பு வந்துள்ளது:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"இணைப்பைத் திற"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"NFC குறியைப் படிக்க இயலவில்லை. பிறகு முயலவும்."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"இந்த NFC குறிக்கு ஏற்ற ஆப்ஸ் இல்லை"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"எப்படி சரிசெய்வது என்பதை அறிய தட்டவும்."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"NFC தரவு ரெக்கார்டு செய்யப்படுகிறது"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"ரெக்கார்டிங்கை நிறுத்த தட்டவும்."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"NFCயை இயக்க <xliff:g id="PKG">%1$s</xliff:g> ஐ அனுமதிக்கவா?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"ஆம்"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"இல்லை"</string>
 </resources>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index d985991..fd68b7c 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"అప్లికేషన్ బాహ్య స్టోరేజ్‌ అనుమతిని కలిగి లేదు. దీనికి ఈ ఫైల్‌ని బీమ్ చేయడం అవసరమవుతుంది"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"లింక్‌ని తెరవాలా?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"మీ టాబ్లెట్ NFC ద్వారా లింక్‌ని పొందింది:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"మీ ఫోన్ NFC ద్వారా లింక్‌ని పొందింది:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"మీ ఫోన్ NFC ద్వారా లింక్‌ని పొందింది:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"మీ టాబ్లెట్ NFC ద్వారా లింక్‌ను పొందింది:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"లింక్‌ని తెరవండి"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"NFCని రీడ్ చేయ‌డంలో ఎర్రర్. మళ్లీ ట్రై చేయండి"</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"NFC ట్యాగ్‌కి మద్దతు ఇచ్చే అప్లికేషన్ ఏదీ లేదు"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"ఎలా పరిష్కరించాలో తెలుసుకోవడానికి ట్యాప్ చేయండి."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"NFC డేటా రికార్డ్ అవుతోంది"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"రికార్డింగ్ ఆపడానికి ట్యాప్ చేయండి."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"NFCని ఎనేబుల్ చేయడానికి <xliff:g id="PKG">%1$s</xliff:g>‌ని అనుమతించాలా?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"అనుమతించండి"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"అనుమతించవద్దు"</string>
 </resources>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index 41bfff9..942b399 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"แอปพลิเคชันไม่มีสิทธิ์ใช้พื้นที่เก็บข้อมูลภายนอก ซึ่งเป็นสิทธิ์ที่ต้องใช้ในการบีมไฟล์นี้"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"เปิดลิงก์ไหม"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"แท็บเล็ตได้รับลิงก์ทาง NFC:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"โทรศัพท์ได้รับลิงก์ทาง NFC:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"โทรศัพท์ได้รับลิงก์ทาง NFC:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"แท็บเล็ตได้รับลิงก์ทาง NFC:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"เปิดลิงก์"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"เกิดข้อผิดพลาดในการอ่าน NFC โปรดลองอีกครั้ง"</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"ไม่มีแอปพลิเคชันที่รองรับแท็ก NFC นี้"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"แตะเพื่อดูวิธีแก้ไข"</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"กำลังบันทึกข้อมูล NFC"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"แตะเพื่อหยุดบันทึก"</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"อนุญาตให้ <xliff:g id="PKG">%1$s</xliff:g> เปิดใช้ NFC ไหม"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"ใช่"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"ไม่"</string>
 </resources>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index 24a7fd2..16dbc11 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"Walang Pahintulot sa External Storage ang application. Kinakailangan ito upang Ma-beam ang file na ito"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"Buksan ang link?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"Nakatanggap ng link ang iyong tablet sa pamamagitan ng NFC:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"Nakatanggap ng link ang iyong telepono sa pamamagitan ng NFC:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"Nakatanggap ng link ang iyong telepono sa pamamagitan ng NFC:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"Nakatanggap ng link ang iyong tablet sa pamamagitan ng NFC:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Buksan ang link"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"Error sa pagbasa ng NFC. Subukan ulit."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"Walang sinusuportahang application para sa Tag ng NFC na ito"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"I-tap para malaman kung paano ayusin."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"Nagre-record ng data ng NFC"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"I-tap para ihinto ang pag-record."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"Payagan ang <xliff:g id="PKG">%1$s</xliff:g> na i-enable ang NFC?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Oo"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"Hindi"</string>
 </resources>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index 5df3d45..f92781f 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"Uygulamanın Harici Depolama Alanı İzni yok. Dosyanın Işınlanması için bu izin gereklidir."</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"Bağlantı açılsın mı?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"Tabletiniz NFC üzerinden bir bağlantı aldı:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"Telefonunuz NFC üzerinden bir bağlantı aldı:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"Telefonunuz NFC üzerinden bir bağlantı aldı:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"Tabletiniz NFC üzerinden bir bağlantı aldı:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Bağlantıyı aç"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"NFC okuma hatası. Tekrar deneyin."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"Bu NFC Etiketi için desteklenen uygulama yok"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Nasıl düzelteceğinizi öğrenmek için dokunun."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"NFC verileri kaydediliyor"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Kaydı durdurmak için dokunun."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"<xliff:g id="PKG">%1$s</xliff:g> paketinin NFC\'yi etkinleştirmesine izin verilsin mi?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Evet"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"Hayır"</string>
 </resources>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index 5e8beeb..d799e48 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Передавання даних Android"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"Додаток не має доступу до зовнішньої пам’яті. Доступ потрібен для передавання файлу"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"Відкрити посилання?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"На планшет надіслано посилання через зв’язок малого радіуса дії (NFC):"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"На телефон надіслано посилання через зв’язок малого радіуса дії (NFC):"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"На телефон надіслано посилання через зв’язок малого радіуса дії (NFC):"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"На планшеті отримано посилання через NFC:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Відкрити посилання"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"NFC-пристрій не розпізнано. Повторіть спробу."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"Немає підтримуваного додатка для цієї NFC-мітки"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Натисніть, щоб дізнатися, як це виправити."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"Записуються дані NFC"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Натисніть, щоб зупинити записування."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"Дозволити <xliff:g id="PKG">%1$s</xliff:g> вмикати NFC?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Так"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"Ні"</string>
 </resources>
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
index 9b4852d..9e118dd 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"ایپلیکیشن کو خارجی اسٹوریج کی اجازت نہیں ہے۔ اسے یہ فائل بیم کرنے کی ضرورت ہے"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"لنک کھولیں؟"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"‏NFC کے ذریعے آپ کے ٹیبلیٹ کو ایک لنک موصول ہوا ہے:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"‏NFC کے ذریعے آپ کے فون کو ایک لنک موصول ہوا ہے:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"‏NFC کے ذریعے آپ کے فون کو ایک لنک موصول ہوا ہے:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"‏NFC کے ذریعے آپ کے ٹیبلیٹ کو ایک لنک موصول ہوا ہے:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"لنک کھولیں"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"‏NFC پڑھنے میں خرابی۔ دوبارہ کوشش کریں۔"</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"‏اس NFC ٹیگ کے لیے کوئی تعاون یافتہ ایپلیکیشن نہیں ہے"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"درست کرنے کا طریقہ جاننے کے لیے تھپتھپائیں۔"</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"‏NFC ڈیٹا ریکارڈ کیا جا رہا ہے"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"ریکارڈنگ روکنے کے لیے تھپتھپائیں۔"</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"‏<xliff:g id="PKG">%1$s</xliff:g> کو NFC فعال کرنے کی اجازت دیں؟"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"ہاں"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"نہیں"</string>
 </resources>
diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml
index 3e2f153..31065f4 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"Ilovada tashqi xotira ruxsati yo‘q. Bu ruxsat mazkur faylni uzatish uchun zarur"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"Havola ochilsinmi?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"Planshetingizga NFC orqali havola keldi:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"Telefoningizga NFC orqali havola keldi:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"Telefoningizga NFC orqali havola keldi:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"Planshetingizga NFC orqali havola keldi:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Havolani ochish"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"NFC tegni o‘qishda xato. Qaytadan urining."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"Mazkur NFC yorliq bilan ishlaydigan hech qanday ilova topilmadi"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Muammo yechimini olish uchun bosing."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"NFC maʼlumotlar yozib olinmoqda"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Yozib olishni toʻxtatish uchun bosing."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"<xliff:g id="PKG">%1$s</xliff:g> NFC aloqani yoqishiga ruxsat berilsinmi?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Ha"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"Yoʻq"</string>
 </resources>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index 9ec960f..4339d90 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"Ứng dụng không có quyền truy cập vào Bộ nhớ ngoài. Ứng dụng cần phải có quyền này để chiếu tệp này"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"Mở đường liên kết?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"Máy tính bảng của bạn đã nhận được một liên kết thông qua NFC:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"Điện thoại của bạn đã nhận được một liên kết thông qua NFC:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"Điện thoại của bạn đã nhận được một liên kết thông qua NFC:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"Máy tính bảng của bạn đã nhận được một liên kết thông qua NFC:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Mở đường liên kết"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"Lỗi đọc thẻ NFC. Hãy thử lại."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"Không có ứng dụng nào hỗ trợ thẻ NFC này"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Nhấn để tìm hiểu cách khắc phục."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"Dữ liệu về NFC đang được ghi lại"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Nhấn để dừng ghi."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"Cho phép <xliff:g id="PKG">%1$s</xliff:g> bật NFC?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Có"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"Không"</string>
 </resources>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 6468495..87085e5 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"应用不具备外部存储权限。需要此权限才能传输此文件"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"要打开链接吗?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"您的平板电脑已通过 NFC 收到一个链接:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"您的手机已通过 NFC 收到一个链接:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"您的手机已通过 NFC 收到一个链接:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"您的平板电脑通过 NFC 收到了一个链接:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"打开链接"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"NFC 读取错误,请重试。"</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"没有应用支持此 NFC 标签"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"点按即可了解如何解决该问题。"</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"正在记录 NFC 数据"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"点按即可停止记录。"</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"允许“<xliff:g id="PKG">%1$s</xliff:g>”启用 NFC?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"是"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"否"</string>
 </resources>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index 2811fe9..4dd1614 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"應用程式沒有「外部儲存空間權限」。你需要此權限才能傳送此檔案"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"要開啟連結嗎?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"你的平板電腦已透過 NFC 收到一個連結:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"你的手機已透過 NFC 收到一個連結:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"你的手機已透過 NFC 收到一個連結:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"你的平板電腦已透過 NFC 收到一個連結:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"開啟連結"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"NFC 讀取錯誤,請再試一次。"</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"沒有支援此 NFC 標籤的應用程式"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"輕按即可瞭解如何修正問題。"</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"正在記錄 NFC 資料"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"輕按即可停止記錄。"</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"要允許「<xliff:g id="PKG">%1$s</xliff:g>」啟用 NFC 嗎?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"是"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"否"</string>
 </resources>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 396c269..f825904 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"應用程式沒有外部儲存空間權限,授予該權限後才能傳輸這個檔案"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"要開啟連結嗎?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"你的平板電腦已透過 NFC 收到 1 個連結:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"你的手機已透過 NFC 收到 1 個連結:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"你的手機已透過 NFC 收到 1 個連結:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"平板電腦已透過 NFC 收到連結:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"開啟連結"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"NFC 讀取錯誤,請再試一次。"</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"找不到支援這個 NFC 標記的應用程式"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"輕觸即可瞭解如何修正這個問題。"</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"正在記錄 NFC 資料"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"輕觸即可停止記錄。"</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"要允許「<xliff:g id="PKG">%1$s</xliff:g>」啟用 NFC 嗎?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"是"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"否"</string>
 </resources>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index 2d8e23d..3a907de 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -46,8 +46,8 @@
     <string name="android_beam" msgid="1666446406999492763">"I-Android Beam"</string>
     <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"Uhlelo lokusebenza alunayo imvume ekusitoreji esingaphandle. Lokhu kuyadingeka ukuze kwenziwe ibhimu leli fayela"</string>
     <string name="title_confirm_url_open" msgid="8069968913244794478">"Vula isixhumanisi?"</string>
-    <string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"Ithebhulethi yakho ithole isixhumanisi nge-NFC:"</string>
-    <string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"Ifoni yakho ithole isixhumanisi nge-NFC:"</string>
+    <string name="summary_confirm_url_open" msgid="1246398412196449226">"Ifoni yakho ithole isixhumanisi nge-NFC:"</string>
+    <string name="summary_confirm_url_open_tablet" msgid="771152442325809851">"Ithebulethi yakho ithole ilinki nge-NFC:"</string>
     <string name="action_confirm_url_open" msgid="3458322738812921189">"Vula isixhumanisi"</string>
     <string name="tag_read_error" msgid="2485274498885877547">"Iphutha lokufunda le-NFC. Zama futhi."</string>
     <string name="tag_dispatch_failed" msgid="3562984995049738400">"Uhlelo lokusebenza olungasekelwa lale thegi ye-NFC Tag"</string>
@@ -55,4 +55,7 @@
     <string name="nfc_blocking_alert_message" msgid="7003156052570107490">"Thepha ukuze ufunde ukuthi ungalungisa kanjani."</string>
     <string name="nfc_logging_alert_title" msgid="5300867034660942987">"Idatha ye-NFC iyarekhodwa"</string>
     <string name="nfc_logging_alert_message" msgid="1550187184825467942">"Thepha ukuze umise ukurekhoda."</string>
+    <string name="title_package_enabling_nfc" msgid="5736481508428918024">"Vumela i-<xliff:g id="PKG">%1$s</xliff:g> ukunika amandla i-NFC?"</string>
+    <string name="enable_nfc_yes" msgid="694867197186062792">"Yebo"</string>
+    <string name="enable_nfc_no" msgid="6549033065900624599">"Cha"</string>
 </resources>
diff --git a/res/values/config.xml b/res/values/config.xml
index 8d62663..20b7dc3 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -19,6 +19,8 @@
 
     <!-- List of SKUs where Secure NFC functionality is supported -->
     <string-array name="config_skuSupportsSecureNfc" translatable="false" />
+    <!-- Set true if Secure NFC functionality supported for all SKUs -->
+    <bool name="enable_secure_nfc_support">false</bool>
     <!-- NFC blocking alert notification link uri string -->
     <string name="antenna_blocked_alert_link" translatable="false" />
     <!-- NFC Antenna Location API -->
@@ -28,4 +30,11 @@
     <integer-array name="antenna_x">0</integer-array>
     <integer-array name="antenna_y">0</integer-array>
     <bool name="nfc_observe_mode_supported">false</bool>
+    <bool name="nfc_proprietary_getcaps_supported">false</bool>
+    <!-- Allow list that contains the package name which are allowed to use NFC -->
+    <string-array name="nfc_allow_list" translatable="false" />
+    <!-- Number of persistent events to store  -->
+    <integer name="max_event_log_num">20</integer>
+    <!-- Restart NFC stack when a sim insertion/removal event is detected -->
+    <bool name="restart_on_sim_change">false</bool>
 </resources>
diff --git a/res/values/overlayable.xml b/res/values/overlayable.xml
index 45b793e..3fbe0af 100644
--- a/res/values/overlayable.xml
+++ b/res/values/overlayable.xml
@@ -34,6 +34,7 @@
             <item name="unknown_tag_polling_delay_count_max" type="integer" />
             <item name="unknown_tag_polling_delay_long" type="integer" />
             <item name="config_skuSupportsSecureNfc" type="array" />
+            <item name="enable_secure_nfc_support" type="bool" />
             <item name="antenna_blocked_alert_link" type="string" />
             <item name="device_width" type="integer" />
             <item name="device_height" type="integer" />
@@ -41,6 +42,9 @@
             <item name="antenna_x" type="array" />
             <item name="antenna_y" type="array" />
             <item name="nfc_observe_mode_supported" type="bool" />
+            <item name="nfc_proprietary_getcaps_supported" type="bool" />
+            <item name="max_event_log_num" type="integer" />
+            <item name="restart_on_sim_change" type="bool" />
           <!-- Params from config.xml that can be overlaid -->
 
           <!-- Params from strings.xml that can be overlaid -->
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 30983d3..bb42514 100755
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -90,8 +90,9 @@
     <!-- Title of the dialog -->
     <string name="title_confirm_url_open">Open link?</string>
     <!-- Summary text appearing before the URL -->
-    <string name="summary_confirm_url_open" product="tablet">Your tablet received a link through NFC:</string>
-    <string name="summary_confirm_url_open" product="default">Your phone received a link through NFC:</string>
+    <string name="summary_confirm_url_open">Your phone received a link through NFC:</string>
+    <!-- Summary text appearing before the URL, for tablet -->
+    <string name="summary_confirm_url_open_tablet">Your tablet received a link through NFC:</string>
     <!-- Label for the button which causes the URL to be opened -->
     <string name="action_confirm_url_open">Open link</string>
 
@@ -107,4 +108,8 @@
     <string name="nfc_logging_alert_title">NFC data is being recorded</string>
     <!-- Notification message string informing the user that NFC data is being recorded -->
     <string name="nfc_logging_alert_message">Tap to stop recording.</string>
+    <!-- Title of the dialog to allow the package to enable NFC -->
+    <string name="title_package_enabling_nfc">Allow <xliff:g id="pkg">%1$s</xliff:g> to enable NFC?</string>
+    <string name="enable_nfc_yes">Yes</string>
+    <string name="enable_nfc_no">No</string>
 </resources>
diff --git a/shim_src/apex/com/android/nfc/TechListChooserActivity.java b/shim_src/apex/com/android/nfc/TechListChooserActivity.java
new file mode 100644
index 0000000..87f87bd
--- /dev/null
+++ b/shim_src/apex/com/android/nfc/TechListChooserActivity.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.nfc;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+// Stub for compilation only. Uses #createNfcResolverIntent API instead.
+public class TechListChooserActivity extends Activity {
+    public static final String EXTRA_RESOLVE_INFOS = "rlist";
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+    }
+}
diff --git a/src/com/android/nfc/TechListChooserActivity.java b/shim_src/non_apex/com/android/nfc/TechListChooserActivity.java
similarity index 100%
rename from src/com/android/nfc/TechListChooserActivity.java
rename to shim_src/non_apex/com/android/nfc/TechListChooserActivity.java
diff --git a/src/com/android/nfc/ArrayUtils.java b/src/com/android/nfc/ArrayUtils.java
new file mode 100644
index 0000000..1ee5f74
--- /dev/null
+++ b/src/com/android/nfc/ArrayUtils.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.nfc;
+
+import android.annotation.Nullable;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Copied over from frameworks/base/core/java/com/android/internal/util/ArrayUtils.java
+ */
+public class ArrayUtils {
+    private ArrayUtils() { /* cannot be instantiated */ }
+
+    /**
+     * Return first index of {@code value} in {@code array}, or {@code -1} if
+     * not found.
+     */
+    public static <T> int indexOf(@Nullable T[] array, T value) {
+        if (array == null) return -1;
+        for (int i = 0; i < array.length; i++) {
+            if (Objects.equals(array[i], value)) return i;
+        }
+        return -1;
+    }
+
+    /**
+     * Checks if given array is null or has zero elements.
+     */
+    public static boolean isEmpty(@Nullable int[] array) {
+        return array == null || array.length == 0;
+    }
+
+    /**
+     * True if the byte array is null or has length 0.
+     */
+    public static boolean isEmpty(@Nullable byte[] array) {
+        return array == null || array.length == 0;
+    }
+
+    /**
+     * Converts from List of bytes to byte array
+     * @param list
+     * @return byte[]
+     */
+    public static byte[] toPrimitive(List<byte[]> list) {
+        if (list.size() == 0) {
+            return new byte[0];
+        }
+        int byteLen = list.get(0).length;
+        byte[] array = new byte[list.size() * byteLen];
+        for (int i = 0; i < list.size(); i++) {
+            for (int j = 0; j < list.get(i).length; j++) {
+                array[i * byteLen + j] = list.get(i)[j];
+            }
+        }
+        return array;
+    }
+}
diff --git a/src/com/android/nfc/DeviceHost.java b/src/com/android/nfc/DeviceHost.java
index 7ca13c8..9ae403e 100644
--- a/src/com/android/nfc/DeviceHost.java
+++ b/src/com/android/nfc/DeviceHost.java
@@ -18,10 +18,12 @@
 
 import android.annotation.Nullable;
 import android.nfc.NdefMessage;
+import android.nfc.cardemulation.PollingFrame;
 import android.os.Bundle;
 
 import java.io.FileDescriptor;
 import java.io.IOException;
+import java.util.List;
 
 public interface DeviceHost {
     public interface DeviceHostListener {
@@ -43,7 +45,9 @@
 
         public void onHwErrorReported();
 
-        public void onPollingLoopDetected(Bundle pollingFrame);
+        public void onPollingLoopDetected(List<PollingFrame> pollingFrames);
+
+        public void onWlcStopped(int wpt_end_condition);
 
         public void onVendorSpecificEvent(int gid, int oid, byte[] payload);
     }
@@ -71,6 +75,7 @@
         byte[] readNdef();
         boolean writeNdef(byte[] data);
         NdefMessage findAndReadNdef();
+        NdefMessage getNdef();
         boolean formatNdef(byte[] key);
         boolean isNdefFormatable();
         boolean makeReadOnly();
@@ -173,11 +178,7 @@
 
     void dump(FileDescriptor fd);
 
-    boolean enableScreenOffSuspend();
-
-    boolean disableScreenOffSuspend();
-
-    public void doSetScreenState(int screen_state_mask);
+    public void doSetScreenState(int screen_state_mask, boolean alwaysPoll);
 
     public int getNciVersion();
 
@@ -195,6 +196,8 @@
 
     public boolean setObserveMode(boolean enable);
 
+    public boolean isObserveModeEnabled();
+
     /**
     * Get the committed listen mode routing configuration
     */
@@ -220,6 +223,8 @@
      */
     boolean setPowerSavingMode(boolean flag);
 
+    boolean isMultiTag();
+
     void setIsoDepProtocolRoute(int route);
     void setTechnologyABRoute(int route);
     void clearRoutingEntry(int clearFlags);
@@ -233,4 +238,6 @@
      * Sends Vendor NCI command
      */
     NfcVendorNciResponse sendRawVendorCmd(int mt, int gid, int oid, byte[] payload);
+
+    void enableVendorNciNotifications(boolean enabled);
 }
diff --git a/src/com/android/nfc/ForegroundUtils.java b/src/com/android/nfc/ForegroundUtils.java
index ad8d1e3..5d0aa7d 100644
--- a/src/com/android/nfc/ForegroundUtils.java
+++ b/src/com/android/nfc/ForegroundUtils.java
@@ -26,7 +26,7 @@
 import androidx.annotation.VisibleForTesting;
 
 public class ForegroundUtils implements ActivityManager.OnUidImportanceListener {
-    static final boolean DBG = NfcProperties.debug_enabled().orElse(false);
+    static final boolean DBG = NfcProperties.debug_enabled().orElse(true);
     private final String TAG = "ForegroundUtils";
     private final ActivityManager mActivityManager;
 
diff --git a/src/com/android/nfc/NfcApplication.java b/src/com/android/nfc/NfcApplication.java
index d2b94e2..eea9d7e 100644
--- a/src/com/android/nfc/NfcApplication.java
+++ b/src/com/android/nfc/NfcApplication.java
@@ -21,6 +21,7 @@
 import android.app.Application;
 import android.content.pm.PackageManager;
 import android.nfc.Constants;
+import android.os.Looper;
 import android.os.Process;
 import android.os.UserHandle;
 
@@ -63,7 +64,7 @@
             }
         }
         if (UserHandle.myUserId() == 0 && isMainProcess) {
-            mNfcService = new NfcService(this);
+            mNfcService = new NfcService(this, new NfcInjector(this, Looper.myLooper()));
         }
     }
 }
diff --git a/src/com/android/nfc/NfcDiagnostics.java b/src/com/android/nfc/NfcDiagnostics.java
new file mode 100644
index 0000000..268dce1
--- /dev/null
+++ b/src/com/android/nfc/NfcDiagnostics.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.nfc;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.BugreportManager;
+import android.os.BugreportParams;
+import android.os.Build;
+import android.os.SystemClock;
+import android.util.Log;
+
+/**
+ * A class to trigger bugreport and other logs for Nfc related failures
+ */
+public class NfcDiagnostics {
+    private static final String TAG = "NfcDiagnostics";
+    private static final int MS_IN_HOUR = 60 * 60 * 1000;
+    public static final int DEFAULT_BUG_REPORT_MIN_INTERVAL_MS = 24 * MS_IN_HOUR;
+
+    private final Context mContext;
+    private final SystemBuildProperties mSystemBuildProperties;
+    private long mLastBugReportTimeMs;
+
+    static class SystemBuildProperties {
+        /** @return if it is an eng build. */
+        public boolean isEngBuild() {
+            return Build.TYPE.equals("eng");
+        }
+
+        /** @return if it is an userdebug build. */
+        public boolean isUserdebugBuild() {
+            return Build.TYPE.equals("userdebug");
+        }
+
+        /** @return if it is a normal user build. */
+        public boolean isUserBuild() {
+            return Build.TYPE.equals("user");
+        }
+    }
+
+    public NfcDiagnostics(Context context) {
+        mContext = context;
+        mSystemBuildProperties = new SystemBuildProperties();
+    }
+
+    /**
+     * Take a bug report if it is in user debug build and there is no recent bug
+     * report
+     */
+    public void takeBugReport(String bugTitle, String description) {
+        if (!mSystemBuildProperties.isUserdebugBuild()) {
+            Log.d(TAG, "Skip bugreport because it can be triggered only in userDebug build");
+            return;
+        }
+        long currentTimeMs = getElapsedSinceBootMillis();
+        long timeSinceLastUploadMs = currentTimeMs - mLastBugReportTimeMs;
+
+        if (timeSinceLastUploadMs < DEFAULT_BUG_REPORT_MIN_INTERVAL_MS
+                && mLastBugReportTimeMs > 0) {
+            Log.d(TAG, "Bugreport was filed recently, Skip " + bugTitle);
+            return;
+        }
+
+        if (!takeBugreportThroughBetterBug(bugTitle, description)) {
+            takeBugreportThroughBugreportManager(bugTitle, description);
+        }
+    }
+
+    private boolean takeBugreportThroughBetterBug(String bugTitle, String description) {
+        Intent launchBetterBugIntent = new BetterBugIntentBuilder()
+                .setIssueTitle(bugTitle)
+                .setHappenedTimestamp(System.currentTimeMillis())
+                .setAdditionalComment(description)
+                .build();
+        boolean isIntentUnSafe = mContext
+                .getPackageManager().queryIntentActivities(launchBetterBugIntent, 0)
+                .isEmpty();
+        if (isIntentUnSafe) {
+            Log.d(TAG, "intent is unsafe and skip bugreport from betterBug: " + bugTitle);
+            return false;
+        }
+
+        long identity = Binder.clearCallingIdentity();
+        try {
+            launchBetterBugIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+            mContext.startActivity(launchBetterBugIntent);
+            Log.d(TAG, "Taking the bugreport through betterBug: " + bugTitle);
+            mLastBugReportTimeMs = getElapsedSinceBootMillis();
+            return true;
+        } catch (RuntimeException e) {
+            Log.e(TAG, "Error taking bugreport: " + e);
+            return false;
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    private long getElapsedSinceBootMillis() {
+        return SystemClock.elapsedRealtime();
+    }
+
+    private boolean takeBugreportThroughBugreportManager(String bugTitle, String description) {
+        BugreportManager bugreportManager = mContext.getSystemService(BugreportManager.class);
+        BugreportParams params = new BugreportParams(BugreportParams.BUGREPORT_MODE_FULL);
+        try {
+            bugreportManager.requestBugreport(params, bugTitle, description);
+            Log.d(TAG, "Taking the bugreport through bugreportManager: " + bugTitle);
+            mLastBugReportTimeMs = getElapsedSinceBootMillis();
+            return true;
+        } catch (RuntimeException e) {
+            Log.e(TAG, "Error taking bugreport: ", e);
+            return false;
+        }
+    }
+
+    /**
+     * @return the last time when the bug report is taken
+     */
+    long getLastBugReportTimeMs() {
+        return mLastBugReportTimeMs;
+    }
+
+    /**
+     * Builder for communicating with the betterbug.
+     */
+    private static class BetterBugIntentBuilder {
+
+        private static final String ACTION_FILE_BUG_DEEPLINK =
+                "com.google.android.apps.betterbug.intent.FILE_BUG_DEEPLINK";
+        private static final boolean DEFAULT_AUTO_UPLOAD_ENABLED = false;
+        private static final boolean DEFAULT_BUGREPORT_REQUIRED = true;
+        private static final String DEFAULT_BUG_ASSIGNEE = "android-nfc-team@google.com";
+        private static final long DEFAULT_COMPONENT_ID = 48448L;
+
+        private static final String EXTRA_DEEPLINK = "EXTRA_DEEPLINK";
+        private static final String EXTRA_ISSUE_TITLE = "EXTRA_ISSUE_TITLE";
+        private static final String EXTRA_DEEPLINK_SILENT = "EXTRA_DEEPLINK_SILENT";
+        private static final String EXTRA_ADDITIONAL_COMMENT = "EXTRA_ADDITIONAL_COMMENT";
+        private static final String EXTRA_REQUIRE_BUGREPORT = "EXTRA_REQUIRE_BUGREPORT";
+        private static final String EXTRA_HAPPENED_TIME = "EXTRA_HAPPENED_TIME";
+        private static final String EXTRA_BUG_ASSIGNEE = "EXTRA_BUG_ASSIGNEE";
+        private static final String EXTRA_COMPONENT_ID = "EXTRA_COMPONENT_ID";
+
+        private final Intent mBetterBugIntent;
+
+        BetterBugIntentBuilder() {
+            mBetterBugIntent = new Intent().setAction(ACTION_FILE_BUG_DEEPLINK)
+                    .putExtra(EXTRA_DEEPLINK, true);
+            setAutoUpload(DEFAULT_AUTO_UPLOAD_ENABLED);
+            setBugreportRequired(DEFAULT_BUGREPORT_REQUIRED);
+            setBugAssignee(DEFAULT_BUG_ASSIGNEE);
+            setComponentId(DEFAULT_COMPONENT_ID);
+        }
+
+        public BetterBugIntentBuilder setIssueTitle(String title) {
+            mBetterBugIntent.putExtra(EXTRA_ISSUE_TITLE, title);
+            return this;
+        }
+
+        public BetterBugIntentBuilder setAutoUpload(boolean autoUploadEnabled) {
+            mBetterBugIntent.putExtra(EXTRA_DEEPLINK_SILENT, autoUploadEnabled);
+            return this;
+        }
+
+        public BetterBugIntentBuilder setComponentId(long componentId) {
+            mBetterBugIntent.putExtra(EXTRA_COMPONENT_ID, componentId);
+            return this;
+        }
+
+        public BetterBugIntentBuilder setBugreportRequired(boolean isBugreportRequired) {
+            mBetterBugIntent.putExtra(EXTRA_REQUIRE_BUGREPORT, isBugreportRequired);
+            return this;
+        }
+
+        public BetterBugIntentBuilder setHappenedTimestamp(long happenedTimeSinceEpochMs) {
+            mBetterBugIntent.putExtra(EXTRA_HAPPENED_TIME, happenedTimeSinceEpochMs);
+            return this;
+        }
+
+       public BetterBugIntentBuilder setAdditionalComment(String additionalComment) {
+            mBetterBugIntent.putExtra(EXTRA_ADDITIONAL_COMMENT, additionalComment);
+            return this;
+        }
+
+        public BetterBugIntentBuilder setBugAssignee(String assignee) {
+            mBetterBugIntent.putExtra(EXTRA_BUG_ASSIGNEE, assignee);
+            return this;
+        }
+
+        public Intent build() {
+            return mBetterBugIntent;
+        }
+    }
+}
diff --git a/src/com/android/nfc/NfcDispatcher.java b/src/com/android/nfc/NfcDispatcher.java
index 2fbabb4..7bd0b35 100644
--- a/src/com/android/nfc/NfcDispatcher.java
+++ b/src/com/android/nfc/NfcDispatcher.java
@@ -16,6 +16,10 @@
 
 package com.android.nfc;
 
+import static android.content.pm.PackageManager.MATCH_CLONE_PROFILE;
+import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY;
+import static android.nfc.Flags.enableNfcMainline;
+
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.AlertDialog;
@@ -47,6 +51,7 @@
 import android.os.Message;
 import android.os.Messenger;
 import android.os.Process;
+import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.sysprop.NfcProperties;
@@ -79,7 +84,7 @@
  */
 class NfcDispatcher {
     private static final boolean DBG =
-            NfcProperties.debug_enabled().orElse(false);
+            NfcProperties.debug_enabled().orElse(true);
     private static final String TAG = "NfcDispatcher";
 
     static final int DISPATCH_SUCCESS = 1;
@@ -149,6 +154,10 @@
         super.finalize();
     }
 
+    public synchronized void resetForegroundDispatch() {
+        setForegroundDispatch(null, null, new String[][]{});
+    }
+
     public synchronized void setForegroundDispatch(PendingIntent intent,
             IntentFilter[] filters, String[][] techLists) {
         if (DBG) Log.d(TAG, "Set Foreground Dispatch");
@@ -184,6 +193,27 @@
        mProvisioningOnly = false;
     }
 
+    private static Intent createNfcResolverIntent(
+            Intent target,
+            CharSequence title,
+            List<ResolveInfo> resolutionList) {
+        Intent resolverIntent = new Intent(NfcAdapter.ACTION_SHOW_NFC_RESOLVER);
+        resolverIntent.putExtra(Intent.EXTRA_INTENT, target);
+        resolverIntent.putExtra(Intent.EXTRA_TITLE, title);
+        resolverIntent.putParcelableArrayListExtra(
+                NfcAdapter.EXTRA_RESOLVE_INFOS, new ArrayList<>(resolutionList));
+        resolverIntent.setFlags(
+                Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+        return resolverIntent;
+    }
+
+    private static List<ResolveInfo> queryNfcIntentActivitiesAsUser(
+            PackageManager packageManager, Intent intent, UserHandle uh) {
+        return packageManager.queryIntentActivitiesAsUser(intent,
+                ResolveInfoFlags.of(MATCH_DEFAULT_ONLY | MATCH_CLONE_PROFILE),
+                uh);
+    }
+
     /**
      * Helper for re-used objects and methods during a single tag dispatch.
      */
@@ -191,7 +221,7 @@
         public final Intent intent;
         public final Tag tag;
 
-        final Intent rootIntent;
+        Intent rootIntent;
         final Uri ndefUri;
         final String ndefMimeType;
         final PackageManager packageManager;
@@ -258,8 +288,8 @@
             boolean status = false;
             List<UserHandle> luh = getCurrentActiveUserHandles();
             for (UserHandle uh : luh) {
-                List<ResolveInfo> activities = packageManager.queryIntentActivitiesAsUser(intent,
-                        ResolveInfoFlags.of(PackageManager.MATCH_DEFAULT_ONLY), uh);
+                List<ResolveInfo> activities = queryNfcIntentActivitiesAsUser(
+                        packageManager, intent, uh);;
                 activities = activities.stream().filter(activity -> activity.activityInfo.exported)
                         .collect(Collectors.toList());
                 if (activities.size() > 0) {
@@ -308,10 +338,14 @@
             if (muteAppCount > 0) {
                 if (DBG) Log.d(TAG, "muteAppCount = " + muteAppCount);
                 if (filtered.size() > 0) {
-                    rootIntent.setClass(context, TechListChooserActivity.class);
-                    rootIntent.putExtra(Intent.EXTRA_INTENT, intent);
-                    rootIntent.putParcelableArrayListExtra(
-                            TechListChooserActivity.EXTRA_RESOLVE_INFOS, filtered);
+                    if (enableNfcMainline()) {
+                        rootIntent = createNfcResolverIntent(intent, null, filtered);
+                    } else {
+                        rootIntent.setClass(context, TechListChooserActivity.class);
+                        rootIntent.putExtra(Intent.EXTRA_INTENT, intent);
+                        rootIntent.putParcelableArrayListExtra(
+                                TechListChooserActivity.EXTRA_RESOLVE_INFOS, filtered);
+                    }
                 }
             }
             return filtered;
@@ -331,9 +365,8 @@
             // to determine if there is an Activity to handle this intent, and base the
             // result of off that.
             // try current user if there is an Activity to handle this intent
-            List<ResolveInfo> activities = packageManager.queryIntentActivitiesAsUser(intent,
-                    ResolveInfoFlags.of(PackageManager.MATCH_DEFAULT_ONLY),
-                    UserHandle.of(ActivityManager.getCurrentUser()));
+            List<ResolveInfo> activities = queryNfcIntentActivitiesAsUser(
+                    packageManager, intent, UserHandle.of(ActivityManager.getCurrentUser()));
             activities = activities.stream().filter(activity -> activity.activityInfo.exported)
                     .collect(Collectors.toList());
             if (mIsTagAppPrefSupported) {
@@ -362,8 +395,7 @@
             List<UserHandle> userHandles = getCurrentActiveUserHandles();
             userHandles.remove(UserHandle.of(ActivityManager.getCurrentUser()));
             for (UserHandle uh : userHandles) {
-                activities = packageManager.queryIntentActivitiesAsUser(intent,
-                        ResolveInfoFlags.of(PackageManager.MATCH_DEFAULT_ONLY), uh);
+                activities = queryNfcIntentActivitiesAsUser(packageManager, intent, uh);
                 activities = activities.stream().filter(activity -> activity.activityInfo.exported)
                         .collect(Collectors.toList());
                 if (mIsTagAppPrefSupported) {
@@ -394,9 +426,8 @@
 
         boolean tryStartActivity(Intent intentToStart) {
             // try current user if there is an Activity to handle this intent
-            List<ResolveInfo> activities = packageManager.queryIntentActivitiesAsUser(
-                    intentToStart, ResolveInfoFlags.of(PackageManager.MATCH_DEFAULT_ONLY),
-                    UserHandle.of(ActivityManager.getCurrentUser()));
+            List<ResolveInfo> activities = queryNfcIntentActivitiesAsUser(
+                    packageManager, intentToStart, UserHandle.of(ActivityManager.getCurrentUser()));
             activities = activities.stream().filter(activity -> activity.activityInfo.exported)
                     .collect(Collectors.toList());
             if (activities.size() > 0) {
@@ -420,8 +451,7 @@
             List<UserHandle> userHandles = getCurrentActiveUserHandles();
             userHandles.remove(UserHandle.of(ActivityManager.getCurrentUser()));
             for (UserHandle uh : userHandles) {
-                activities = packageManager.queryIntentActivitiesAsUser(intentToStart,
-                        ResolveInfoFlags.of(PackageManager.MATCH_DEFAULT_ONLY), uh);
+                activities = queryNfcIntentActivitiesAsUser(packageManager, intentToStart, uh);
                 activities = activities.stream().filter(activity -> activity.activityInfo.exported)
                         .collect(Collectors.toList());
                 if (mIsTagAppPrefSupported) {
@@ -920,11 +950,15 @@
             dispatch.intent.setComponent(null);
         } else if (matches.size() > 1) {
             // Multiple matches, show a custom activity chooser dialog
-            Intent intent = new Intent(mContext, TechListChooserActivity.class);
-            intent.putExtra(Intent.EXTRA_INTENT, dispatch.intent);
-            intent.putParcelableArrayListExtra(TechListChooserActivity.EXTRA_RESOLVE_INFOS,
-                    matches);
-
+            Intent intent;
+            if (enableNfcMainline()) {
+                intent = createNfcResolverIntent(dispatch.intent, null, matches);
+            } else {
+                intent = new Intent(mContext, TechListChooserActivity.class);
+                intent.putExtra(Intent.EXTRA_INTENT, dispatch.intent);
+                intent.putParcelableArrayListExtra(TechListChooserActivity.EXTRA_RESOLVE_INFOS,
+                        matches);
+            }
             if (DBG) Log.i(TAG, "matched multiple TECH");
             NfcStatsLog.write(NfcStatsLog.NFC_READER_CONFLICT_OCCURRED);
             return dispatch.tryStartActivity(intent);
@@ -1068,6 +1102,11 @@
         return enabled;
     }
 
+    private boolean isTablet() {
+        return Arrays.asList(SystemProperties.get("ro.build.characteristics").split(","))
+                .contains("tablet");
+    }
+
     boolean showWebLinkConfirmation(DispatchInfo dispatch) {
         if (!mContext.getResources().getBoolean(R.bool.enable_nfc_url_open_dialog)) {
             return dispatch.tryStartActivity();
@@ -1077,7 +1116,9 @@
                 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);
+        View view = inflater.inflate(
+            isTablet() ? R.layout.url_open_confirmation_tablet : R.layout.url_open_confirmation,
+            null);
         if (view != null) {
             TextView url = view.findViewById(R.id.url_open_confirmation_link);
             if (url != null) {
diff --git a/src/com/android/nfc/NfcEnableAllowlistActivity.java b/src/com/android/nfc/NfcEnableAllowlistActivity.java
new file mode 100644
index 0000000..c05da47
--- /dev/null
+++ b/src/com/android/nfc/NfcEnableAllowlistActivity.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.nfc;
+
+import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+
+import static com.android.nfc.NfcService.APP_NAME_ENABLING_NFC;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+
+import androidx.annotation.Nullable;
+
+public class NfcEnableAllowlistActivity extends Activity implements View.OnClickListener{
+
+    static final String TAG = "NfcEnableAllowListActivity";
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        Intent intent = getIntent();
+        getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
+        CharSequence appName = intent.getStringExtra(APP_NAME_ENABLING_NFC);
+        String formatString = getString(R.string.title_package_enabling_nfc);
+        AlertDialog mAlertDialog = new AlertDialog.Builder(this, R.style.DialogAlertDayNight)
+                .setTitle(String.format(formatString, appName))
+                .setNegativeButton(R.string.enable_nfc_no, null)
+                .setPositiveButton(R.string.enable_nfc_yes, null)
+                .create();
+
+        mAlertDialog.show();
+        super.onCreate(savedInstanceState);
+        mAlertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(this);
+        mAlertDialog.getButton(AlertDialog.BUTTON_NEGATIVE).setOnClickListener(
+                v -> {
+                    Log.i(TAG, "Nfc is disallowed by user for app: " + appName);
+                    finish();
+                });
+    }
+
+    @Override
+    public void onClick(View v) {
+        NfcService sService = NfcService.getInstance();
+        sService.enableNfc();
+        finish();
+    }
+}
diff --git a/src/com/android/nfc/NfcEventLog.java b/src/com/android/nfc/NfcEventLog.java
new file mode 100644
index 0000000..3b6d151
--- /dev/null
+++ b/src/com/android/nfc/NfcEventLog.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.nfc;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.AtomicFile;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.nfc.proto.NfcEventProto;
+
+import libcore.util.HexEncoding;
+
+import com.google.protobuf.InvalidProtocolBufferException;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayDeque;
+
+/**
+ * Used to store important NFC event logs persistently for debugging purposes.
+ */
+public final class NfcEventLog {
+    private static final String TAG = "NfcEventLog";
+    @VisibleForTesting
+    public static final DateTimeFormatter FORMATTER =
+            DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
+    private final Context mContext;
+    private final NfcInjector mNfcInjector;
+    private final Handler mHander;
+    private final int mMaxEventNum;
+    private final AtomicFile mLogFile;
+    private final ArrayDeque<NfcEventProto.Event> mEventList;
+
+    public NfcEventLog(Context context, NfcInjector nfcInjector, Looper looper,
+            AtomicFile logFile) {
+        mContext = context;
+        mNfcInjector = nfcInjector;
+        mMaxEventNum = context.getResources().getInteger(R.integer.max_event_log_num);
+        mEventList = new ArrayDeque<NfcEventProto.Event>(mMaxEventNum);
+        mHander = new Handler(looper);
+        mLogFile = logFile;
+        mHander.post(() -> readListFromLogFile());
+    }
+
+    private byte[] readLogFile() {
+        byte[] bytes;
+        try {
+            bytes = mLogFile.readFully();
+        } catch (IOException e) {
+            return null;
+        }
+        return bytes;
+    }
+
+    private void writeLogFile(byte[] bytes) throws IOException {
+        FileOutputStream out = null;
+        try {
+            out = mLogFile.startWrite();
+            out.write(bytes);
+            mLogFile.finishWrite(out);
+        } catch (IOException e) {
+            if (out != null) {
+                mLogFile.failWrite(out);
+            }
+            throw e;
+        }
+    }
+
+    private void readListFromLogFile() {
+        byte[] bytes = readLogFile();
+        if (bytes == null) {
+            Log.i(TAG, "No NFC events found in log file");
+            return;
+        }
+        NfcEventProto.EventList eventList;
+        try {
+            eventList = NfcEventProto.EventList.parseFrom(bytes);
+        } catch (InvalidProtocolBufferException e) {
+            Log.e(TAG, "Failed to deserialize events from log file", e);
+            return;
+        }
+        synchronized (mEventList) {
+            for (NfcEventProto.Event event : eventList.getEventsList()) {
+                mEventList.add(event);
+            }
+        }
+    }
+
+    private void writeListToLogFile() {
+        NfcEventProto.EventList.Builder eventListBuilder =
+                NfcEventProto.EventList.newBuilder();
+        synchronized (mEventList) {
+            for (NfcEventProto.Event event:  mEventList) {
+                eventListBuilder.addEvents(event);
+            }
+        }
+        byte[] bytes = eventListBuilder.build().toByteArray();
+        Log.d(TAG, "writeListToLogFile: " + HexEncoding.encodeToString(bytes));
+        try {
+            writeLogFile(bytes);
+        } catch (IOException e) {
+            Log.e(TAG, "Failed to write to log file", e);
+        }
+    }
+
+    private void addAndWriteListToLogFile(NfcEventProto.Event event) {
+        synchronized (mEventList) {
+            // Trim the list to MAX_EVENTS.
+            if (mEventList.size() == mMaxEventNum) {
+                mEventList.remove();
+            }
+            mEventList.add(event);
+            writeListToLogFile();
+        }
+    }
+
+    /**
+     * Log NFC event
+     * Does not block the main NFC thread for logging, posts it to the logging thraead.
+     */
+    public void logEvent(NfcEventProto.EventType eventType) {
+        mHander.post(() -> {
+            NfcEventProto.Event event = NfcEventProto.Event.newBuilder()
+                    .setTimestamp(mNfcInjector.getLocalDateTime().format(FORMATTER))
+                    .setEventType(eventType)
+                    .build();
+            addAndWriteListToLogFile(event);
+        });
+    }
+
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("===== Nfc Event Log =====");
+        synchronized (mEventList) {
+            for (NfcEventProto.Event event: mEventList) {
+                // Cleanup the proto string output to make it more readable.
+                String eventTypeString = event.getEventType().toString()
+                    .replaceAll("# com.android.nfc.proto.*", "")
+                    .replaceAll("\n", "");
+                pw.println(event.getTimestamp() + ": " + eventTypeString);
+            }
+        }
+        pw.println("===== Nfc Event Log =====");
+    }
+
+    @VisibleForTesting
+    public ArrayDeque<NfcEventProto.Event> getEventsList() {
+        return mEventList;
+    }
+}
diff --git a/src/com/android/nfc/NfcInjector.java b/src/com/android/nfc/NfcInjector.java
new file mode 100644
index 0000000..f1a5d06
--- /dev/null
+++ b/src/com/android/nfc/NfcInjector.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.nfc;
+
+import android.annotation.NonNull;
+import android.app.ActivityManager;
+import android.app.backup.BackupManager;
+import android.content.ApexEnvironment;
+import android.content.Context;
+import android.content.res.Resources;
+import android.nfc.Constants;
+import android.nfc.NfcFrameworkInitializer;
+import android.nfc.NfcServiceManager;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.os.VibrationEffect;
+import android.provider.Settings;
+import android.se.omapi.ISecureElementService;
+import android.se.omapi.SeFrameworkInitializer;
+import android.se.omapi.SeServiceManager;
+import android.text.TextUtils;
+import android.util.AtomicFile;
+import android.util.Log;
+
+import com.android.nfc.cardemulation.util.StatsdUtils;
+import com.android.nfc.dhimpl.NativeNfcManager;
+import com.android.nfc.flags.FeatureFlags;
+import com.android.nfc.handover.HandoverDataParser;
+import com.android.nfc.proto.NfcEventProto;
+
+import java.io.File;
+import java.time.LocalDateTime;
+
+/**
+ * To be used for dependency injection (especially helps mocking static dependencies).
+ * TODO: Migrate more classes to injector to resolve circular dependencies in the NFC stack.
+ */
+public class NfcInjector {
+    private static final String TAG = "NfcInjector";
+    private static final String APEX_NAME = "com.android.nfcservices";
+    private static final String NFC_DATA_DIR = "/data/nfc";
+    private static final String EVENT_LOG_FILE_NAME = "event_log.binpb";
+
+    private final Context mContext;
+    private final Looper mMainLooper;
+    private final NfcEventLog mNfcEventLog;
+    private final RoutingTableParser mRoutingTableParser;
+    private final ScreenStateHelper mScreenStateHelper;
+    private final NfcUnlockManager mNfcUnlockManager;
+    private final HandoverDataParser mHandoverDataParser;
+    private final DeviceConfigFacade mDeviceConfigFacade;
+    private final NfcDispatcher mNfcDispatcher;
+    private final VibrationEffect mVibrationEffect;
+    private final BackupManager mBackupManager;
+    private final FeatureFlags mFeatureFlags;
+    private final StatsdUtils mStatsdUtils;
+    private final ForegroundUtils mForegroundUtils;
+    private final NfcDiagnostics mNfcDiagnostics;
+    private final NfcServiceManager.ServiceRegisterer mNfcManagerRegisterer;
+    private static NfcInjector sInstance;
+
+    public static NfcInjector getInstance() {
+        if (sInstance == null) throw new IllegalStateException("Nfc injector instance null");
+        return sInstance;
+    }
+
+
+    public NfcInjector(@NonNull Context context, @NonNull Looper mainLooper) {
+        if (sInstance != null) throw new IllegalStateException("Nfc injector instance not null");
+
+        mContext = context;
+        mMainLooper = mainLooper;
+        mRoutingTableParser = new RoutingTableParser();
+        mScreenStateHelper = new ScreenStateHelper(mContext);
+        mNfcUnlockManager = NfcUnlockManager.getInstance();
+        mHandoverDataParser = new HandoverDataParser();
+        mDeviceConfigFacade = new DeviceConfigFacade(mContext, new Handler(mainLooper));
+        mNfcDispatcher = new NfcDispatcher(mContext, mHandoverDataParser, isInProvisionMode());
+        mVibrationEffect = VibrationEffect.createOneShot(200, VibrationEffect.DEFAULT_AMPLITUDE);
+        mBackupManager = new BackupManager(mContext);
+        mFeatureFlags = new com.android.nfc.flags.FeatureFlagsImpl();
+        mStatsdUtils = mFeatureFlags.statsdCeEventsFlag() ? new StatsdUtils() : null;
+        mForegroundUtils =
+                ForegroundUtils.getInstance(mContext.getSystemService(ActivityManager.class));
+        mNfcDiagnostics = new NfcDiagnostics(mContext);
+
+        NfcServiceManager manager = NfcFrameworkInitializer.getNfcServiceManager();
+        if (manager == null) {
+            Log.e(TAG, "NfcServiceManager is null");
+            throw new UnsupportedOperationException();
+        }
+        mNfcManagerRegisterer = manager.getNfcManagerServiceRegisterer();
+
+        // Create UWB event log thread.
+        HandlerThread eventLogThread = new HandlerThread("NfcEventLog");
+        eventLogThread.start();
+        mNfcEventLog = new NfcEventLog(mContext, this, eventLogThread.getLooper(),
+                new AtomicFile(new File(NFC_DATA_DIR, EVENT_LOG_FILE_NAME)));
+        sInstance = this;
+    }
+
+    public Context getContext() {
+        return mContext;
+    }
+
+    public Looper getMainLooper() {
+        return mMainLooper;
+    }
+
+    public NfcEventLog getNfcEventLog() {
+        return mNfcEventLog;
+    }
+
+    public ScreenStateHelper getScreenStateHelper() {
+        return mScreenStateHelper;
+    }
+
+    public RoutingTableParser getRoutingTableParser() {
+        return mRoutingTableParser;
+    }
+
+    public NfcUnlockManager getNfcUnlockManager() {
+        return mNfcUnlockManager;
+    }
+
+    public HandoverDataParser getHandoverDataParser() {
+        return mHandoverDataParser;
+    }
+
+    public DeviceConfigFacade getDeviceConfigFacade() {
+        return mDeviceConfigFacade;
+    }
+
+    public NfcDispatcher getNfcDispatcher() {
+        return mNfcDispatcher;
+    }
+
+    public VibrationEffect getVibrationEffect() {
+        return mVibrationEffect;
+    }
+
+    public BackupManager getBackupManager() {
+        return mBackupManager;
+    }
+
+    public FeatureFlags getFeatureFlags() {
+        return mFeatureFlags;
+    }
+
+    public StatsdUtils getStatsdUtils() {
+        return mStatsdUtils;
+    }
+
+    public ForegroundUtils getForegroundUtils() {
+        return mForegroundUtils;
+    }
+
+    public NfcDiagnostics getNfcDiagnostics() {
+        return mNfcDiagnostics;
+    }
+
+    public NfcServiceManager.ServiceRegisterer getNfcManagerRegisterer() {
+        return mNfcManagerRegisterer;
+    }
+
+    public DeviceHost makeDeviceHost(DeviceHost.DeviceHostListener listener) {
+        return new NativeNfcManager(mContext, listener);
+    }
+
+    /**
+     * NFC apex DE folder.
+     */
+    public static File getDeviceProtectedDataDir() {
+        return ApexEnvironment.getApexEnvironment(APEX_NAME)
+                .getDeviceProtectedDataDir();
+    }
+
+    public LocalDateTime getLocalDateTime() {
+        return LocalDateTime.now();
+    }
+
+    public boolean isInProvisionMode() {
+        boolean isNfcProvisioningEnabled = false;
+        try {
+            isNfcProvisioningEnabled = mContext.getResources().getBoolean(
+                    R.bool.enable_nfc_provisioning);
+        } catch (Resources.NotFoundException e) {
+        }
+
+        if (isNfcProvisioningEnabled) {
+            return Settings.Global.getInt(mContext.getContentResolver(),
+                    Settings.Global.DEVICE_PROVISIONED, 0) == 0;
+        } else {
+            return false;
+        }
+    }
+
+    public boolean checkIsSecureNfcCapable() {
+        if (mContext.getResources().getBoolean(R.bool.enable_secure_nfc_support)) {
+            return true;
+        }
+        String[] skuList = mContext.getResources().getStringArray(
+                R.array.config_skuSupportsSecureNfc);
+        String sku = SystemProperties.get("ro.boot.hardware.sku");
+        if (TextUtils.isEmpty(sku) || !Utils.arrayContains(skuList, sku)) {
+            return false;
+        }
+        return true;
+    }
+
+    public ISecureElementService connectToSeService() throws RemoteException {
+        SeServiceManager manager = SeFrameworkInitializer.getSeServiceManager();
+        if (manager == null) {
+            Log.e(TAG, "SEServiceManager is null");
+            return null;
+        }
+        return ISecureElementService.Stub.asInterface(
+                manager.getSeManagerServiceRegisterer().get());
+    }
+
+    /**
+     * Kill the NFC stack.
+     */
+    public void killNfcStack() {
+        System.exit(0);
+    }
+
+    public boolean isSatelliteModeSensitive() {
+        final String satelliteRadios =
+                Settings.Global.getString(mContext.getContentResolver(),
+                        Constants.SETTINGS_SATELLITE_MODE_RADIOS);
+        return satelliteRadios == null || satelliteRadios.contains(Settings.Global.RADIO_NFC);
+    }
+
+    /** Returns true if satellite mode is turned on. */
+    public boolean isSatelliteModeOn() {
+        if (!isSatelliteModeSensitive()) return false;
+        return Settings.Global.getInt(
+                mContext.getContentResolver(), Constants.SETTINGS_SATELLITE_MODE_ENABLED, 0) == 1;
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/nfc/NfcPermissions.java b/src/com/android/nfc/NfcPermissions.java
index 4d43ef6..c88b667 100644
--- a/src/com/android/nfc/NfcPermissions.java
+++ b/src/com/android/nfc/NfcPermissions.java
@@ -1,5 +1,7 @@
 package com.android.nfc;
 
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
 import android.app.ActivityManager;
 import android.content.Context;
 import android.os.Binder;
@@ -67,6 +69,9 @@
         context.enforceCallingOrSelfPermission(ADMIN_PERM, ADMIN_PERM_ERROR);
     }
 
+    public static boolean checkAdminPermissions(Context context) {
+        return context.checkCallingPermission(ADMIN_PERM) == PERMISSION_GRANTED;
+    }
 
     public static void enforceUserPermissions(Context context) {
         context.enforceCallingOrSelfPermission(NFC_PERMISSION, NFC_PERM_ERROR);
diff --git a/src/com/android/nfc/NfcProprietaryCaps.java b/src/com/android/nfc/NfcProprietaryCaps.java
new file mode 100644
index 0000000..d9890ed
--- /dev/null
+++ b/src/com/android/nfc/NfcProprietaryCaps.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.nfc;
+
+import android.util.Log;
+
+import java.util.Arrays;
+
+public class NfcProprietaryCaps {
+    private static final String TAG = "NfcProprietaryCaps";
+    private static final int PASSIVE_OBSERVE_MODE = 0;
+    private static final int POLLING_FRAME_NTF = 1;
+    private static final int POWER_SAVING_MODE = 2;
+    private static final int AUTOTRANSACT_POLLING_LOOP_FILTER = 3;
+    private final PassiveObserveMode mPassiveObserveMode;
+    private final boolean mIsPollingFrameNotificationSupported;
+    private final boolean mIsPowerSavingModeSupported;
+    private final boolean mIsAutotransactPollingLoopFilterSupported;
+
+    public enum PassiveObserveMode {
+        NOT_SUPPORTED,
+        SUPPORT_WITH_RF_DEACTIVATION,
+        SUPPORT_WITHOUT_RF_DEACTIVATION,
+    }
+
+    public PassiveObserveMode getPassiveObserveMode() {
+        return mPassiveObserveMode;
+    }
+
+    public boolean isPollingFrameNotificationSupported() {
+        return mIsPollingFrameNotificationSupported;
+    }
+
+    public boolean isPowerSavingModeSupported() {
+        return mIsPowerSavingModeSupported;
+    }
+
+    public boolean isAutotransactPollingLoopFilterSupported() {
+        return mIsAutotransactPollingLoopFilterSupported;
+    }
+
+    public NfcProprietaryCaps(PassiveObserveMode passiveObserveMode,
+            boolean isPollingFrameNotificationSupported, boolean isPowerSavingModeSupported,
+            boolean isAutotransactPollingLoopFilterSupported) {
+        mPassiveObserveMode = passiveObserveMode;
+        mIsPollingFrameNotificationSupported = isPollingFrameNotificationSupported;
+        mIsPowerSavingModeSupported = isPowerSavingModeSupported;
+        mIsAutotransactPollingLoopFilterSupported = isAutotransactPollingLoopFilterSupported;
+    }
+
+    public static NfcProprietaryCaps createFromByteArray(byte[] caps) {
+        Log.i(TAG, "parsing proprietary caps: " + Arrays.toString(caps));
+        PassiveObserveMode passiveObserveMode = PassiveObserveMode.NOT_SUPPORTED;
+        boolean isPollingFrameNotificationSupported = false;
+        boolean isPowerSavingModeSupported = false;
+        boolean isAutotransactPollingLoopFilterSupported  = false;
+        int offset = 0;
+        while ((offset + 2) < caps.length) {
+            int id = caps[offset++];
+            int value_len = caps[offset++];
+            int value_offset = offset;
+            offset += value_len;
+
+            // value bounds check
+            // all caps have minimum length of 1, check this bound
+            // here to simplify match cases.
+            if (value_len < 1 || offset > caps.length) {
+                break;
+            }
+            switch (id) {
+                case PASSIVE_OBSERVE_MODE:
+                    passiveObserveMode = switch (caps[value_offset]) {
+                        case 0 -> PassiveObserveMode.NOT_SUPPORTED;
+                        case 1 -> PassiveObserveMode.SUPPORT_WITH_RF_DEACTIVATION;
+                        case 2 -> PassiveObserveMode.SUPPORT_WITHOUT_RF_DEACTIVATION;
+                        default -> passiveObserveMode;
+                    };
+                    break;
+                case POLLING_FRAME_NTF:
+                    isPollingFrameNotificationSupported = caps[value_offset] == 0x1;
+                    break;
+                case POWER_SAVING_MODE:
+                    isPowerSavingModeSupported = caps[value_offset] == 0x1;
+                    break;
+                case AUTOTRANSACT_POLLING_LOOP_FILTER:
+                    isAutotransactPollingLoopFilterSupported = caps[value_offset] == 0x1;
+                    break;
+            }
+        }
+        return new NfcProprietaryCaps(passiveObserveMode, isPollingFrameNotificationSupported,
+                isPowerSavingModeSupported, isAutotransactPollingLoopFilterSupported);
+    }
+}
diff --git a/src/com/android/nfc/NfcService.java b/src/com/android/nfc/NfcService.java
index 6e322e5..74fcce2 100644
--- a/src/com/android/nfc/NfcService.java
+++ b/src/com/android/nfc/NfcService.java
@@ -16,6 +16,11 @@
 
 package com.android.nfc;
 
+import static com.android.nfc.NfcStatsLog.NFC_OBSERVE_MODE_STATE_CHANGED__TRIGGER_SOURCE__FOREGROUND_APP;
+import static com.android.nfc.NfcStatsLog.NFC_OBSERVE_MODE_STATE_CHANGED__TRIGGER_SOURCE__TRIGGER_SOURCE_UNKNOWN;
+import static com.android.nfc.NfcStatsLog.NFC_OBSERVE_MODE_STATE_CHANGED__TRIGGER_SOURCE__WALLET_ROLE_HOLDER;
+
+import android.annotation.NonNull;
 import android.app.ActivityManager;
 import android.app.Application;
 import android.app.BroadcastOptions;
@@ -23,8 +28,8 @@
 import android.app.KeyguardManager.KeyguardLockedStateListener;
 import android.app.PendingIntent;
 import android.app.VrManager;
-import android.app.admin.DevicePolicyManager;
 import android.app.backup.BackupManager;
+import android.app.role.RoleManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -42,9 +47,6 @@
 import android.net.Uri;
 import android.nfc.AvailableNfcAntenna;
 import android.nfc.Constants;
-import android.nfc.NfcFrameworkInitializer;
-import android.nfc.NfcServiceManager;
-import android.nfc.cardemulation.CardEmulation;
 import android.nfc.ErrorCodes;
 import android.nfc.FormatException;
 import android.nfc.IAppCallback;
@@ -52,19 +54,25 @@
 import android.nfc.INfcAdapterExtras;
 import android.nfc.INfcCardEmulation;
 import android.nfc.INfcControllerAlwaysOnListener;
-import android.nfc.INfcVendorNciCallback;
 import android.nfc.INfcDta;
 import android.nfc.INfcFCardEmulation;
 import android.nfc.INfcOemExtensionCallback;
 import android.nfc.INfcTag;
 import android.nfc.INfcUnlockHandler;
+import android.nfc.INfcVendorNciCallback;
+import android.nfc.INfcWlcStateListener;
 import android.nfc.ITagRemovedCallback;
 import android.nfc.NdefMessage;
 import android.nfc.NfcAdapter;
 import android.nfc.NfcAntennaInfo;
+import android.nfc.NfcFrameworkInitializer;
+import android.nfc.NfcServiceManager;
 import android.nfc.Tag;
 import android.nfc.TechListParcel;
 import android.nfc.TransceiveResult;
+import android.nfc.WlcListenerDeviceInfo;
+import android.nfc.cardemulation.CardEmulation;
+import android.nfc.cardemulation.PollingFrame;
 import android.nfc.tech.Ndef;
 import android.nfc.tech.TagTechnology;
 import android.os.AsyncTask;
@@ -73,11 +81,13 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.Looper;
 import android.os.Message;
+import android.os.ParcelFileDescriptor;
 import android.os.PowerManager;
+import android.os.PowerManager.OnThermalStatusChangedListener;
 import android.os.Process;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
@@ -91,6 +101,7 @@
 import android.se.omapi.SeFrameworkInitializer;
 import android.se.omapi.SeServiceManager;
 import android.sysprop.NfcProperties;
+import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.EventLog;
 import android.util.Log;
@@ -104,8 +115,9 @@
 import com.android.nfc.cardemulation.util.StatsdUtils;
 import com.android.nfc.dhimpl.NativeNfcManager;
 import com.android.nfc.flags.FeatureFlags;
-import com.android.nfc.Utils;
 import com.android.nfc.handover.HandoverDataParser;
+import com.android.nfc.proto.NfcEventProto;
+import com.android.nfc.wlc.NfcCharging;
 
 import org.json.JSONException;
 import org.json.JSONObject;
@@ -124,25 +136,24 @@
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.HexFormat;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.Scanner;
 import java.util.Set;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.stream.Collectors;
 import java.util.concurrent.ExecutionException;
-import java.util.concurrent.FutureTask;
-import java.util.concurrent.TimeoutException;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Executor;
 import java.util.concurrent.ExecutorService;
-
+import java.util.concurrent.Executors;
+import java.util.concurrent.FutureTask;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.stream.Collectors;
 
 public class NfcService implements DeviceHostListener, ForegroundUtils.Callback {
-    static final boolean DBG = NfcProperties.debug_enabled().orElse(false);
+    static final boolean DBG = NfcProperties.debug_enabled().orElse(true);
+    private static final boolean VDBG = false; // turn on for local testing.
     static final String TAG = "NfcService";
     private static final int APP_INFO_FLAGS_SYSTEM_APP =
             ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
@@ -160,6 +171,9 @@
     static final String PREF_NFC_READER_OPTION_ON = "nfc_reader_on";
     static final boolean NFC_READER_OPTION_DEFAULT = true;
 
+    static final String PREF_NFC_CHARGING_ON = "nfc_charging_on";
+    static final boolean NFC_CHARGING_ON_DEFAULT = true;
+
     static final String PREF_SECURE_NFC_ON = "secure_nfc_on";
     static final boolean SECURE_NFC_ON_DEFAULT = false;
     static final String PREF_FIRST_BOOT = "first_boot";
@@ -244,6 +258,9 @@
     public static final String ACTION_RF_FIELD_OFF_DETECTED =
             "com.android.nfc_extras.action.RF_FIELD_OFF_DETECTED";
 
+    public static final String APP_NAME_ENABLING_NFC =
+            "com.android.nfc.PACKAGE_NAME_ENABLING_NFC";
+
     public static boolean sIsShortRecordLayout = false;
 
     public static boolean sIsNfcRestore = false;
@@ -272,6 +289,7 @@
     private static final int NCI_MSG_PROP_ANDROID = 0x0C;
     private static final int NCI_MSG_PROP_ANDROID_POWER_SAVING = 0x01;
 
+    private final Looper mLooper;
     private final UserManager mUserManager;
     private final ActivityManager mActivityManager;
 
@@ -350,6 +368,8 @@
     boolean mIsReaderOptionEnabled = true;
     boolean mReaderOptionCapable;
     Context mContext;
+    NfcInjector mNfcInjector;
+    NfcEventLog mNfcEventLog;
     private DeviceHost mDeviceHost;
     private SharedPreferences mPrefs;
     private SharedPreferences.Editor mPrefsEditor;
@@ -374,6 +394,12 @@
     boolean mIsRecovering;
     boolean mIsNfcUserRestricted;
     boolean mIsWatchType;
+    boolean mPendingPowerStateUpdate;
+    boolean mIsWlcCapable;
+    boolean mIsWlcEnabled;
+    boolean mIsRWCapable;
+    WlcListenerDeviceInfo mWlcListenerDeviceInfo;
+    public NfcDiagnostics  mNfcDiagnostics;
 
     // polling delay control variables
     private final int mPollDelayTime;
@@ -395,6 +421,7 @@
     private HandoverDataParser mHandoverDataParser;
     private ContentResolver mContentResolver;
     private CardEmulationManager mCardEmulationManager;
+    private NfcCharging mNfcCharging;
     private Vibrator mVibrator;
     private VibrationEffect mVibrationEffect;
     private ISecureElementService mSEService;
@@ -416,8 +443,9 @@
     private final Set<INfcControllerAlwaysOnListener> mAlwaysOnListeners =
             Collections.synchronizedSet(new HashSet<>());
 
-    private final FeatureFlags mFeatureFlags = new com.android.nfc.flags.FeatureFlagsImpl();
-
+    private final FeatureFlags mFeatureFlags;
+    private final Set<INfcWlcStateListener> mWlcStateListener =
+            Collections.synchronizedSet(new HashSet<>());
     private final StatsdUtils mStatsdUtils;
 
     private  INfcVendorNciCallback mNfcVendorNciCallBack = null;
@@ -459,17 +487,25 @@
     @Override
     public void onRemoteFieldActivated() {
         sendMessage(NfcService.MSG_RF_FIELD_ACTIVATED, null);
+
+        if (mStatsdUtils != null) {
+            mStatsdUtils.logFieldChanged(true, 0);
+        }
     }
 
     @Override
     public void onRemoteFieldDeactivated() {
         sendMessage(NfcService.MSG_RF_FIELD_DEACTIVATED, null);
+
+        if (mStatsdUtils != null) {
+            mStatsdUtils.logFieldChanged(false, 0);
+        }
     }
 
     @Override
-    public void onPollingLoopDetected(Bundle pollingFrame) {
-        if (mCardEmulationManager != null) {
-            mCardEmulationManager.onPollingLoopDetected(pollingFrame);
+    public void onPollingLoopDetected(List<PollingFrame> frames) {
+        if (mCardEmulationManager != null && android.nfc.Flags.nfcReadPollingLoop()) {
+            mCardEmulationManager.onPollingLoopDetected(frames);
         }
     }
 
@@ -484,8 +520,7 @@
         new ApplyRoutingTask().execute();
     }
 
-    @Override
-    public void onHwErrorReported() {
+    private void restartStack() {
         try {
             mContext.unregisterReceiver(mReceiver);
         } catch (IllegalArgumentException e) {
@@ -497,8 +532,13 @@
     }
 
     @Override
+    public void onHwErrorReported() {
+        restartStack();
+    }
+
+    @Override
     public void onVendorSpecificEvent(int gid, int oid, byte[] payload) {
-        sendVendorNciNotification(gid, oid, payload);
+        mHandler.post(() -> mNfcAdapter.sendVendorNciNotification(gid, oid, payload));
     }
 
     /**
@@ -529,6 +569,33 @@
         return false;
     }
 
+    public void onWlcData(Map<String, Integer> WlcDeviceInfo) {
+        for (String key : WlcDeviceInfo.keySet()) {
+            Log.d(TAG, " onWlcData  " + key + " =  " + WlcDeviceInfo.get(key));
+        }
+        synchronized (mWlcStateListener) {
+            mWlcListenerDeviceInfo = new WlcListenerDeviceInfo(
+                    WlcDeviceInfo.get(mNfcCharging.VendorId),
+                    WlcDeviceInfo.get(mNfcCharging.TemperatureListener),
+                    WlcDeviceInfo.get(mNfcCharging.BatteryLevel),
+                    WlcDeviceInfo.get(mNfcCharging.State));
+            for (INfcWlcStateListener listener : mWlcStateListener) {
+                try {
+                    listener.onWlcStateChanged(mWlcListenerDeviceInfo);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "error in onWlcData");
+                }
+            }
+        }
+    }
+
+    /** Notifies WLC procedure stopped */
+    @Override
+    public void onWlcStopped(int wpt_end_condition) {
+        Log.d(TAG, "onWlcStopped() - End condition is " + wpt_end_condition);
+        mNfcCharging.onWlcStopped(wpt_end_condition);
+    }
+
     final class ReaderModeParams {
         public int flags;
         public IAppCallback callback;
@@ -556,28 +623,6 @@
         }
     }
 
-    /**
-     * @hide constant copied from {@link Settings.Global}
-     * TODO(b/274636414): Migrate to official API in Android V.
-     */
-    private static final String SETTINGS_SATELLITE_MODE_RADIOS = "satellite_mode_radios";
-    /**
-     * @hide constant copied from {@link Settings.Global}
-     * TODO(b/274636414): Migrate to official API in Android V.
-     */
-    private static final String SETTINGS_SATELLITE_MODE_ENABLED = "satellite_mode_enabled";
-
-    private boolean isSatelliteModeSensitive() {
-        final String satelliteRadios =
-                Settings.Global.getString(mContentResolver, SETTINGS_SATELLITE_MODE_RADIOS);
-        return satelliteRadios == null || satelliteRadios.contains(Settings.Global.RADIO_NFC);
-    }
-
-    /** Returns true if satellite mode is turned on. */
-    private boolean isSatelliteModeOn() {
-        if (!isSatelliteModeSensitive()) return false;
-        return Settings.Global.getInt(mContentResolver, SETTINGS_SATELLITE_MODE_ENABLED, 0) == 1;
-    }
 
     /** Returns true if NFC has user restriction set. */
     private boolean isNfcUserRestricted() {
@@ -586,43 +631,48 @@
     }
 
     boolean shouldEnableNfc() {
-        return getNfcOnSetting() && !isSatelliteModeOn() && !isNfcUserRestricted();
+        return getNfcOnSetting() && !mNfcInjector.isSatelliteModeOn() && !isNfcUserRestricted();
     }
 
-    public NfcService(Application nfcApplication) {
+    private void registerGlobalBroadcastsReceiver() {
+        IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
+        filter.addAction(Intent.ACTION_SCREEN_ON);
+        filter.addAction(Intent.ACTION_USER_PRESENT);
+        filter.addAction(Intent.ACTION_USER_SWITCHED);
+        filter.addAction(Intent.ACTION_USER_ADDED);
+        if (mContext.getResources().getBoolean(R.bool.restart_on_sim_change)) {
+            filter.addAction(TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED);
+            filter.addAction(TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED);
+        }
+        mContext.registerReceiverForAllUsers(mReceiver, filter, null, null);
+    }
+
+    public NfcService(Application nfcApplication, NfcInjector nfcInjector) {
         mUserId = ActivityManager.getCurrentUser();
         mContext = nfcApplication;
+        mNfcInjector = nfcInjector;
+        mLooper = mNfcInjector.getMainLooper();
+        mHandler = new NfcServiceHandler(mLooper);
+        mNfcEventLog = mNfcInjector.getNfcEventLog();
 
         mNfcTagService = new TagService();
         mNfcAdapter = new NfcAdapterService();
-        mRoutingTableParser = new RoutingTableParser();
+        mRoutingTableParser = mNfcInjector.getRoutingTableParser();
         Log.i(TAG, "Starting NFC service");
 
         sService = this;
 
-        mScreenStateHelper = new ScreenStateHelper(mContext);
+        mScreenStateHelper = mNfcInjector.getScreenStateHelper();
         mContentResolver = mContext.getContentResolver();
-        mDeviceHost = new NativeNfcManager(mContext, this);
+        mDeviceHost = mNfcInjector.makeDeviceHost(this);
 
-        mNfcUnlockManager = NfcUnlockManager.getInstance();
+        mNfcUnlockManager = mNfcInjector.getNfcUnlockManager();
 
-        mHandoverDataParser = new HandoverDataParser();
-        boolean isNfcProvisioningEnabled = false;
-        try {
-            isNfcProvisioningEnabled = mContext.getResources().getBoolean(
-                    R.bool.enable_nfc_provisioning);
-        } catch (NotFoundException e) {
-        }
+        mHandoverDataParser = mNfcInjector.getHandoverDataParser();
+        mInProvisionMode = mNfcInjector.isInProvisionMode();
+        mDeviceConfigFacade = mNfcInjector.getDeviceConfigFacade();
 
-        if (isNfcProvisioningEnabled) {
-            mInProvisionMode = Settings.Global.getInt(mContentResolver,
-                    Settings.Global.DEVICE_PROVISIONED, 0) == 0;
-        } else {
-            mInProvisionMode = false;
-        }
-        mDeviceConfigFacade = new DeviceConfigFacade(mContext, mHandler);
-
-        mNfcDispatcher = new NfcDispatcher(mContext, mHandoverDataParser, mInProvisionMode);
+        mNfcDispatcher = mNfcInjector.getNfcDispatcher();
 
         mPrefs = mContext.getSharedPreferences(PREF, Context.MODE_PRIVATE);
         mPrefsEditor = mPrefs.edit();
@@ -645,11 +695,13 @@
         mUserManager = mContext.getSystemService(UserManager.class);
         mActivityManager = mContext.getSystemService(ActivityManager.class);
         mVibrator = mContext.getSystemService(Vibrator.class);
-        mVibrationEffect = VibrationEffect.createOneShot(200, VibrationEffect.DEFAULT_AMPLITUDE);
+        mVibrationEffect = mNfcInjector.getVibrationEffect();
 
         PackageManager pm = mContext.getPackageManager();
         mIsWatchType = pm.hasSystemFeature(PackageManager.FEATURE_WATCH);
 
+        mNfcDiagnostics = mNfcInjector.getNfcDiagnostics();
+
         if (pm.hasSystemFeature(PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE) &&
                 !mIsWatchType) {
             mVrManager = mContext.getSystemService(VrManager.class);
@@ -659,17 +711,13 @@
 
         mScreenState = mScreenStateHelper.checkScreenState();
 
-        mBackupManager = new BackupManager(mContext);
+        mBackupManager = mNfcInjector.getBackupManager();
 
-        mStatsdUtils = mFeatureFlags.statsdCeEventsFlag() ? new StatsdUtils() : null;
+        mFeatureFlags = mNfcInjector.getFeatureFlags();
+        mStatsdUtils = mNfcInjector.getStatsdUtils();
 
         // Intents for all users
-        IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
-        filter.addAction(Intent.ACTION_SCREEN_ON);
-        filter.addAction(Intent.ACTION_USER_PRESENT);
-        filter.addAction(Intent.ACTION_USER_SWITCHED);
-        filter.addAction(Intent.ACTION_USER_ADDED);
-        mContext.registerReceiverForAllUsers(mReceiver, filter, null, null);
+        registerGlobalBroadcastsReceiver();
 
         // Listen for work profile adds or removes.
         IntentFilter managedProfileFilter = new IntentFilter();
@@ -695,6 +743,16 @@
 
         updatePackageCache();
 
+        mIsRWCapable = pm.hasSystemFeature(PackageManager.FEATURE_NFC);
+        mIsWlcCapable = android.nfc.Flags.enableNfcCharging() &&
+                pm.hasSystemFeature(PackageManager.FEATURE_NFC_CHARGING);
+        if (mIsWlcCapable) {
+            mNfcCharging = new NfcCharging(mContext, mDeviceHost);
+            mIsWlcEnabled = mPrefs.getBoolean(PREF_NFC_CHARGING_ON, NFC_CHARGING_ON_DEFAULT);
+            // Register ThermalStatusChangedListener
+            addThermalStatusListener();
+        }
+
         mIsHceCapable =
                 pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION) ||
                 pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION_NFCF);
@@ -703,9 +761,8 @@
         if (mIsHceCapable) {
             mCardEmulationManager = new CardEmulationManager(mContext);
         }
-        mForegroundUtils = ForegroundUtils.getInstance(mActivityManager);
-
-        mIsSecureNfcCapable = mNfcAdapter.deviceSupportsNfcSecure();
+        mForegroundUtils = mNfcInjector.getForegroundUtils();
+        mIsSecureNfcCapable = mNfcInjector.checkIsSecureNfcCapable();
         mIsSecureNfcEnabled =
             mPrefs.getBoolean(PREF_SECURE_NFC_ON, SECURE_NFC_ON_DEFAULT) &&
             mIsSecureNfcCapable;
@@ -743,12 +800,7 @@
         mPollingDisableAllowed = mContext.getResources().getBoolean(R.bool.polling_disable_allowed);
 
         // Make sure this is only called when object construction is complete.
-        NfcServiceManager manager = NfcFrameworkInitializer.getNfcServiceManager();
-        if (manager == null) {
-            Log.e(TAG, "NfcServiceManager is null");
-            throw new UnsupportedOperationException();
-        }
-        manager.getNfcManagerServiceRegisterer().register(mNfcAdapter);
+        mNfcInjector.getNfcManagerRegisterer().register(mNfcAdapter);
 
         mIsAlwaysOnSupported =
             mContext.getResources().getBoolean(R.bool.nfcc_always_on_allowed);
@@ -756,7 +808,7 @@
         mIsTagAppPrefSupported =
             mContext.getResources().getBoolean(R.bool.tag_intent_app_pref_supported);
 
-        Uri uri = Settings.Global.getUriFor(SETTINGS_SATELLITE_MODE_ENABLED);
+        Uri uri = Settings.Global.getUriFor(Constants.SETTINGS_SATELLITE_MODE_ENABLED);
         if (uri == null) {
             Log.e(TAG, "satellite mode key does not exist in Settings");
         } else {
@@ -766,7 +818,7 @@
                     new ContentObserver(null) {
                         @Override
                         public void onChange(boolean selfChange) {
-                            if (isSatelliteModeSensitive()) {
+                            if (mNfcInjector.isSatelliteModeSensitive()) {
                                 Log.i(TAG, "Satellite mode change detected");
                                 if (shouldEnableNfc()) {
                                     new EnableDisableTask().execute(TASK_ENABLE);
@@ -920,13 +972,7 @@
 
     private void connectToSeService() {
         try {
-            SeServiceManager manager = SeFrameworkInitializer.getSeServiceManager();
-            if (manager == null) {
-                Log.e(TAG, "SEServiceManager is null");
-                return;
-            }
-            mSEService = ISecureElementService.Stub.asInterface(
-                    manager.getSeManagerServiceRegisterer().get());
+            mSEService = mNfcInjector.connectToSeService();
             if (mSEService != null) {
                 IBinder seServiceBinder = mSEService.asBinder();
                 seServiceBinder.linkToDeath(mSeServiceDeathRecipient, 0);
@@ -1069,6 +1115,7 @@
                     disableInternal();
                     break;
                 case TASK_BOOT:
+                    // Initialize the event log cache.
                     boolean initialized;
                     if (mPrefs.getBoolean(PREF_FIRST_BOOT, true)) {
                         Log.i(TAG, "First Boot");
@@ -1078,13 +1125,20 @@
                         setPaymentForegroundPreference(mUserId);
                     }
                     Log.d(TAG, "checking on firmware download");
-                    if (shouldEnableNfc()) {
+                    boolean enableNfc = shouldEnableNfc();
+                    if (enableNfc) {
                         Log.d(TAG, "NFC is on. Doing normal stuff");
                         initialized = enableInternal();
                     } else {
                         Log.d(TAG, "NFC is off.  Checking firmware version");
                         initialized = mDeviceHost.checkFirmware();
                     }
+                    mNfcEventLog.logEvent(
+                            NfcEventProto.EventType.newBuilder()
+                                    .setBootupState(NfcEventProto.NfcBootupState.newBuilder()
+                                            .setEnabled(enableNfc)
+                                            .build())
+                            .build());
                     if (initialized) {
                         // TODO(279846422) The system property will be temporary
                         // available for vendors that depend on it.
@@ -1165,6 +1219,8 @@
             nci_version = getNciVersion();
             Log.d(TAG, "NCI_Version: " + nci_version);
 
+            mPendingPowerStateUpdate = false;
+
             synchronized (NfcService.this) {
                 mObjectMap.clear();
 
@@ -1182,7 +1238,7 @@
             if(mNfcUnlockManager.isLockscreenPollingEnabled())
                 applyRouting(false);
 
-            mDeviceHost.doSetScreenState(screen_state_mask);
+            mDeviceHost.doSetScreenState(screen_state_mask, mIsWlcEnabled);
 
             sToast_debounce = false;
 
@@ -1196,13 +1252,8 @@
 
             if (mIsRecovering) {
                  // Intents for all users
-                 IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
-                 filter.addAction(Intent.ACTION_SCREEN_ON);
-                 filter.addAction(Intent.ACTION_USER_PRESENT);
-                 filter.addAction(Intent.ACTION_USER_SWITCHED);
-                 filter.addAction(Intent.ACTION_USER_ADDED);
-                 mContext.registerReceiverForAllUsers(mReceiver, filter, null, null);
-                 mIsRecovering = false;
+                registerGlobalBroadcastsReceiver();
+                mIsRecovering = false;
             }
 
             if(mIsPowerSavingModeEnabled) {
@@ -1233,6 +1284,14 @@
             WatchDogThread watchDog = new WatchDogThread("disableInternal", ROUTING_WATCHDOG_MS);
             watchDog.start();
 
+            if (mIsWlcEnabled) {
+                if (mNfcCharging.NfcChargingOnGoing == true) {
+                    mNfcCharging.disconnectNfcCharging();
+                    mNfcCharging.NfcChargingOnGoing = false;
+                }
+                mNfcCharging.resetInternalValues();
+            }
+
             if (mIsHceCapable) {
                 mCardEmulationManager.onNfcDisabled();
             }
@@ -1252,7 +1311,7 @@
                 mReaderModeParams = null;
                 mDiscoveryTechParams = null;
             }
-            mNfcDispatcher.setForegroundDispatch(null, null, null);
+            mNfcDispatcher.resetForegroundDispatch();
 
             boolean result;
             if (!mIsAlwaysOnSupported || mIsRecovering
@@ -1418,6 +1477,12 @@
                 Log.d(TAG, "Disabling reader mode because app died or moved to background");
                 mReaderModeParams = null;
                 StopPresenceChecking();
+                mNfcEventLog.logEvent(
+                        NfcEventProto.EventType.newBuilder()
+                                .setReaderModeChange(NfcEventProto.NfcReaderModeChange.newBuilder()
+                                        .setFlags(0)
+                                        .build())
+                                .build());
                 if (isNfcEnabled()) {
                     applyRouting(false);
                 }
@@ -1446,32 +1511,84 @@
         }
     }
 
+    public void enableNfc() {
+        saveNfcOnSetting(true);
+
+        if (shouldEnableNfc()) {
+            new EnableDisableTask().execute(TASK_ENABLE);
+        }
+    }
+
+    private @NonNull CharSequence getAppName(@NonNull String packageName, int uid) {
+        ApplicationInfo applicationInfo = null;
+        try {
+            applicationInfo = mContext.getPackageManager().getApplicationInfoAsUser(
+                    packageName, 0, UserHandle.getUserHandleForUid(uid));
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.e(TAG, "Failed to find app name for " + packageName);
+            return "";
+        }
+        return mContext.getPackageManager().getApplicationLabel(applicationInfo);
+    }
+
     public boolean isSecureNfcEnabled() {
         return mIsSecureNfcEnabled;
     }
 
     final class NfcAdapterService extends INfcAdapter.Stub {
+        private boolean isPrivileged(int callingUid) {
+            // Check for root uid to help invoking privileged APIs from rooted shell only.
+            return callingUid == Process.SYSTEM_UID || callingUid == Process.ROOT_UID;
+        }
+
         @Override
-        public boolean enable() throws RemoteException {
+        public boolean enable(String pkg) throws RemoteException {
             NfcPermissions.enforceAdminPermissions(mContext);
 
-            saveNfcOnSetting(true);
+            Log.i(TAG, "Enabling Nfc service. Package:" + pkg);
+            List<String> allowlist = new ArrayList<>(
+                    Arrays.asList(mContext.getResources().getStringArray(R.array.nfc_allow_list)));
+            if (!allowlist.isEmpty() && !allowlist.contains(pkg)) {
+                Intent allowUsingNfcIntent = new Intent()
+                        .putExtra(APP_NAME_ENABLING_NFC, getAppName(pkg, mUserId))
+                        .setClass(mContext, NfcEnableAllowlistActivity.class);
 
-            if (shouldEnableNfc()) {
-                new EnableDisableTask().execute(TASK_ENABLE);
+                mContext.startActivityAsUser(allowUsingNfcIntent, UserHandle.CURRENT);
+                return true;
             }
-
+            mNfcEventLog.logEvent(
+                    NfcEventProto.EventType.newBuilder()
+                            .setStateChange(NfcEventProto.NfcStateChange.newBuilder()
+                                    .setAppInfo(NfcEventProto.NfcAppInfo.newBuilder()
+                                            .setPackageName(pkg)
+                                            .setUid(Binder.getCallingUid())
+                                            .build())
+                                    .setEnabled(true)
+                                    .build())
+                            .build());
+            enableNfc();
             return true;
         }
 
         @Override
-        public boolean disable(boolean saveState) throws RemoteException {
+        public boolean disable(boolean saveState, String pkg) throws RemoteException {
             NfcPermissions.enforceAdminPermissions(mContext);
 
+            Log.i(TAG, "Disabling Nfc service. Package:" + pkg);
             if (saveState) {
                 saveNfcOnSetting(false);
             }
 
+            mNfcEventLog.logEvent(
+                    NfcEventProto.EventType.newBuilder()
+                            .setStateChange(NfcEventProto.NfcStateChange.newBuilder()
+                                    .setAppInfo(NfcEventProto.NfcAppInfo.newBuilder()
+                                            .setPackageName(pkg)
+                                            .setUid(Binder.getCallingUid())
+                                            .build())
+                                    .setEnabled(false)
+                                    .build())
+                            .build());
             new EnableDisableTask().execute(TASK_DISABLE);
 
             return true;
@@ -1491,43 +1608,56 @@
         }
 
         @Override
+        public boolean isObserveModeEnabled() {
+            NfcPermissions.enforceUserPermissions(mContext);
+            return mDeviceHost.isObserveModeEnabled();
+        }
+
+        @Override
         public boolean setObserveMode(boolean enable) {
-            long token = Binder.clearCallingIdentity();
-            try {
-                if (!android.nfc.Flags.nfcObserveMode()) {
-                    return false;
-                }
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-            boolean privilegedCaller = false;
             int callingUid = Binder.getCallingUid();
-            UserHandle user = Binder.getCallingUserHandle();
-            // Allow non-foreground callers with system uid or default payment service.
-            String packageName = getPackageNameFromUid(callingUid);
-            if (packageName != null) {
-                String defaultPaymentService = Settings.Secure.getString(
-                    mContext.createContextAsUser(user, 0).getContentResolver(),
-                Constants.SETTINGS_SECURE_NFC_PAYMENT_DEFAULT_COMPONENT);
-                if (defaultPaymentService != null) {
-                    String defaultPaymentPackage =
-                        ComponentName.unflattenFromString(defaultPaymentService).getPackageName();
-                    privilegedCaller = (callingUid == Process.SYSTEM_UID
-                            || packageName.equals(defaultPaymentPackage));
-                }
-            } else {
-                privilegedCaller = (callingUid == Process.SYSTEM_UID);
-            }
-            if (!privilegedCaller) {
+            int triggerSource =
+                    NFC_OBSERVE_MODE_STATE_CHANGED__TRIGGER_SOURCE__TRIGGER_SOURCE_UNKNOWN;
+            if (!isPrivileged(callingUid)) {
                 NfcPermissions.enforceUserPermissions(mContext);
-                if (!mForegroundUtils.isInForeground(Binder.getCallingUid())) {
-                    Log.e(TAG, "setObserveMode: Caller not in foreground.");
+                String packageName = getPackageNameFromUid(callingUid);
+                if (packageName == null) {
+                    Log.e(TAG, "no package name associated with non-privileged calling UID");
+                }
+                if (mCardEmulationManager.isPreferredServicePackageNameForUser(packageName,
+                        UserHandle.getUserHandleForUid(callingUid).getIdentifier())) {
+                    if (android.permission.flags.Flags.walletRoleEnabled()) {
+                        UserHandle user = Binder.getCallingUserHandle();
+                        triggerSource = packageName.equals(getWalletRoleHolder(user))
+                            ? NFC_OBSERVE_MODE_STATE_CHANGED__TRIGGER_SOURCE__WALLET_ROLE_HOLDER
+                            : NFC_OBSERVE_MODE_STATE_CHANGED__TRIGGER_SOURCE__FOREGROUND_APP;
+                    } else {
+                        if (mForegroundUtils.isInForeground(callingUid)) {
+                            triggerSource =
+                                NFC_OBSERVE_MODE_STATE_CHANGED__TRIGGER_SOURCE__FOREGROUND_APP;
+                        }
+                    }
+                } else {
+                    Log.e(TAG, "setObserveMode: Caller not preferred NFC service.");
                     return false;
                 }
             }
+
+            if (mStatsdUtils != null) {
+                mStatsdUtils.logObserveModeStateChanged(enable, triggerSource,
+                        0 /* TODO(b/334983405) measure latency */);
+            }
+
             return mDeviceHost.setObserveMode(enable);
         }
 
+        private String getWalletRoleHolder(UserHandle user) {
+            RoleManager roleManager = mContext.createContextAsUser(user, 0)
+                    .getSystemService(RoleManager.class);
+            List<String> roleHolders = roleManager.getRoleHolders(RoleManager.ROLE_WALLET);
+            return roleHolders.isEmpty() ? null : roleHolders.get(0);
+        }
+
         @Override
         public void pausePolling(int timeoutInMs) {
             NfcPermissions.enforceAdminPermissions(mContext);
@@ -1612,7 +1742,7 @@
             }
             // Short-cut the disable path
             if (intent == null && filters == null && techListsParcel == null) {
-                mNfcDispatcher.setForegroundDispatch(null, null, null);
+                mNfcDispatcher.resetForegroundDispatch();
                 return;
             }
 
@@ -1786,16 +1916,14 @@
         @Override
         public void setReaderMode(IBinder binder, IAppCallback callback, int flags, Bundle extras)
                 throws RemoteException {
-            boolean privilegedCaller = false;
             int callingUid = Binder.getCallingUid();
             int callingPid = Binder.getCallingPid();
+            boolean privilegedCaller = isPrivileged(callingUid)
+                    || NfcPermissions.checkAdminPermissions(mContext);
             // Allow non-foreground callers with system uid or systemui
             String packageName = getPackageNameFromUid(callingUid);
             if (packageName != null) {
-                privilegedCaller = (callingUid == Process.SYSTEM_UID
-                        || packageName.equals(SYSTEM_UI));
-            } else {
-                privilegedCaller = (callingUid == Process.SYSTEM_UID);
+                privilegedCaller |= packageName.equals(SYSTEM_UI);
             }
             Log.d(TAG, "setReaderMode: uid=" + callingUid + ", packageName: "
                     + packageName + ", flags: " + flags);
@@ -1863,6 +1991,16 @@
                         Log.e(TAG, "Reader mode Binder was never registered.");
                     }
                 }
+                mNfcEventLog.logEvent(
+                        NfcEventProto.EventType.newBuilder()
+                                .setReaderModeChange(NfcEventProto.NfcReaderModeChange.newBuilder()
+                                        .setAppInfo(NfcEventProto.NfcAppInfo.newBuilder()
+                                                .setPackageName(packageName)
+                                                .setUid(callingUid)
+                                                .build())
+                                        .setFlags(flags)
+                                        .build())
+                                .build());
                 if (isNfcEnabled()) {
                     applyRouting(false);
                 }
@@ -1907,13 +2045,7 @@
 
         @Override
         public boolean deviceSupportsNfcSecure() {
-            String skuList[] = mContext.getResources().getStringArray(
-                R.array.config_skuSupportsSecureNfc);
-            String sku = SystemProperties.get("ro.boot.hardware.sku");
-            if (TextUtils.isEmpty(sku) || !Utils.arrayContains(skuList, sku)) {
-                return false;
-            }
-            return true;
+            return mIsSecureNfcCapable;
         }
 
         @Override
@@ -1941,6 +2073,48 @@
                     availableNfcAntennas);
         }
 
+        @Override
+        public boolean setWlcEnabled(boolean enable) {
+            if (!mIsWlcCapable) {
+                return false;
+            }
+            NfcPermissions.enforceAdminPermissions(mContext);
+            // enable or disable WLC
+            if (DBG) Log.d(TAG, "setWlcEnabled: " + enable);
+            synchronized (NfcService.this) {
+                // check whether NFC is enabled
+                if (!isNfcEnabled()) {
+                    return false;
+                }
+                mPrefsEditor.putBoolean(PREF_NFC_CHARGING_ON, enable);
+                mPrefsEditor.apply();
+                mIsWlcEnabled = enable;
+                mBackupManager.dataChanged();
+            }
+            return true;
+        }
+
+        @Override
+        public boolean isWlcEnabled() throws RemoteException {
+            if (!mIsWlcCapable) {
+                return false;
+            }
+            // check whether WLC is enabled or disabled
+            synchronized (NfcService.this) {
+                return mIsWlcEnabled;
+            }
+        }
+
+        @Override
+        public WlcListenerDeviceInfo getWlcListenerDeviceInfo() {
+            if (!mIsWlcCapable) {
+                return null;
+            }
+            synchronized (NfcService.this) {
+                return mWlcListenerDeviceInfo;
+            }
+        }
+
         private int computeLockscreenPollMask(int[] techList) {
 
             Map<Integer, Integer> techCodeToMask = new HashMap<Integer, Integer>();
@@ -2111,6 +2285,97 @@
             return mIsReaderOptionEnabled;
         }
 
+        @Override
+        public void registerWlcStateListener(
+                INfcWlcStateListener listener) throws RemoteException {
+            if (!mIsWlcCapable) {
+                return;
+            }
+            NfcPermissions.enforceAdminPermissions(mContext);
+
+            mWlcStateListener.add(listener);
+        }
+
+        @Override
+        public void unregisterWlcStateListener(
+                INfcWlcStateListener listener) throws RemoteException {
+            if (!mIsWlcCapable) {
+                return;
+            }
+            NfcPermissions.enforceAdminPermissions(mContext);
+
+            mWlcStateListener.remove(listener);
+        }
+
+        @Override
+        public void notifyPollingLoop(PollingFrame frame) {
+            try {
+                byte[] data;
+                int type = frame.getType();
+                int gain = frame.getVendorSpecificGain();
+                byte[] frame_data = frame.getData();
+
+                long timestamp = frame.getTimestamp();
+                HexFormat format = HexFormat.ofDelimiter(" ");
+                String timestampBytes = format.formatHex(new byte[] {
+                        (byte) (timestamp >>> 24),
+                        (byte) (timestamp >>> 16),
+                        (byte) (timestamp >>> 8),
+                        (byte) timestamp });
+                int frame_data_length = frame_data == null ? 0 : frame_data.length;
+                String frame_data_str = frame_data_length == 0 ? "" : " " + format.formatHex(frame_data);
+                String type_str = "FF";
+                switch (type) {
+                    case PollingFrame.POLLING_LOOP_TYPE_ON:
+                        type_str = "00";
+                        data = new byte[] { 0x01 };
+                        break;
+                    case PollingFrame.POLLING_LOOP_TYPE_OFF:
+                        type_str = "00";
+                        data = new byte[] { 0x00 };
+                        break;
+                    case PollingFrame.POLLING_LOOP_TYPE_A:
+                        type_str = "01";
+                        break;
+                    case PollingFrame.POLLING_LOOP_TYPE_B:
+                        type_str = "02";
+                        break;
+                    case PollingFrame.POLLING_LOOP_TYPE_F:
+                        type_str = "03";
+                        break;
+                    case PollingFrame.POLLING_LOOP_TYPE_UNKNOWN:
+                        type_str = "07";
+                        break;
+                }
+                data = format.parseHex("6f 0C " + String.format("%02x", 9 + frame_data_length)
+                        + " 03 " + type_str
+                        + " 00 " + String.format("%02x", 5 + frame_data_length) + " "
+                        + timestampBytes + " " + String.format("%02x", gain) + frame_data_str);
+                ((NativeNfcManager) mDeviceHost).notifyPollingLoopFrame(data.length, data);
+            } catch (Exception ex) {
+                Log.e(TAG, "error when notifying polling loop", ex);
+            }
+        }
+
+        @Override
+        public void notifyHceDeactivated() {
+            try {
+                mCardEmulationManager.onHostCardEmulationDeactivated(1);
+            } catch (Exception ex) {
+                Log.e(TAG, "error when notifying HCE deactivated", ex);
+            }
+        }
+
+        @Override
+        public int handleShellCommand(@NonNull ParcelFileDescriptor in,
+                @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err,
+                @NonNull String[] args) {
+
+            NfcShellCommand shellCommand = new NfcShellCommand(NfcService.this, mContext);
+            return shellCommand.exec(this, in.getFileDescriptor(), out.getFileDescriptor(),
+                    err.getFileDescriptor(), args);
+        }
+
         private static boolean isPowerSavingModeCmd(int gid, int oid, byte[] payload) {
             return gid == NCI_GID_PROP && oid == NCI_MSG_PROP_ANDROID && payload.length > 0
                     && payload[0] == NCI_MSG_PROP_ANDROID_POWER_SAVING;
@@ -2134,7 +2399,8 @@
                        NfcVendorNciResponse response =
                                mDeviceHost.sendRawVendorCmd(mt, gid, oid, payload);
                        if (response.status == NCI_STATUS_OK) {
-                           sendVendorNciResponse(response.gid, response.oid, response.payload);
+                           mHandler.post(() -> mNfcAdapter.sendVendorNciResponse(
+                                             response.gid, response.oid, response.payload));
                        }
                        return Integer.valueOf(response.status);
                    }
@@ -2154,19 +2420,21 @@
         }
 
         @Override
-        public void registerVendorExtensionCallback(INfcVendorNciCallback callbacks)
+        public synchronized void registerVendorExtensionCallback(INfcVendorNciCallback callbacks)
                 throws RemoteException {
             if (DBG) Log.i(TAG, "Register the callback");
             NfcPermissions.enforceAdminPermissions(mContext);
             mNfcVendorNciCallBack = callbacks;
+            mDeviceHost.enableVendorNciNotifications(true);
         }
 
         @Override
-        public void unregisterVendorExtensionCallback(INfcVendorNciCallback callbacks)
+        public synchronized void unregisterVendorExtensionCallback(INfcVendorNciCallback callbacks)
                 throws RemoteException {
             if (DBG) Log.i(TAG, "Unregister the callback");
             NfcPermissions.enforceAdminPermissions(mContext);
             mNfcVendorNciCallBack = null;
+            mDeviceHost.enableVendorNciNotifications(false);
         }
 
         @Override
@@ -2193,7 +2461,7 @@
         }
 
         private synchronized void sendVendorNciResponse(int gid, int oid, byte[] payload) {
-            if (DBG) Log.i(TAG, "onVendorNciResponseReceived");
+            if (VDBG) Log.i(TAG, "onVendorNciResponseReceived");
             if (mNfcVendorNciCallBack != null) {
                 try {
                     mNfcVendorNciCallBack.onVendorResponseReceived(gid, oid, payload);
@@ -2204,7 +2472,7 @@
         }
 
         private synchronized void sendVendorNciNotification(int gid, int oid, byte[] payload) {
-            if (DBG) Log.i(TAG, "sendVendorNciNotification");
+            if (VDBG) Log.i(TAG, "sendVendorNciNotification");
             if (mNfcVendorNciCallBack != null) {
                 try {
                     mNfcVendorNciCallBack.onVendorNotificationReceived(gid, oid, payload);
@@ -2215,16 +2483,6 @@
         }
     }
 
-    private void sendVendorNciNotification(int gid, int oid, byte[] payload) {
-        if (DBG) Log.i(TAG, "sendVendorNciNotification");
-        if (mNfcVendorNciCallBack != null) {
-            try {
-                mNfcVendorNciCallBack.onVendorNotificationReceived(gid, oid, payload);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Failed to send vendor notification", e);
-            }
-        }
-    }
 
     final class SeServiceDeathRecipient implements IBinder.DeathRecipient {
         @Override
@@ -2794,10 +3052,48 @@
             new KeyguardLockedStateListener() {
         @Override
         public void onKeyguardLockedStateChanged(boolean isKeyguardLocked) {
-            applyScreenState(mScreenStateHelper.checkScreenState());
+            if (!mIsWlcCapable || !mNfcCharging.NfcChargingOnGoing) {
+                applyScreenState(mScreenStateHelper.checkScreenState());
+            }
         }
     };
 
+    private void addThermalStatusListener() {
+        try {
+            if (mPowerManager != null) {
+                mPowerManager.addThermalStatusListener(mContext.getMainExecutor(),
+                        mOnThermalStatusChangedListener);
+            }
+        } catch (Exception e) {
+            Log.e(TAG, "Exception in addThermalStatusListener " + e);
+        }
+    }
+
+    /**
+     * Receives Thermal state updates
+     */
+    private OnThermalStatusChangedListener mOnThermalStatusChangedListener =
+            new OnThermalStatusChangedListener() {
+        @Override
+        public void onThermalStatusChanged(int status) {
+            switch (status) {
+                case PowerManager.THERMAL_STATUS_MODERATE:
+                    Log.d(TAG, "Thermal status changed to MODERATE");
+                    break;
+                case PowerManager.THERMAL_STATUS_SEVERE:
+                    Log.d(TAG, "Thermal status changed to SEVERE");
+                    break;
+                case PowerManager.THERMAL_STATUS_CRITICAL:
+                    Log.d(TAG, "Thermal status changed to CRITICAL");
+                    break;
+                default:
+                    Log.d(TAG, "Unknown thermal status: " + status);
+                    break;
+            }
+        }
+    };
+
+
     /**
      * Read mScreenState and apply NFC-C polling and NFC-EE routing
      */
@@ -3042,6 +3338,21 @@
         mHandler.sendEmptyMessage(MSG_COMMIT_ROUTING);
     }
 
+    public boolean sendScreenMessageAfterNfcCharging() {
+        if (DBG) Log.d(TAG, "sendScreenMessageAfterNfcCharging() ");
+
+        if (mPendingPowerStateUpdate == true) {
+            int screenState = mScreenStateHelper.checkScreenState();
+            if (DBG) Log.d(TAG,
+                  "sendScreenMessageAfterNfcCharging - applying postponed screen state "
+                          + screenState);
+            NfcService.getInstance().sendMessage(NfcService.MSG_APPLY_SCREEN_STATE, screenState);
+            mPendingPowerStateUpdate = false;
+            return true;
+        }
+        return false;
+    }
+
     public boolean sendData(byte[] data) {
         return mDeviceHost.sendRawFrame(data);
     }
@@ -3086,6 +3397,10 @@
     }
 
     final class NfcServiceHandler extends Handler {
+        public NfcServiceHandler(Looper looper) {
+            super(looper);
+        }
+
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
@@ -3269,11 +3584,36 @@
 
                     mLastReadNdefMessage = ndefMsg;
 
-                    tag.startPresenceChecking(presenceCheckDelay, callback);
-                    dispatchTagEndpoint(tag, readerParams);
+                    if (mIsWlcEnabled) {
+                        if (DBG) Log.d(TAG, "Wlc enabled, check for WLC_CAP record");
+
+                        if (!mNfcCharging.NfcChargingMode
+                                && (mNfcCharging.checkWlcCapMsg(ndefMsg) == true)) {
+                            if (DBG) Log.d(TAG, "checkWlcCapMsg returned true");
+                            if (mNfcCharging.startNfcCharging(tag)) {
+                                mNfcCharging.NfcChargingMode = true;
+                                if (DBG) Log.d(TAG, "Nfc charging mode started successfully");
+                            } else {
+                                if (DBG) Log.d(TAG, "Nfc charging mode not detected");
+                            }
+                        } else if (mIsRWCapable) {
+                            tag.startPresenceChecking(presenceCheckDelay, callback);
+                            dispatchTagEndpoint(tag, readerParams);
+                        } else {
+                            tag.startPresenceChecking(presenceCheckDelay, callback);
+                        }
+                    } else if (mIsRWCapable) {
+                        tag.startPresenceChecking(presenceCheckDelay, callback);
+                        dispatchTagEndpoint(tag, readerParams);
+                    } else {
+                        tag.startPresenceChecking(presenceCheckDelay, callback);
+                    }
                     break;
 
                 case MSG_RF_FIELD_ACTIVATED:
+                    if (mCardEmulationManager != null) {
+                        mCardEmulationManager.onFieldChangeDetected(true);
+                    }
                     Intent fieldOnIntent = new Intent(ACTION_RF_FIELD_ON_DETECTED);
                     sendNfcPermissionProtectedBroadcast(fieldOnIntent);
                     if (mIsSecureNfcEnabled) {
@@ -3281,6 +3621,9 @@
                     }
                     break;
                 case MSG_RF_FIELD_DEACTIVATED:
+                    if (mCardEmulationManager != null) {
+                        mCardEmulationManager.onFieldChangeDetected(false);
+                    }
                     Intent fieldOffIntent = new Intent(ACTION_RF_FIELD_OFF_DETECTED);
                     sendNfcPermissionProtectedBroadcast(fieldOffIntent);
                     break;
@@ -3332,7 +3675,7 @@
                             applyRouting(false);
                         }
 
-                        mDeviceHost.doSetScreenState(screen_state_mask);
+                        mDeviceHost.doSetScreenState(screen_state_mask, mIsWlcEnabled);
                     } finally {
                         mRoutingWakeLock.release();
                     }
@@ -3724,7 +4067,7 @@
         }
     }
 
-    private NfcServiceHandler mHandler = new NfcServiceHandler();
+    private NfcServiceHandler mHandler;
 
     class ApplyRoutingTask extends AsyncTask<Integer, Void, Void> {
         @Override
@@ -3756,6 +4099,13 @@
                     || action.equals(Intent.ACTION_SCREEN_OFF)
                     || action.equals(Intent.ACTION_USER_PRESENT)) {
                 // Perform applyRouting() in AsyncTask to serialize blocking calls
+
+                if (mIsWlcCapable && mNfcCharging.NfcChargingOnGoing) {
+                    Log.d(TAG,
+                        "MSG_APPLY_SCREEN_STATE postponing due to a charging pier device");
+                    mPendingPowerStateUpdate = true;
+                    return;
+                }
                 if (action.equals(Intent.ACTION_SCREEN_ON)) {
                     synchronized (NfcService.this) {
                         mPollDelayCount = 0;
@@ -3787,6 +4137,20 @@
                             UserHandle.of(ActivityManager.getCurrentUser()), /*flags=*/0))
                             .startNotification();
                 }
+            } else if (action.equals(TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED) ||
+                    action.equals(TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED)) {
+                if (!mContext.getResources().getBoolean(R.bool.restart_on_sim_change)) return;
+                int state = intent.getIntExtra(TelephonyManager.EXTRA_SIM_STATE,
+                        TelephonyManager.SIM_STATE_UNKNOWN);
+                // Use |SIM_STATE_ABSENT| for detecting sim removal
+                // Use |SIM_STATE_LOADED| for detecting sim insertion and ready.
+                if (state == TelephonyManager.SIM_STATE_ABSENT
+                        || state == TelephonyManager.SIM_STATE_LOADED) {
+                    if (isNfcEnabled()) {
+                        Log.w(TAG, "Restarting NFC stack on SIM state change, SIM_STATE: " + state);
+                        restartStack();
+                    }
+                }
             }
         }
     };
@@ -3853,16 +4217,24 @@
                 screenState = ScreenStateHelper.SCREEN_STATE_ON_UNLOCKED;
             }
         }
+        if (DBG) Log.d(TAG, "applyScreenState(): screenState=" + screenState );
         if (mScreenState != screenState) {
             if (nci_version != NCI_VERSION_2_0) {
                 new ApplyRoutingTask().execute(Integer.valueOf(screenState));
             }
+            if (DBG) Log.d(TAG, "applyScreenState(): screenState != mScreenState=" + mScreenState );
             sendMessage(NfcService.MSG_APPLY_SCREEN_STATE, screenState);
         }
     }
 
     private void setPaymentForegroundPreference(int user) {
-        Context uc = mContext.createContextAsUser(UserHandle.of(user), 0);
+        Context uc;
+        try {
+            uc = mContext.createContextAsUser(UserHandle.of(user), 0);
+        } catch (IllegalStateException e) {
+            Log.d(TAG, "Fail to get user context for user: " + user);
+            return;
+        }
         try {
             // Check whether the Settings.Secure.NFC_PAYMENT_FOREGROUND exists or not.
             Settings.Secure.getInt(uc.getContentResolver(),
@@ -4015,6 +4387,9 @@
             pw.println("mIsSecureNfcEnabled=" + mIsSecureNfcEnabled);
             pw.println("mIsReaderOptionEnabled=" + mIsReaderOptionEnabled);
             pw.println("mIsAlwaysOnSupported=" + mIsAlwaysOnSupported);
+            if(mIsWlcCapable) {
+                pw.println("WlcEnabled=" + mIsWlcEnabled);
+            }
             pw.println("SnoopLogMode=" + NFC_SNOOP_LOG_MODE);
             pw.println("VendorDebugEnabled=" + NFC_VENDOR_DEBUG_ENABLED);
             pw.println("mIsPowerSavingModeEnabled=" + mIsPowerSavingModeEnabled);
@@ -4027,6 +4402,7 @@
                 mRoutingTableParser.dump(mDeviceHost, pw);
             }
             dumpTagAppPreference(pw);
+            mNfcInjector.getNfcEventLog().dump(fd, pw, args);
             copyNativeCrashLogsIfAny(pw);
             pw.flush();
             mDeviceHost.dump(fd);
diff --git a/src/com/android/nfc/NfcShellCommand.java b/src/com/android/nfc/NfcShellCommand.java
new file mode 100644
index 0000000..36ec00e
--- /dev/null
+++ b/src/com/android/nfc/NfcShellCommand.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.nfc;
+
+import android.content.Context;
+import android.os.Binder;
+import android.os.Process;
+import android.os.RemoteException;
+import android.text.TextUtils;
+
+import com.android.modules.utils.BasicShellCommandHandler;
+
+import java.io.PrintWriter;
+
+/**
+ * Interprets and executes 'adb shell cmd nfc [args]'.
+ *
+ * To add new commands:
+ * - onCommand: Add a case "<command>" execute. Return a 0
+ *   if command executed successfully.
+ * - onHelp: add a description string.
+ *
+ * Permissions: currently root permission is required for some commands. Others will
+ * enforce the corresponding API permissions.
+ */
+public class NfcShellCommand extends BasicShellCommandHandler {
+    private static final int DISABLE_POLLING_FLAGS = 0x1000;
+    private static final int ENABLE_POLLING_FLAGS = 0x0000;
+
+    // These don't require root access. However, these do perform permission checks in the
+    // corresponding binder methods in mNfcService.mNfcAdapter.
+    // Note: Any time you invoke a method from an internal class, consider making it privileged
+    // since these shell commands are available on production builds, we don't want apps to use
+    // this command to bypass security restrictions. mNfcService.mNfcAdapter binder
+    // methods already enforce permissions of the invoking shell (non-rooted shell has limited
+    // set of privileges).
+    private static final String[] NON_PRIVILEGED_COMMANDS = {
+            "help",
+            "disable-nfc",
+            "enable-nfc",
+            "status",
+    };
+    private final NfcService mNfcService;
+    private final Context mContext;
+
+    NfcShellCommand(NfcService nfcService, Context context) {
+        mNfcService = nfcService;
+        mContext = context;
+    }
+
+    @Override
+    public int onCommand(String cmd) {
+        // Treat no command as help command.
+        if (cmd == null || cmd.equals("")) {
+            cmd = "help";
+        }
+        // Explicit exclusion from root permission
+        if (ArrayUtils.indexOf(NON_PRIVILEGED_COMMANDS, cmd) == -1) {
+            final int uid = Binder.getCallingUid();
+            if (uid != Process.ROOT_UID) {
+                throw new SecurityException(
+                        "Uid " + uid + " does not have access to " + cmd + " nfc command "
+                                + "(or such command doesn't exist)");
+            }
+        }
+
+        final PrintWriter pw = getOutPrintWriter();
+        try {
+            switch (cmd) {
+                case "status":
+                    printStatus(pw);
+                    return 0;
+                case "disable-nfc":
+                    String stringSaveState = getNextArg();
+                    boolean saveState = false;
+                    if (TextUtils.equals(stringSaveState, "[persist]")) {
+                        saveState = true;
+                    }
+                    mNfcService.mNfcAdapter.disable(saveState, mContext.getPackageName());
+                    return 0;
+                case "enable-nfc":
+                    mNfcService.mNfcAdapter.enable(mContext.getPackageName());
+                    return 0;
+                case "set-reader-mode":
+                    boolean enable_polling =
+                            getNextArgRequiredTrueOrFalse("enable-polling", "disable-polling");
+                    int flags = enable_polling ? ENABLE_POLLING_FLAGS : DISABLE_POLLING_FLAGS;
+                    mNfcService.mNfcAdapter.setReaderMode(new Binder(), null, flags, null);
+                    return 0;
+                case "set-observe-mode":
+                    boolean enable = getNextArgRequiredTrueOrFalse("enable", "disable");
+                    mNfcService.mNfcAdapter.setObserveMode(enable);
+                    return 0;
+                default:
+                    return handleDefaultCommands(cmd);
+            }
+        } catch (IllegalArgumentException e) {
+            pw.println("Invalid args for " + cmd + ": ");
+            e.printStackTrace(pw);
+            return -1;
+        } catch (Exception e) {
+            pw.println("Exception while executing nfc shell command" + cmd + ": ");
+            e.printStackTrace(pw);
+            return -1;
+        }
+    }
+
+    private static boolean argTrueOrFalse(String arg, String trueString, String falseString) {
+        if (trueString.equals(arg)) {
+            return true;
+        } else if (falseString.equals(arg)) {
+            return false;
+        } else {
+            throw new IllegalArgumentException("Expected '" + trueString + "' or '" + falseString
+                    + "' as next arg but got '" + arg + "'");
+        }
+
+    }
+
+    private boolean getNextArgRequiredTrueOrFalse(String trueString, String falseString)
+            throws IllegalArgumentException {
+        String nextArg = getNextArgRequired();
+        return argTrueOrFalse(nextArg, trueString, falseString);
+    }
+
+    private void printStatus(PrintWriter pw) throws RemoteException {
+        pw.println("Nfc is " + (mNfcService.isNfcEnabled() ? "enabled" : "disabled"));
+    }
+
+    private void onHelpNonPrivileged(PrintWriter pw) {
+        pw.println("  status");
+        pw.println("    Gets status of UWB stack");
+        pw.println("  enable-nfc");
+        pw.println("    Toggle NFC on");
+        pw.println("  disable-nfc [persist]");
+        pw.println("    Toggle NFC off (optionally make it persistent)");
+    }
+
+    private void onHelpPrivileged(PrintWriter pw) {
+        pw.println("  set-observe-mode enable|disable");
+        pw.println("    Enable or disable observe mode.");
+        pw.println("  set-reader-mode enable-polling|disable-polling");
+        pw.println("    Enable or reader mode polling");
+    }
+
+    @Override
+    public void onHelp() {
+        final PrintWriter pw = getOutPrintWriter();
+        pw.println("NFC (Near-field communication) commands:");
+        pw.println("  help or -h");
+        pw.println("    Print this help text.");
+        onHelpNonPrivileged(pw);
+        if (Binder.getCallingUid() == Process.ROOT_UID) {
+            onHelpPrivileged(pw);
+        }
+        pw.println();
+    }
+}
diff --git a/src/com/android/nfc/RegisteredComponentCache.java b/src/com/android/nfc/RegisteredComponentCache.java
index 0d703c0..34c450b 100644
--- a/src/com/android/nfc/RegisteredComponentCache.java
+++ b/src/com/android/nfc/RegisteredComponentCache.java
@@ -37,7 +37,9 @@
 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
+import java.util.Objects;
 import java.util.concurrent.atomic.AtomicReference;
 
 /**
@@ -46,7 +48,8 @@
 public class RegisteredComponentCache {
     private static final String TAG = "RegisteredComponentCache";
     private static final boolean DEBUG =
-            NfcProperties.debug_enabled().orElse(false);
+            NfcProperties.debug_enabled().orElse(true);
+    private static final boolean VDBG = false; // turn on for local testing.
 
     final Context mContext;
     final String mAction;
@@ -54,7 +57,7 @@
     final AtomicReference<BroadcastReceiver> mReceiver;
 
     // synchronized on this
-    private ArrayList<ComponentInfo> mComponents;
+    private ArrayList<ComponentInfo> mComponents = new ArrayList<>();
 
     public RegisteredComponentCache(Context context, String action, String metaDataName) {
         mContext = context;
@@ -107,6 +110,21 @@
             }
             return out.toString();
         }
+
+        @Override
+        public boolean equals(Object other) {
+            if (other instanceof ComponentInfo) {
+                ComponentInfo oCI = (ComponentInfo) other;
+                return Objects.equals(resolveInfo.activityInfo, oCI.resolveInfo.activityInfo)
+                        && Arrays.equals(techs, oCI.techs);
+            }
+            return false;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(Arrays.hashCode(techs), resolveInfo.activityInfo);
+        }
     }
 
     /**
@@ -170,8 +188,19 @@
             }
         }
 
-        if (DEBUG) {
+        if (VDBG) {
+            Log.i(TAG, "Components => ");
             dump(components);
+        } else {
+            // dump only new components added or removed
+            ArrayList<ComponentInfo> newComponents = new ArrayList<>(components);
+            newComponents.removeAll(mComponents);
+            ArrayList<ComponentInfo> removedComponents = new ArrayList<>(mComponents);
+            removedComponents.removeAll(components);
+            Log.i(TAG, "New Components => ");
+            dump(newComponents);
+            Log.i(TAG, "Removed Components => ");
+            dump(removedComponents);
         }
 
         synchronized (this) {
diff --git a/src/com/android/nfc/RoutingTableParser.java b/src/com/android/nfc/RoutingTableParser.java
index 775e65f..56ee5dc 100644
--- a/src/com/android/nfc/RoutingTableParser.java
+++ b/src/com/android/nfc/RoutingTableParser.java
@@ -30,7 +30,7 @@
 * Parse the Routing Table from the last backup lmrt cmd and dump it with a clear typography
 */
 public class RoutingTableParser {
-    static final boolean DBG = NfcProperties.debug_enabled().orElse(false);
+    static final boolean DBG = NfcProperties.debug_enabled().orElse(true);
     private static final String TAG = "RoutingTableParser";
     private static int sRoutingTableSize = 0;
     private static int sRoutingTableMaxSize = 0;
diff --git a/src/com/android/nfc/cardemulation/AidRoutingManager.java b/src/com/android/nfc/cardemulation/AidRoutingManager.java
index 11babac..fc9fc34 100644
--- a/src/com/android/nfc/cardemulation/AidRoutingManager.java
+++ b/src/com/android/nfc/cardemulation/AidRoutingManager.java
@@ -38,7 +38,7 @@
 
     static final String TAG = "AidRoutingManager";
 
-    static final boolean DBG = NfcProperties.debug_enabled().orElse(false);
+    static final boolean DBG = NfcProperties.debug_enabled().orElse(true);
 
     static final int ROUTE_HOST = 0x00;
 
diff --git a/src/com/android/nfc/cardemulation/CardEmulationManager.java b/src/com/android/nfc/cardemulation/CardEmulationManager.java
index 4d0919d..506e779 100644
--- a/src/com/android/nfc/cardemulation/CardEmulationManager.java
+++ b/src/com/android/nfc/cardemulation/CardEmulationManager.java
@@ -15,7 +15,10 @@
  */
 package com.android.nfc.cardemulation;
 
+import android.annotation.TargetApi;
+import android.annotation.FlaggedApi;
 import android.app.ActivityManager;
+import android.app.role.RoleManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
@@ -25,11 +28,11 @@
 import android.nfc.INfcCardEmulation;
 import android.nfc.INfcFCardEmulation;
 import android.nfc.NfcAdapter;
-import android.nfc.NfcManager;
 import android.nfc.cardemulation.AidGroup;
 import android.nfc.cardemulation.ApduServiceInfo;
 import android.nfc.cardemulation.CardEmulation;
 import android.nfc.cardemulation.NfcFServiceInfo;
+import android.nfc.cardemulation.PollingFrame;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Looper;
@@ -44,6 +47,7 @@
 import android.util.Log;
 import android.util.proto.ProtoOutputStream;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.nfc.ForegroundUtils;
 import com.android.nfc.NfcPermissions;
 import com.android.nfc.NfcService;
@@ -54,6 +58,7 @@
 import java.util.List;
 
 import com.android.nfc.R;
+import android.permission.flags.Flags;
 
 /**
  * CardEmulationManager is the central entity
@@ -72,9 +77,9 @@
  */
 public class CardEmulationManager implements RegisteredServicesCache.Callback,
         RegisteredNfcFServicesCache.Callback, PreferredServices.Callback,
-        EnabledNfcFServices.Callback {
+        EnabledNfcFServices.Callback, WalletRoleObserver.Callback {
     static final String TAG = "CardEmulationManager";
-    static final boolean DBG = NfcProperties.debug_enabled().orElse(false);
+    static final boolean DBG = NfcProperties.debug_enabled().orElse(true);
 
     static final int NFC_HCE_APDU = 0x01;
     static final int NFC_HCE_NFCF = 0x04;
@@ -100,14 +105,16 @@
     final HostEmulationManager mHostEmulationManager;
     final HostNfcFEmulationManager mHostNfcFEmulationManager;
     final PreferredServices mPreferredServices;
+
+    final WalletRoleObserver mWalletRoleObserver;
     final EnabledNfcFServices mEnabledNfcFServices;
     final Context mContext;
     final CardEmulationInterface mCardEmulationInterface;
     final NfcFCardEmulationInterface mNfcFCardEmulationInterface;
     final PowerManager mPowerManager;
     boolean mNotSkipAid;
-	
-	final ForegroundUtils mForegroundUtils;
+
+    final ForegroundUtils mForegroundUtils;
     private int mForegroundUid;
 
     RoutingOptionManager mRoutingOptionManager;
@@ -118,25 +125,71 @@
         mContext = context;
         mCardEmulationInterface = new CardEmulationInterface();
         mNfcFCardEmulationInterface = new NfcFCardEmulationInterface();
-        mForegroundUtils = ForegroundUtils.getInstance(context.getSystemService(ActivityManager.class));
-        mAidCache = new RegisteredAidCache(context);
+        mForegroundUtils = ForegroundUtils.getInstance(
+            context.getSystemService(ActivityManager.class));
+        mWalletRoleObserver = new WalletRoleObserver(context,
+                context.getSystemService(RoleManager.class), this);
+        mAidCache = new RegisteredAidCache(context, mWalletRoleObserver);
         mT3tIdentifiersCache = new RegisteredT3tIdentifiersCache(context);
         mHostEmulationManager =
                 new HostEmulationManager(context, Looper.getMainLooper(), mAidCache);
         mHostNfcFEmulationManager = new HostNfcFEmulationManager(context, mT3tIdentifiersCache);
         mServiceCache = new RegisteredServicesCache(context, this);
         mNfcFServicesCache = new RegisteredNfcFServicesCache(context, this);
-        mPreferredServices = new PreferredServices(context, mServiceCache, mAidCache, this);
+        mPreferredServices = new PreferredServices(context, mServiceCache, mAidCache,
+                mWalletRoleObserver, this);
         mEnabledNfcFServices = new EnabledNfcFServices(
                 context, mNfcFServicesCache, mT3tIdentifiersCache, this);
-        mServiceCache.initialize();
-        mNfcFServicesCache.initialize();
         mPowerManager = context.getSystemService(PowerManager.class);
         mRoutingOptionManager = RoutingOptionManager.getInstance();
         mOffHostRouteEse = mRoutingOptionManager.getOffHostRouteEse();
         mOffHostRouteUicc = mRoutingOptionManager.getOffHostRouteUicc();
+        initialize();
+    }
 
+    @VisibleForTesting
+    CardEmulationManager(Context context,
+            ForegroundUtils foregroundUtils,
+            WalletRoleObserver walletRoleObserver,
+            RegisteredAidCache registeredAidCache,
+            RegisteredT3tIdentifiersCache registeredT3tIdentifiersCache,
+            HostEmulationManager hostEmulationManager,
+            HostNfcFEmulationManager hostNfcFEmulationManager,
+            RegisteredServicesCache registeredServicesCache,
+            RegisteredNfcFServicesCache registeredNfcFServicesCache,
+            PreferredServices preferredServices,
+            EnabledNfcFServices enabledNfcFServices,
+            RoutingOptionManager routingOptionManager,
+            PowerManager powerManager) {
+        mContext = context;
+        mCardEmulationInterface = new CardEmulationInterface();
+        mNfcFCardEmulationInterface = new NfcFCardEmulationInterface();
+        mForegroundUtils = foregroundUtils;
+        mWalletRoleObserver = walletRoleObserver;
+        mAidCache = registeredAidCache;
+        mT3tIdentifiersCache = registeredT3tIdentifiersCache;
+        mHostEmulationManager = hostEmulationManager;
+        mHostNfcFEmulationManager = hostNfcFEmulationManager;
+        mServiceCache = registeredServicesCache;
+        mNfcFServicesCache = registeredNfcFServicesCache;
+        mPreferredServices = preferredServices;
+        mEnabledNfcFServices = enabledNfcFServices;
+        mPowerManager = powerManager;
+        mRoutingOptionManager = routingOptionManager;
+        mOffHostRouteEse = mRoutingOptionManager.getOffHostRouteEse();
+        mOffHostRouteUicc = mRoutingOptionManager.getOffHostRouteUicc();
+        initialize();
+    }
+
+    private void initialize() {
+        mServiceCache.initialize();
+        mNfcFServicesCache.initialize();
         mForegroundUid = Process.INVALID_UID;
+        if (mWalletRoleObserver.isWalletRoleFeatureEnabled()) {
+            int currentUser = ActivityManager.getCurrentUser();
+            onWalletRoleHolderChanged(
+                    mWalletRoleObserver.getDefaultWalletRoleHolder(currentUser), currentUser);
+        }
     }
 
     public INfcCardEmulation getNfcCardEmulationInterface() {
@@ -147,8 +200,13 @@
         return mNfcFCardEmulationInterface;
     }
 
-    public void onPollingLoopDetected(Bundle pollingFrame) {
-        mHostEmulationManager.onPollingLoopDetected(pollingFrame);
+    @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+    public void onPollingLoopDetected(List<PollingFrame> pollingFrames) {
+        mHostEmulationManager.onPollingLoopDetected(pollingFrames);
+    }
+
+    public void onFieldChangeDetected(boolean fieldOn) {
+        mHostEmulationManager.onFieldChangeDetected(fieldOn);
     }
 
     public void onHostCardEmulationActivated(int technology) {
@@ -200,6 +258,7 @@
     }
 
     public void onUserSwitched(int userId) {
+        mWalletRoleObserver.onUserSwitched(userId);
         // for HCE
         mServiceCache.onUserSwitched();
         mPreferredServices.onUserSwitched(userId);
@@ -296,13 +355,17 @@
     @Override
     public void onServicesUpdated(int userId, List<ApduServiceInfo> services,
             boolean validateInstalled) {
-        // Verify defaults are still the same
-        verifyDefaults(userId, services, validateInstalled);
+        if (!mWalletRoleObserver.isWalletRoleFeatureEnabled()) {
+            // Verify defaults are still the same
+            verifyDefaults(userId, services, validateInstalled);
+        }
         // Update the AID cache
         mAidCache.onServicesUpdated(userId, services);
         // Update the preferred services list
         mPreferredServices.onServicesUpdated();
-
+        if (android.nfc.Flags.nfcReadPollingLoop()) {
+            mHostEmulationManager.updatePollingLoopFilters(userId, services);
+        }
         NfcService.getInstance().onPreferredPaymentChanged(NfcAdapter.PREFERRED_PAYMENT_UPDATED);
     }
 
@@ -524,6 +587,10 @@
             if (!isServiceRegistered(userId, service)) {
                 return false;
             }
+            if (mWalletRoleObserver.isWalletRoleFeatureEnabled()) {
+                return service.getPackageName()
+                        .equals(mWalletRoleObserver.getDefaultWalletRoleHolder(userId));
+            }
             ComponentName defaultService =
                     getDefaultServiceForCategory(userId, category, true);
             return (defaultService != null && defaultService.equals(service));
@@ -563,13 +630,14 @@
         }
 
         @Override
-        public boolean setServiceObserveModeDefault(int userId,
+        public boolean setShouldDefaultToObserveModeForService(int userId,
             ComponentName service, boolean enable) {
             NfcPermissions.validateUserId(userId);
+            NfcPermissions.enforceUserPermissions(mContext);
             if (!isServiceRegistered(userId, service)) {
                 return false;
             }
-            return mServiceCache.setServiceObserveModeDefault(userId, Binder.getCallingUid(),
+            return mServiceCache.setShouldDefaultToObserveModeForService(userId, Binder.getCallingUid(),
                 service, enable);
         }
 
@@ -579,6 +647,7 @@
             NfcPermissions.validateUserId(userId);
             NfcPermissions.enforceUserPermissions(mContext);
             if (!isServiceRegistered(userId, service)) {
+                Log.e(TAG, "service ("+ service + ") isn't registered for user " + userId);
                 return false;
             }
             if (!mServiceCache.registerAidGroupForService(userId, Binder.getCallingUid(), service,
@@ -591,6 +660,62 @@
         }
 
         @Override
+        @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+        public boolean registerPollingLoopFilterForService(int userId, ComponentName service,
+                String pollingLoopFilter, boolean autoTransact) throws RemoteException {
+            NfcPermissions.validateUserId(userId);
+            NfcPermissions.enforceUserPermissions(mContext);
+            if (!isServiceRegistered(userId, service)) {
+                Log.e(TAG, "service ("+ service + ") isn't registered for user " + userId);
+                return false;
+            }
+            return mServiceCache.registerPollingLoopFilterForService(userId, Binder.getCallingUid(),
+                    service, pollingLoopFilter, autoTransact);
+        }
+
+        @Override
+        @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+        public boolean removePollingLoopFilterForService(int userId, ComponentName service,
+                String pollingLoopFilter) throws RemoteException {
+            NfcPermissions.validateUserId(userId);
+            NfcPermissions.enforceUserPermissions(mContext);
+            if (!isServiceRegistered(userId, service)) {
+                Log.e(TAG, "service ("+ service + ") isn't registered for user " + userId);
+                return false;
+            }
+            return mServiceCache.removePollingLoopFilterForService(userId, Binder.getCallingUid(),
+                    service, pollingLoopFilter);
+        }
+
+        @Override
+        @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+        public boolean registerPollingLoopPatternFilterForService(int userId, ComponentName service,
+                String pollingLoopPatternFilter, boolean autoTransact) throws RemoteException {
+            NfcPermissions.validateUserId(userId);
+            NfcPermissions.enforceUserPermissions(mContext);
+            if (!isServiceRegistered(userId, service)) {
+                Log.e(TAG, "service ("+ service + ") isn't registed for user " + userId);
+                return false;
+            }
+            return mServiceCache.registerPollingLoopPatternFilterForService(userId,
+                    Binder.getCallingUid(), service, pollingLoopPatternFilter, autoTransact);
+        }
+
+        @Override
+        @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+        public boolean removePollingLoopPatternFilterForService(int userId, ComponentName service,
+                String pollingLoopPatternFilter) throws RemoteException {
+            NfcPermissions.validateUserId(userId);
+            NfcPermissions.enforceUserPermissions(mContext);
+            if (!isServiceRegistered(userId, service)) {
+                Log.e(TAG, "service ("+ service + ") isn't registed for user " + userId);
+                return false;
+            }
+            return mServiceCache.removePollingLoopPatternFilterForService(userId,
+                    Binder.getCallingUid(), service, pollingLoopPatternFilter);
+        }
+
+        @Override
         public boolean setOffHostForService(int userId, ComponentName service, String offHostSE) {
             NfcPermissions.validateUserId(userId);
             NfcPermissions.enforceUserPermissions(mContext);
@@ -703,6 +828,11 @@
 
         @Override
         public boolean isDefaultPaymentRegistered() throws RemoteException {
+            if (mWalletRoleObserver.isWalletRoleFeatureEnabled()) {
+                int callingUserId = Binder.getCallingUserHandle().getIdentifier();
+                return mWalletRoleObserver
+                        .getDefaultWalletRoleHolder(callingUserId) != null;
+            }
             String defaultComponent = Settings.Secure.getString(mContext.getContentResolver(),
                     Constants.SETTINGS_SECURE_NFC_PAYMENT_DEFAULT_COMPONENT);
             return defaultComponent != null ? true : false;
@@ -714,7 +844,8 @@
                     ", technology " + technology);
 
             int callingUid = Binder.getCallingUid();
-            if (!mForegroundUtils.registerUidToBackgroundCallback(mForegroundCallback, callingUid)) {
+            if (!mForegroundUtils
+                    .registerUidToBackgroundCallback(mForegroundCallback, callingUid)) {
                 Log.e(TAG, "overrideRoutingTable: Caller is not in foreground.");
                 return false;
             }
@@ -722,7 +853,8 @@
 
             int protocolRoute = getRouteForSecureElement(protocol);
             int technologyRoute = getRouteForSecureElement(technology);
-            if (DBG) Log.d(TAG, "protocolRoute " + protocolRoute + ", technologyRoute " + technologyRoute);
+            if (DBG) Log.d(TAG, "protocolRoute " + protocolRoute +
+                ", technologyRoute " + technologyRoute);
 
 //            mRoutingOptionManager.overrideDefaultRoute(protocolRoute);
             mRoutingOptionManager.overrideDefaultIsoDepRoute(protocolRoute);
@@ -902,39 +1034,52 @@
 
     @Override
     public void onPreferredPaymentServiceChanged(int userId, ComponentName service) {
+        Log.i(TAG, "onPreferredPaymentServiceChanged");
         mAidCache.onPreferredPaymentServiceChanged(userId, service);
         mHostEmulationManager.onPreferredPaymentServiceChanged(userId, service);
 
         NfcService.getInstance().onPreferredPaymentChanged(
                 NfcAdapter.PREFERRED_PAYMENT_CHANGED);
+        updateForShouldDefaultToObserveMode(userId);
     }
 
     @Override
     public void onPreferredForegroundServiceChanged(int userId, ComponentName service) {
+        Log.i(TAG, "onPreferredForegroundServiceChanged");
         mAidCache.onPreferredForegroundServiceChanged(userId, service);
         mHostEmulationManager.onPreferredForegroundServiceChanged(userId, service);
 
         NfcService.getInstance().onPreferredPaymentChanged(
                 NfcAdapter.PREFERRED_PAYMENT_CHANGED);
+        updateForShouldDefaultToObserveMode(userId);
+    }
+
+    private void updateForShouldDefaultToObserveMode(int userId) {
         long token = Binder.clearCallingIdentity();
         try {
             if (!android.nfc.Flags.nfcObserveMode()) {
+                Log.d(TAG, "observe mode isn't enabled");
                 return;
             }
+
+            NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
+            if (adapter == null) {
+                Log.e(TAG, "adapter is null, returning");
+                return;
+            }
+            ComponentName preferredService = mAidCache.getPreferredService();
+            boolean enableObserveMode = mServiceCache.doesServiceShouldDefaultToObserveMode(userId,
+                    preferredService);
+            adapter.setObserveModeEnabled(enableObserveMode);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
+    }
 
-        ComponentName paymentService = getDefaultServiceForCategory(userId,
-                    CardEmulation.CATEGORY_PAYMENT, false);
-        NfcManager manager = mContext.getSystemService(NfcManager.class);
-        NfcAdapter adapter = manager.getDefaultAdapter();
-        if (mServiceCache.doesServiceDefaultToObserveMode(userId,
-            service != null ? service : paymentService)) {
-            adapter.disallowTransaction();
-        } else {
-            adapter.allowTransaction();
-        }
+    @Override
+    public void onWalletRoleHolderChanged(String holder, int userId) {
+        mPreferredServices.onWalletRoleHolderChanged(holder, userId);
+        mAidCache.onWalletRoleHolderChanged(holder, userId);
     }
 
     @Override
@@ -946,7 +1091,7 @@
     public String getRegisteredAidCategory(String aid) {
         RegisteredAidCache.AidResolveInfo resolvedInfo = mAidCache.resolveAid(aid);
         if (resolvedInfo != null) {
-            return resolvedInfo.category;
+            return resolvedInfo.getCategory();
         }
         return "";
     }
@@ -954,4 +1099,8 @@
     public boolean isRequiresScreenOnServiceExist() {
         return mAidCache.isRequiresScreenOnServiceExist();
     }
+
+    public boolean isPreferredServicePackageNameForUser(String packageName, int userId) {
+        return mAidCache.isPreferredServicePackageNameForUser(packageName, userId);
+    }
 }
diff --git a/src/com/android/nfc/cardemulation/EnabledNfcFServices.java b/src/com/android/nfc/cardemulation/EnabledNfcFServices.java
index d7607d3..5857967 100644
--- a/src/com/android/nfc/cardemulation/EnabledNfcFServices.java
+++ b/src/com/android/nfc/cardemulation/EnabledNfcFServices.java
@@ -34,7 +34,7 @@
 
 public class EnabledNfcFServices implements com.android.nfc.ForegroundUtils.Callback {
     static final String TAG = "EnabledNfcFCardEmulationServices";
-    static final boolean DBG = NfcProperties.debug_enabled().orElse(false);
+    static final boolean DBG = NfcProperties.debug_enabled().orElse(true);
 
     final Context mContext;
     final RegisteredNfcFServicesCache mNfcFServiceCache;
diff --git a/src/com/android/nfc/cardemulation/HostEmulationManager.java b/src/com/android/nfc/cardemulation/HostEmulationManager.java
index eb55ddb..1c31bde 100644
--- a/src/com/android/nfc/cardemulation/HostEmulationManager.java
+++ b/src/com/android/nfc/cardemulation/HostEmulationManager.java
@@ -16,6 +16,9 @@
 
 package com.android.nfc.cardemulation;
 
+import android.annotation.TargetApi;
+import android.annotation.FlaggedApi;
+import android.app.ActivityManager;
 import android.app.KeyguardManager;
 import android.content.ComponentName;
 import android.content.Context;
@@ -24,9 +27,11 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.nfc.NfcAdapter;
 import android.nfc.cardemulation.ApduServiceInfo;
 import android.nfc.cardemulation.CardEmulation;
 import android.nfc.cardemulation.HostApduService;
+import android.nfc.cardemulation.PollingFrame;
 import android.nfc.cardemulation.Utils;
 import android.os.Bundle;
 import android.os.Handler;
@@ -43,20 +48,30 @@
 
 import androidx.annotation.VisibleForTesting;
 
+import com.android.nfc.NfcInjector;
 import com.android.nfc.NfcService;
 import com.android.nfc.NfcStatsLog;
 import com.android.nfc.cardemulation.RegisteredAidCache.AidResolveInfo;
-import com.android.nfc.cardemulation.RegisteredServicesCache.DynamicSettings;
 import com.android.nfc.cardemulation.util.StatsdUtils;
 import com.android.nfc.flags.Flags;
+import com.android.nfc.proto.NfcEventProto;
+
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HexFormat;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
 import java.util.Objects;
+import java.util.Set;
+import java.util.regex.Pattern;
+
 
 public class HostEmulationManager {
     static final String TAG = "HostEmulationManager";
-    static final boolean DBG = NfcProperties.debug_enabled().orElse(false);
+    static final boolean DBG = NfcProperties.debug_enabled().orElse(true);
 
     static final int STATE_IDLE = 0;
     static final int STATE_W4_SELECT = 1;
@@ -74,6 +89,8 @@
     static final byte INSTR_SELECT = (byte)0xA4;
 
     static final String ANDROID_HCE_AID = "A000000476416E64726F6964484345";
+    static final String NDEF_V1_AID = "D2760000850100";
+    static final String NDEF_V2_AID = "D2760000850101";
     static final byte[] ANDROID_HCE_RESPONSE = {0x14, (byte)0x81, 0x00, 0x00, (byte)0x90, 0x00};
 
     static final byte[] AID_NOT_FOUND = {0x6A, (byte)0x82};
@@ -83,6 +100,8 @@
             NfcStatsLog.NFC_CARDEMULATION_OCCURRED__CATEGORY__HCE_PAYMENT;
     static final int CE_HCE_OTHER =
             NfcStatsLog.NFC_CARDEMULATION_OCCURRED__CATEGORY__HCE_OTHER;
+    static final String NFC_PACKAGE = "com.android.nfc";
+    static final String DATA_KEY = "data";
 
     final Context mContext;
     final RegisteredAidCache mAidCache;
@@ -102,13 +121,18 @@
     boolean mServiceBound = false;
     ComponentName mServiceName = null;
     int mServiceUserId; // The UserId of the non-payment service
-    ArrayList<Bundle> mPendingPollingLoopFrames = null;
+    ArrayList<PollingFrame> mPendingPollingLoopFrames = null;
+    private Map<Integer, Map<String, List<ApduServiceInfo>>> mPollingLoopFilters;
+    private Map<Integer, Map<Pattern, List<ApduServiceInfo>>> mPollingLoopPatternFilters;
 
     // Variables below are for a payment service,
     // which is typically bound persistently to improve on
     // latency.
     Messenger mPaymentService;
     boolean mPaymentServiceBound = false;
+
+    boolean mEnableObserveModeAfterTransaction = false;
+    boolean mEnableObserveModeOnFieldOff = false;
     ComponentName mPaymentServiceName = null;
     int mPaymentServiceUserId; // The userId of the payment service
     ComponentName mLastBoundPaymentServiceName;
@@ -124,23 +148,33 @@
     String mLastSelectedAid;
     int mState;
     byte[] mSelectApdu;
+    Handler mHandler;
 
     public HostEmulationManager(Context context, Looper looper, RegisteredAidCache aidCache) {
+        this(context, looper, aidCache, new StatsdUtils(StatsdUtils.SE_NAME_HCE));
+    }
+
+    @VisibleForTesting
+    HostEmulationManager(Context context, Looper looper, RegisteredAidCache aidCache,
+                         StatsdUtils statsdUtils) {
         mContext = context;
         mLooper = looper;
+        mHandler = new Handler(looper);
         mLock = new Object();
         mAidCache = aidCache;
         mState = STATE_IDLE;
         mKeyguard = context.getSystemService(KeyguardManager.class);
         mPowerManager = context.getSystemService(PowerManager.class);
-        mStatsdUtils = Flags.statsdCeEventsFlag() ? new StatsdUtils(StatsdUtils.SE_NAME_HCE) : null;
+        mStatsdUtils = Flags.statsdCeEventsFlag() ? statsdUtils : null;
+        mPollingLoopFilters = new HashMap<Integer, Map<String, List<ApduServiceInfo>>>();
+        mPollingLoopPatternFilters = new HashMap<Integer, Map<Pattern, List<ApduServiceInfo>>>();
     }
 
     /**
      *  Preferred payment service changed
      */
     public void onPreferredPaymentServiceChanged(int userId, final ComponentName service) {
-        new Handler(mLooper).post(() -> {
+        mHandler.post(() -> {
             synchronized (mLock) {
                 if (service != null) {
                     bindPaymentServiceLocked(userId, service);
@@ -154,37 +188,153 @@
     private Messenger getForegroundServiceOrDefault() {
         PackageManager packageManager = mContext.getPackageManager();
         ComponentName preferredServiceName = mAidCache.getPreferredService();
-        try {
-            ApplicationInfo preferredServiceInfo =
-                packageManager.getApplicationInfo(preferredServiceName.getPackageName(), 0);
-            UserHandle user = UserHandle.getUserHandleForUid(preferredServiceInfo.uid);
-            return bindServiceIfNeededLocked(user.getIdentifier(), preferredServiceName);
-        } catch (NameNotFoundException nnfe) {
-            Log.e(TAG, "Packange name not found, dropping polling frame", nnfe);
-            unbindServiceIfNeededLocked();
+        if (preferredServiceName != null) {
+            try {
+                ApplicationInfo preferredServiceInfo =
+                    packageManager.getApplicationInfo(preferredServiceName.getPackageName(), 0);
+                UserHandle user = UserHandle.getUserHandleForUid(preferredServiceInfo.uid);
+                return bindServiceIfNeededLocked(user.getIdentifier(), preferredServiceName);
+            } catch (NameNotFoundException nnfe) {
+                Log.e(TAG, "Packange name not found, dropping polling frame", nnfe);
+                unbindServiceIfNeededLocked();
+            }
         }
         return bindServiceIfNeededLocked(mPaymentServiceUserId, mPaymentServiceName);
     }
 
-    public void onPollingLoopDetected(Bundle pollingFrame) {
+    @TargetApi(35)
+    @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+    public void updatePollingLoopFilters(int userId, List<ApduServiceInfo> services) {
+        HashMap<String, List<ApduServiceInfo>> pollingLoopFilters =
+                new HashMap<String, List<ApduServiceInfo>>();
+        HashMap<Pattern, List<ApduServiceInfo>> pollingLoopPatternFilters =
+                new HashMap<Pattern, List<ApduServiceInfo>>();
+        for (ApduServiceInfo serviceInfo : services) {
+            for (String plf : serviceInfo.getPollingLoopFilters()) {
+                List<ApduServiceInfo> list =
+                        pollingLoopFilters.getOrDefault(plf, new ArrayList<ApduServiceInfo>());
+                list.add(serviceInfo);
+                pollingLoopFilters.putIfAbsent(plf, list);
+
+            }
+            for (Pattern plpf : serviceInfo.getPollingLoopPatternFilters()) {
+                List<ApduServiceInfo> list =
+                        pollingLoopPatternFilters.getOrDefault(plpf,
+                        new ArrayList<ApduServiceInfo>());
+                list.add(serviceInfo);
+                pollingLoopPatternFilters.putIfAbsent(plpf, list);
+
+            }
+        }
+        mPollingLoopFilters.put(Integer.valueOf(userId), pollingLoopFilters);
+        mPollingLoopPatternFilters.put(Integer.valueOf(userId), pollingLoopPatternFilters);
+    }
+
+    @TargetApi(35)
+    @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+    public void onPollingLoopDetected(List<PollingFrame> pollingFrames) {
         synchronized (mLock) {
             if (mState == STATE_IDLE) {
                 mState = STATE_POLLING_LOOP;
             }
-            Messenger service = getForegroundServiceOrDefault();
-            if (service != null) {
-                ArrayList<Bundle> frames = new ArrayList<Bundle>();
-                frames.add(pollingFrame);
-                sendPollingFramesToServiceLocked(service, frames);
-            } else {
-                if (mPendingPollingLoopFrames == null) {
-                    mPendingPollingLoopFrames = new ArrayList<Bundle>(1);
-                }
+            int onCount = 0;
+            if (mPendingPollingLoopFrames == null) {
+                mPendingPollingLoopFrames = new ArrayList<PollingFrame>(1);
+            }
+            Messenger service = null;
+            for (PollingFrame pollingFrame : pollingFrames) {
                 mPendingPollingLoopFrames.add(pollingFrame);
+                if (pollingFrame.getType()
+                        == PollingFrame.POLLING_LOOP_TYPE_F) {
+                    service = getForegroundServiceOrDefault();
+                } else if (pollingFrame.getType()
+                        == PollingFrame.POLLING_LOOP_TYPE_UNKNOWN) {
+                    byte[] data = pollingFrame.getData();
+                    String dataStr = HexFormat.of().formatHex(data).toUpperCase(Locale.ROOT);
+                    List<ApduServiceInfo> serviceInfos =
+                            mPollingLoopFilters.get(ActivityManager.getCurrentUser()).get(dataStr);
+                    Map<Pattern, List<ApduServiceInfo>> patternMappingForUser =
+                            mPollingLoopPatternFilters.get(ActivityManager.getCurrentUser());
+                    Set<Pattern> patternSet = patternMappingForUser.keySet();
+                    List<Pattern> matchedPatterns = patternSet.stream()
+                            .filter(p -> p.matcher(dataStr).matches()).toList();
+                    if (!matchedPatterns.isEmpty()) {
+                        if (service == null) {
+                            serviceInfos = new ArrayList<ApduServiceInfo>();
+                        }
+                        for (Pattern matchedPattern : matchedPatterns) {
+                            serviceInfos.addAll(patternMappingForUser.get(matchedPattern));
+                        }
+                    }
+                    if (serviceInfos != null && serviceInfos.size() > 0) {
+                        ApduServiceInfo serviceInfo;
+                        if (serviceInfos.size() == 1) {
+                            serviceInfo = serviceInfos.get(0);
+                        } else {
+                            serviceInfo = mAidCache.resolvePollingLoopFilterConflict(serviceInfos);
+                            if (serviceInfo == null) {
+                                /*  If neither the foreground or payments service can handle the plf,
+                                *  pick the first in the list. */
+                                serviceInfo = serviceInfos.get(0);
+                            }
+                        }
+                        if (serviceInfo.getShouldAutoTransact(dataStr)) {
+                            allowOneTransaction();
+                            pollingFrame.setTriggeredAutoTransact(true);
+                        }
+                        UserHandle user = UserHandle.getUserHandleForUid(serviceInfo.getUid());
+                        service = bindServiceIfNeededLocked(user.getIdentifier(),
+                                serviceInfo.getComponent());
+                    } else {
+                        service = getForegroundServiceOrDefault();
+                    }
+
+                    if (mStatsdUtils != null) {
+                        mStatsdUtils.tallyPollingFrame(dataStr, pollingFrame);
+                    }
+                }
+                if (mStatsdUtils != null) {
+                    mStatsdUtils.logPollingFrames();
+                }
+            }
+
+            if (service == null) {
+                if (mActiveService != null) {
+                        service = mActiveService;
+                } else if (mPendingPollingLoopFrames.size() >= 4) {
+                    loop_on_off: for (PollingFrame frame : mPendingPollingLoopFrames) {
+                        int type = frame.getType();
+                        switch (type) {
+                            case PollingFrame.POLLING_LOOP_TYPE_ON:
+                                onCount++;
+                                break;
+                            case PollingFrame.POLLING_LOOP_TYPE_OFF:
+                                // Send the loop data if we've seen at least one on before an off.
+                                if (onCount >=1) {
+                                    service = getForegroundServiceOrDefault();
+                                    break loop_on_off;
+                                }
+                                break;
+                            default:
+                        }
+                    }
+                }
+            }
+
+            if (service != null) {
+                sendPollingFramesToServiceLocked(service, mPendingPollingLoopFrames);
+                mPendingPollingLoopFrames = null;
             }
         }
     }
 
+    private void allowOneTransaction() {
+        Log.d(TAG, "disabling observe mode for one transaction.");
+        mEnableObserveModeAfterTransaction = true;
+        NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
+        adapter.setObserveModeEnabled(false);
+    }
+
     /**
      *  Preferred foreground service changed
      */
@@ -198,24 +348,53 @@
          }
      }
 
+    public void onFieldChangeDetected(boolean fieldOn) {
+        if (!fieldOn && mEnableObserveModeOnFieldOff &&  mEnableObserveModeAfterTransaction) {
+            Log.d(TAG, "re-enabling observe mode after NFC Field off.");
+            mEnableObserveModeAfterTransaction = false;
+            mEnableObserveModeOnFieldOff = false;
+            NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
+            adapter.setObserveModeEnabled(true);
+        }
+    }
+
     public void onHostEmulationActivated() {
-        Log.d(TAG, "notifyHostEmulationActivated");
         synchronized (mLock) {
             // Regardless of what happens, if we're having a tap again
             // activity up, close it
             Intent intent = new Intent(TapAgainDialog.ACTION_CLOSE);
-            intent.setPackage("com.android.nfc");
+            intent.setPackage(NFC_PACKAGE);
             mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
             if (mState != STATE_IDLE) {
                 Log.e(TAG, "Got activation event in non-idle state");
             }
             mState = STATE_W4_SELECT;
         }
-        if (Flags.testFlag()) {
-            Log.v(TAG, "Test feature flag enabled");
+    }
+
+    static private class UnroutableAidBugReportRunnable implements Runnable {
+        List<String> mUnroutedAids;
+
+        UnroutableAidBugReportRunnable(String aid) {
+            mUnroutedAids = new ArrayList<String>(1);
+            mUnroutedAids.add(aid);
+        }
+
+        void addAid(String aid) {
+            mUnroutedAids.add(aid);
+        }
+        @Override
+        public void run() {
+            NfcService.getInstance().mNfcDiagnostics.takeBugReport(
+                    "NFC tap failed."
+                        + " (If you weren't using NFC, "
+                        + "no need to submit this report.)",
+                    "Couldn't route " + String.join(", ", mUnroutedAids));
         }
     }
 
+    UnroutableAidBugReportRunnable mUnroutableAidBugReportRunnable = null;
+
     public void onHostEmulationData(byte[] data) {
         Log.d(TAG, "notifyHostEmulationData");
         String selectAid = findSelectAid(data);
@@ -237,9 +416,37 @@
                 }
                 resolveInfo = mAidCache.resolveAid(selectAid);
                 if (resolveInfo == null || resolveInfo.services.size() == 0) {
+                    if (selectAid.equals(NDEF_V1_AID) || selectAid.equals(NDEF_V2_AID)) {
+                        Log.w(TAG, "Can't route NDEF AID, sending AID_NOT_FOUND");
+                    } else if (!mPowerManager.isScreenOn()) {
+                      Log.i(TAG,
+                          "Screen is off, sending AID_NOT_FOUND, but not triggering bug report");
+                    } else {
+                        Log.w(TAG, "Can't handle AID " + selectAid + " sending AID_NOT_FOUND");
+                        if (mUnroutableAidBugReportRunnable != null) {
+                            mUnroutableAidBugReportRunnable.addAid(selectAid);
+                        } else {
+                            mUnroutableAidBugReportRunnable =
+                                    new UnroutableAidBugReportRunnable(selectAid);
+                            /* Wait 1s to see if there is an alternate AID we can route before
+                             * taking a bug report */
+                            mHandler.postDelayed(mUnroutableAidBugReportRunnable, 1000);
+                        }
+                    }
+                    NfcInjector.getInstance().getNfcEventLog().logEvent(
+                            NfcEventProto.EventType.newBuilder()
+                                    .setCeUnroutableAid(
+                                        NfcEventProto.NfcCeUnroutableAid.newBuilder()
+                                            .setAid(selectAid)
+                                            .build())
+                                    .build());
                     // Tell the remote we don't handle this AID
                     NfcService.getInstance().sendData(AID_NOT_FOUND);
                     return;
+                } else if (mUnroutableAidBugReportRunnable != null) {
+                    /* If there is a pending bug report runnable, cancel it. */
+                    mHandler.removeCallbacks(mUnroutableAidBugReportRunnable);
+                    mUnroutableAidBugReportRunnable = null;
                 }
                 mLastSelectedAid = selectAid;
                 if (resolveInfo.defaultService != null) {
@@ -343,7 +550,6 @@
                                     statsdCategory,
                                     "HCE",
                                     uid);
-                            Log.d(TAG, "StatsdCeEventsFlag disabled logged: " + statsdCategory);
                         }
                     } else {
                         Log.d(TAG, "Dropping non-select APDU in STATE_W4_SELECT");
@@ -389,9 +595,17 @@
             mActiveService = null;
             mActiveServiceName = null;
             mActiveServiceUserId = -1;
+            mPendingPollingLoopFrames = null;
             unbindServiceIfNeededLocked();
             mState = STATE_IDLE;
 
+            if (mEnableObserveModeAfterTransaction) {
+                Log.d(TAG, "re-enabling observe mode after HCE deactivation");
+                mEnableObserveModeAfterTransaction = false;
+                NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
+                adapter.setObserveModeEnabled(true);
+            }
+
             if (mStatsdUtils != null) {
                 mStatsdUtils.logCardEmulationDeactivatedEvent();
             }
@@ -406,6 +620,10 @@
             } else {
                 sendDeactivateToActiveServiceLocked(HostApduService.DEACTIVATION_DESELECTED);
             }
+            if (mEnableObserveModeAfterTransaction) {
+                Log.i(TAG, "OffHost AID selected, waiting for Field off to reenable observe mode");
+                mEnableObserveModeOnFieldOff = true;
+            }
             mActiveService = null;
             mActiveServiceName = null;
             mActiveServiceUserId = -1;
@@ -414,12 +632,16 @@
 
             //close the TapAgainDialog
             Intent intent = new Intent(TapAgainDialog.ACTION_CLOSE);
-            intent.setPackage("com.android.nfc");
+            intent.setPackage(NFC_PACKAGE);
             mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
         }
     }
 
     Messenger bindServiceIfNeededLocked(int userId, ComponentName service) {
+        if (service == null) {
+            Log.e(TAG, "service ComponentName is null");
+            return null;
+        }
         if (mPaymentServiceName != null && mPaymentServiceName.equals(service)
                 && mPaymentServiceUserId == userId) {
             Log.d(TAG, "Service already bound as payment service.");
@@ -466,7 +688,7 @@
         }
         Message msg = Message.obtain(null, HostApduService.MSG_COMMAND_APDU);
         Bundle dataBundle = new Bundle();
-        dataBundle.putByteArray("data", data);
+        dataBundle.putByteArray(DATA_KEY, data);
         msg.setData(dataBundle);
         msg.replyTo = mMessenger;
         try {
@@ -476,7 +698,8 @@
         }
     }
 
-    void sendPollingFramesToServiceLocked(Messenger service, ArrayList<Bundle> frames) {
+    void sendPollingFramesToServiceLocked(Messenger service,
+            ArrayList<PollingFrame> pollingFrames) {
         if (!Objects.equals(service, mActiveService)) {
             sendDeactivateToActiveServiceLocked(HostApduService.DEACTIVATION_DESELECTED);
             mActiveService = service;
@@ -490,7 +713,8 @@
         }
         Message msg = Message.obtain(null, HostApduService.MSG_POLLING_LOOP);
         Bundle msgData = new Bundle();
-        msgData.putParcelableArrayList(HostApduService.POLLING_LOOP_FRAMES_BUNDLE_KEY, frames);
+        msgData.putParcelableArrayList(HostApduService.KEY_POLLING_LOOP_FRAMES_BUNDLE,
+                pollingFrames);
         msg.setData(msgData);
         msg.replyTo = mMessenger;
         if (mState == STATE_IDLE) {
@@ -515,13 +739,19 @@
     }
 
     void unbindPaymentServiceLocked() {
+        Log.d(TAG, "Unbinding payment service");
         if (mPaymentServiceBound) {
-            mContext.unbindService(mPaymentConnection);
+            try {
+                mContext.unbindService(mPaymentConnection);
+            } catch (Exception e) {
+                Log.w(TAG, "Failed to unbind payment service: " + mPaymentServiceName, e);
+            }
             mPaymentServiceBound = false;
-            mPaymentService = null;
-            mPaymentServiceName = null;
-            mPaymentServiceUserId = -1;
         }
+
+        mPaymentService = null;
+        mPaymentServiceName = null;
+        mPaymentServiceUserId = -1;
     }
 
     void bindPaymentServiceLocked(int userId, ComponentName service) {
@@ -548,12 +778,17 @@
     void unbindServiceIfNeededLocked() {
         if (mServiceBound) {
             Log.d(TAG, "Unbinding from service " + mServiceName);
-            mContext.unbindService(mConnection);
+            try {
+                mContext.unbindService(mConnection);
+            } catch (Exception e) {
+                Log.w(TAG, "Failed to unbind service " + mServiceName, e);
+            }
             mServiceBound = false;
-            mService = null;
-            mServiceName = null;
-            mServiceUserId = -1;
         }
+
+        mService = null;
+        mServiceName = null;
+        mServiceUserId = -1;
     }
 
     void launchTapAgain(ApduServiceInfo service, String category) {
@@ -607,20 +842,22 @@
             synchronized (mLock) {
                 /* Preferred Payment Service has been changed. */
                 if (!mLastBoundPaymentServiceName.equals(name)) {
+                    Log.i(TAG, "Ignoring bound payment service, " + name + " != "
+                            + mLastBoundPaymentServiceName);
                     return;
                 }
                 mPaymentServiceName = name;
                 mPaymentService = new Messenger(service);
+                Log.i(TAG, "Payment service bound: " + name);
             }
         }
 
         @Override
         public void onServiceDisconnected(ComponentName name) {
+            Log.i(TAG, "Payment service disconnected: " + name);
             synchronized (mLock) {
                 mPaymentService = null;
-                mPaymentServiceBound = false;
                 mPaymentServiceName = null;
-                mPaymentServiceUserId = -1;
             }
         }
     };
@@ -636,7 +873,7 @@
                 mService = new Messenger(service);
                 mServiceName = name;
                 mServiceBound = true;
-                Log.d(TAG, "Service bound");
+                Log.d(TAG, "Service bound: " + name);
                 mState = STATE_XFER;
                 // Send pending select APDU
                 if (mSelectApdu != null) {
@@ -648,6 +885,8 @@
                 } else if (mPendingPollingLoopFrames != null) {
                     sendPollingFramesToServiceLocked(mService, mPendingPollingLoopFrames);
                     mPendingPollingLoopFrames = null;
+                } else {
+                    Log.d(TAG, "bound with nothing to send");
                 }
             }
         }
@@ -655,11 +894,10 @@
         @Override
         public void onServiceDisconnected(ComponentName name) {
             synchronized (mLock) {
-                Log.d(TAG, "Service unbound");
+                Log.d(TAG, "Service unbound: " + name);
                 mService = null;
                 mServiceName = null;
                 mServiceBound = false;
-                mServiceUserId = -1;
             }
         }
     };
@@ -681,7 +919,7 @@
                 if (dataBundle == null) {
                     return;
                 }
-                byte[] data = dataBundle.getByteArray("data");
+                byte[] data = dataBundle.getByteArray(DATA_KEY);
                 if (data == null || data.length == 0) {
                     Log.e(TAG, "Dropping empty R-APDU");
                     return;
@@ -767,6 +1005,11 @@
     }
 
     @VisibleForTesting
+    public ServiceConnection getPaymentConnection(){
+        return mPaymentConnection;
+    }
+
+    @VisibleForTesting
     public IBinder getMessenger(){
         if (mActiveService != null) {
             return mActiveService.getBinder();
@@ -775,6 +1018,11 @@
     }
 
     @VisibleForTesting
+    public Messenger getLocalMessenger() {
+        return mMessenger;
+    }
+
+    @VisibleForTesting
     public ComponentName getServiceName(){
         return mLastBoundPaymentServiceName;
     }
@@ -783,4 +1031,14 @@
     public Boolean isServiceBounded(){
         return mServiceBound;
     }
+
+    @VisibleForTesting
+    public Map<Integer, Map<String, List<ApduServiceInfo>>> getPollingLoopFilters() {
+        return mPollingLoopFilters;
+    }
+
+    @VisibleForTesting
+    public Map<Integer, Map<Pattern, List<ApduServiceInfo>>> getPollingLoopPatternFilters() {
+        return mPollingLoopPatternFilters;
+    }
 }
diff --git a/src/com/android/nfc/cardemulation/HostNfcFEmulationManager.java b/src/com/android/nfc/cardemulation/HostNfcFEmulationManager.java
index 1aa9b32..f7cf14b 100644
--- a/src/com/android/nfc/cardemulation/HostNfcFEmulationManager.java
+++ b/src/com/android/nfc/cardemulation/HostNfcFEmulationManager.java
@@ -45,7 +45,7 @@
 
 public class HostNfcFEmulationManager {
     static final String TAG = "HostNfcFEmulationManager";
-    static final boolean DBG = NfcProperties.debug_enabled().orElse(false);
+    static final boolean DBG = NfcProperties.debug_enabled().orElse(true);
 
     static final int STATE_IDLE = 0;
     static final int STATE_W4_SERVICE = 1;
diff --git a/src/com/android/nfc/cardemulation/PreferredServices.java b/src/com/android/nfc/cardemulation/PreferredServices.java
index e3c91a0..2e22dd5 100644
--- a/src/com/android/nfc/cardemulation/PreferredServices.java
+++ b/src/com/android/nfc/cardemulation/PreferredServices.java
@@ -15,6 +15,7 @@
  */
 package com.android.nfc.cardemulation;
 
+import android.annotation.TargetApi;
 import android.app.ActivityManager;
 import android.content.ComponentName;
 import android.content.Context;
@@ -24,10 +25,13 @@
 import android.nfc.cardemulation.ApduServiceInfo;
 import android.nfc.cardemulation.CardEmulation;
 import android.nfc.cardemulation.Utils;
+import android.os.Build;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.permission.flags.Flags;
+
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
 import android.sysprop.NfcProperties;
@@ -38,7 +42,9 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * This class keeps track of what HCE/SE-based services are
@@ -58,7 +64,7 @@
  */
 public class PreferredServices implements com.android.nfc.ForegroundUtils.Callback {
     static final String TAG = "PreferredCardEmulationServices";
-    static final boolean DBG = NfcProperties.debug_enabled().orElse(false);
+    static final boolean DBG = NfcProperties.debug_enabled().orElse(true);
     static final Uri paymentDefaultUri = Settings.Secure.getUriFor(
             Constants.SETTINGS_SECURE_NFC_PAYMENT_DEFAULT_COMPONENT);
     static final Uri paymentForegroundUri = Settings.Secure.getUriFor(
@@ -66,6 +72,7 @@
 
     final SettingsObserver mSettingsObserver;
     final Context mContext;
+    final WalletRoleObserver mWalletRoleObserver;
     final RegisteredServicesCache mServiceCache;
     final RegisteredAidCache mAidCache;
     final Callback mCallback;
@@ -93,6 +100,10 @@
     ComponentName mForegroundCurrent; // The currently computed foreground component
     int mForegroundCurrentUid; // The UID of the currently computed foreground component
 
+    ComponentName mDefaultWalletHolderPaymentService;
+
+    int mUserIdDefaultWalletHolder;
+
     public interface Callback {
         /**
          * Notify when preferred payment service is changed
@@ -105,8 +116,10 @@
     }
 
     public PreferredServices(Context context, RegisteredServicesCache serviceCache,
-            RegisteredAidCache aidCache, Callback callback) {
+            RegisteredAidCache aidCache, WalletRoleObserver walletRoleObserver,
+            Callback callback) {
         mContext = context;
+        mWalletRoleObserver = walletRoleObserver;
         mForegroundUtils = ForegroundUtils.getInstance(
                 context.getSystemService(ActivityManager.class));
         mServiceCache = serviceCache;
@@ -121,8 +134,15 @@
                 paymentForegroundUri,
                 true, mSettingsObserver, UserHandle.ALL);
 
+        int currentUserId = ActivityManager.getCurrentUser();
+
         // Load current settings defaults for payments
-        loadDefaultsFromSettings(ActivityManager.getCurrentUser(), false);
+        loadDefaultsFromSettings(currentUserId, false);
+
+        if (mWalletRoleObserver.isWalletRoleFeatureEnabled()) {
+            String holder = mWalletRoleObserver.getDefaultWalletRoleHolder(currentUserId);
+            onWalletRoleHolderChanged(holder, currentUserId);
+        }
     }
 
     private final class SettingsObserver extends ContentObserver {
@@ -141,6 +161,42 @@
         }
     };
 
+    @TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+    public void onWalletRoleHolderChanged(String defaultWalletHolderPackageName, int userId) {
+        if (defaultWalletHolderPackageName == null) {
+            mDefaultWalletHolderPaymentService = null;
+            mCallback.onPreferredPaymentServiceChanged(userId, null);
+            return;
+        }
+        List<ApduServiceInfo> serviceInfos = mServiceCache.getInstalledServices(userId);
+        List<ComponentName> roleHolderPaymentServices = new ArrayList<>();
+        int servicesCount = serviceInfos.size();
+        for(int i = 0; i < servicesCount; i++) {
+            ApduServiceInfo serviceInfo = serviceInfos.get(i);
+            ComponentName componentName = serviceInfo.getComponent();
+            if (componentName.getPackageName()
+                    .equals(defaultWalletHolderPackageName)) {
+                List<String> aids = serviceInfo.getAids();
+                int aidsCount = aids.size();
+                for (int j = 0; j < aidsCount; j++) {
+                    String aid = aids.get(j);
+                    if (serviceInfo.getCategoryForAid(aid)
+                            .equals(CardEmulation.CATEGORY_PAYMENT)) {
+                        roleHolderPaymentServices.add(componentName);
+                        break;
+                    }
+                }
+            }
+        }
+        mUserIdDefaultWalletHolder = userId;
+        ComponentName candidate = !roleHolderPaymentServices.isEmpty()
+                ? roleHolderPaymentServices.get(0) : null;
+        if (!Objects.equals(candidate, mDefaultWalletHolderPaymentService)) {
+            mCallback.onPreferredPaymentServiceChanged(userId, candidate);
+        }
+        mDefaultWalletHolderPaymentService = candidate;
+    }
+
     void loadDefaultsFromSettings(int userId, boolean force) {
         boolean paymentDefaultChanged = false;
         boolean paymentPreferForegroundChanged = false;
@@ -155,9 +211,15 @@
         UserHandle newUser = null;
         // search for default payment setting within enabled profiles
         for (UserHandle uh : userHandles) {
-            name = Settings.Secure.getString(
-                    mContext.createContextAsUser(uh, 0).getContentResolver(),
-                    Constants.SETTINGS_SECURE_NFC_PAYMENT_DEFAULT_COMPONENT);
+            try {
+                name = Settings.Secure.getString(
+                        mContext.createContextAsUser(uh, 0).getContentResolver(),
+                        Constants.SETTINGS_SECURE_NFC_PAYMENT_DEFAULT_COMPONENT);
+            } catch (IllegalStateException e) {
+                Log.d(TAG, "Fail to get PackageManager for user: " + uh);
+                continue;
+            }
+
             if (name != null) {
                 newUser = uh;
                 newDefaultName = name;
@@ -179,8 +241,9 @@
         boolean preferForeground = false;
         try {
             // get the setting from the main user instead of from the user profiles.
-            preferForeground = Settings.Secure.getInt(mContext
-                    .createContextAsUser(currentUser, 0).getContentResolver(),
+            preferForeground = mWalletRoleObserver.isWalletRoleFeatureEnabled()
+                    || Settings.Secure.getInt(mContext
+                            .createContextAsUser(currentUser, 0).getContentResolver(),
                     Constants.SETTINGS_SECURE_NFC_PAYMENT_FOREGROUND) != 0;
         } catch (SettingNotFoundException e) {
         }
@@ -203,7 +266,7 @@
             }
         }
         // Notify if anything changed
-        if (paymentDefaultChanged || force) {
+        if (!mWalletRoleObserver.isWalletRoleFeatureEnabled() && (paymentDefaultChanged || force)) {
             mCallback.onPreferredPaymentServiceChanged(newUser.getIdentifier(), newDefault);
         }
         if (paymentPreferForegroundChanged || force) {
diff --git a/src/com/android/nfc/cardemulation/RegisteredAidCache.java b/src/com/android/nfc/cardemulation/RegisteredAidCache.java
index 3cb220f..6aa98a1 100644
--- a/src/com/android/nfc/cardemulation/RegisteredAidCache.java
+++ b/src/com/android/nfc/cardemulation/RegisteredAidCache.java
@@ -16,13 +16,14 @@
 
 package com.android.nfc.cardemulation;
 
+import android.annotation.TargetApi;
+import android.annotation.FlaggedApi;
 import android.app.ActivityManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.nfc.cardemulation.ApduServiceInfo;
 import android.nfc.cardemulation.CardEmulation;
 import android.nfc.cardemulation.Utils;
-import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.sysprop.NfcProperties;
@@ -31,6 +32,8 @@
 
 import com.android.nfc.NfcService;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -43,12 +46,12 @@
 import java.util.NavigableMap;
 import java.util.PriorityQueue;
 import java.util.TreeMap;
-import androidx.annotation.VisibleForTesting;
 
 public class RegisteredAidCache {
     static final String TAG = "RegisteredAidCache";
 
-    static final boolean DBG = NfcProperties.debug_enabled().orElse(false);
+    static final boolean DBG = NfcProperties.debug_enabled().orElse(true);
+    private static final boolean VDBG = false; // turn on for local testing.
 
     static final int AID_ROUTE_QUAL_SUBSET = 0x20;
     static final int AID_ROUTE_QUAL_PREFIX = 0x10;
@@ -136,11 +139,17 @@
                     ", mustRoute=" + mustRoute +
                     '}';
         }
+
+        String getCategory() {
+            return category;
+        }
     }
 
     final AidResolveInfo EMPTY_RESOLVE_INFO = new AidResolveInfo();
 
     final Context mContext;
+
+    final WalletRoleObserver mWalletRoleObserver;
     final AidRoutingManager mRoutingManager;
 
     final Object mLock = new Object();
@@ -150,19 +159,25 @@
     ComponentName mPreferredForegroundService;
     int mUserIdPreferredForegroundService;
 
+    String mDefaultWalletHolderPackageName;
+
+    int mUserIdDefaultWalletHolder;
+
     boolean mNfcEnabled = false;
     boolean mSupportsPrefixes = false;
     boolean mSupportsSubset = false;
     boolean mRequiresScreenOnServiceExist = false;
 
-    public RegisteredAidCache(Context context) {
-        this(context, new AidRoutingManager());
+    public RegisteredAidCache(Context context, WalletRoleObserver walletRoleObserver) {
+        this(context, walletRoleObserver, new AidRoutingManager());
     }
 
     @VisibleForTesting
-    public RegisteredAidCache(Context context, AidRoutingManager routingManager) {
+    public RegisteredAidCache(Context context, WalletRoleObserver walletRoleObserver,
+            AidRoutingManager routingManager) {
         mContext = context;
-        mRoutingManager = routingManager ;
+        mWalletRoleObserver = walletRoleObserver;
+        mRoutingManager = routingManager;
         mPreferredPaymentService = null;
         mUserIdPreferredPaymentService = -1;
         mPreferredForegroundService = null;
@@ -246,12 +261,12 @@
                 resolveInfo.services.size() == 0) {
             return false;
         }
-
         if (resolveInfo.defaultService != null) {
             return service.equals(resolveInfo.defaultService.getComponent());
         } else if (resolveInfo.services.size() == 1) {
             return service.equals(resolveInfo.services.get(0).getComponent());
         } else {
+            Log.d(TAG, "Not Default Service: " + service.getClassName());
             // More than one service, not the default
             return false;
         }
@@ -261,6 +276,75 @@
         return mRequiresScreenOnServiceExist;
     }
 
+    @TargetApi(35)
+    @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+    ApduServiceInfo resolvePollingLoopFilterConflict(List<ApduServiceInfo> conflictingServices) {
+        ApduServiceInfo matchedForeground = null;
+        List<ApduServiceInfo> roleHolderServices = new ArrayList<>();
+        ApduServiceInfo matchedPayment = null;
+        for (ApduServiceInfo serviceInfo : conflictingServices) {
+            int userId = UserHandle.getUserHandleForUid(serviceInfo.getUid())
+                    .getIdentifier();
+            ComponentName componentName = serviceInfo.getComponent();
+
+            if (componentName.equals(mPreferredForegroundService) &&
+                    userId == mUserIdPreferredForegroundService) {
+                matchedForeground = serviceInfo;
+            } else if(mWalletRoleObserver.isWalletRoleFeatureEnabled()) {
+                if (userId == mUserIdDefaultWalletHolder &&
+                        componentName.getPackageName().equals(mDefaultWalletHolderPackageName)) {
+                    roleHolderServices.add(serviceInfo);
+                }
+            } else if (componentName.equals(mPreferredPaymentService) &&
+                    userId == mUserIdPreferredPaymentService) {
+                matchedPayment = serviceInfo;
+            }
+        }
+        if (matchedForeground != null) {
+            return matchedForeground;
+        }
+        if (mWalletRoleObserver.isWalletRoleFeatureEnabled()) {
+            roleHolderServices.sort((o1, o2) ->
+                    String.CASE_INSENSITIVE_ORDER.compare(o1.getComponent().toShortString(),
+                            o2.getComponent().toShortString()));
+            return roleHolderServices.isEmpty() ? null : roleHolderServices.get(0);
+        }
+        return matchedPayment;
+    }
+
+    private static void nonDefaultResolution(boolean serviceClaimsPaymentAid,
+            ServiceAidInfo serviceAidInfo, AidResolveInfo resolveInfo) {
+        if (serviceClaimsPaymentAid) {
+            // If this service claims it's a payment AID, don't route it,
+            // because it's not the default. Otherwise, add it to the list
+            // but not as default.
+            if (VDBG) Log.d(TAG, "resolveAidLocked: (Ignoring handling service " +
+                    serviceAidInfo.service.getComponent() +
+                    " because it's not the payment default.)");
+        } else {
+            if (serviceAidInfo.service.isCategoryOtherServiceEnabled()) {
+                if (VDBG) Log.d(TAG, "resolveAidLocked: " + serviceAidInfo.service.getComponent() +
+                        " is selected other service");
+                resolveInfo.services.add(serviceAidInfo.service);
+            }
+        }
+    }
+
+    private static void nonDefaultRouting(AidResolveInfo resolveInfo,
+            boolean makeSingleServiceDefault) {
+        if (resolveInfo.services.size() == 1 && makeSingleServiceDefault) {
+            if (DBG) Log.d(TAG,
+                    "resolveAidLocked: DECISION: making single handling service " +
+                            resolveInfo.services.get(0).getComponent() + " default.");
+            resolveInfo.defaultService = resolveInfo.services.get(0);
+        } else {
+            // Nothing to do, all services already in list
+            if (DBG) {
+                Log.d(TAG, "resolveAidLocked: DECISION: routing to all matching services");
+            }
+        }
+    }
+
     /**
      * Resolves a conflict between multiple services handling the same
      * AIDs. Note that the AID itself is not an input to the decision
@@ -269,8 +353,9 @@
      * this:
      *
      * 1) If there is a preferred foreground service, that service wins
-     * 2) Else, if there is a preferred payment service, that service wins
-     * 3) Else, if there is no winner, and all conflicting services will be
+     * 2) Else if there is a default wallet app, that app wins
+     * 3) Else, if there is a preferred payment service, that service wins
+     * 4) Else, if there is no winner, and all conflicting services will be
      *    in the list of resolved services.
      */
      AidResolveInfo resolveAidConflictLocked(Collection<ServiceAidInfo> conflictingServices,
@@ -284,6 +369,8 @@
 
         ApduServiceInfo matchedForeground = null;
         ApduServiceInfo matchedPayment = null;
+        List<ApduServiceInfo> defaultWalletServices = new ArrayList<>();
+
         for (ServiceAidInfo serviceAidInfo : conflictingServices) {
             boolean serviceClaimsPaymentAid =
                     CardEmulation.CATEGORY_PAYMENT.equals(serviceAidInfo.category);
@@ -293,31 +380,34 @@
 
             if (componentName.equals(mPreferredForegroundService) &&
                     userId == mUserIdPreferredForegroundService) {
+                if (VDBG) Log.d(TAG, "Prioritizing foreground services.");
                 resolveInfo.services.add(serviceAidInfo.service);
                 if (serviceClaimsPaymentAid) {
                     resolveInfo.category = CardEmulation.CATEGORY_PAYMENT;
                 }
                 matchedForeground = serviceAidInfo.service;
-            } else if (componentName.equals(mPreferredPaymentService) &&
-                    userId == mUserIdPreferredPaymentService &&
-                    serviceClaimsPaymentAid) {
+            } else if(mWalletRoleObserver.isWalletRoleFeatureEnabled()) {
+                if(userId == mUserIdDefaultWalletHolder
+                    && componentName.getPackageName().equals(
+                    mDefaultWalletHolderPackageName)) {
+                    if (VDBG) Log.d(TAG, "Prioritizing default wallet services.");
+                    resolveInfo.services.add(serviceAidInfo.service);
+                    if (serviceClaimsPaymentAid) {
+                        resolveInfo.category = CardEmulation.CATEGORY_PAYMENT;
+                    }
+                    defaultWalletServices.add(serviceAidInfo.service);
+                } else {
+                    nonDefaultResolution(serviceClaimsPaymentAid, serviceAidInfo, resolveInfo);
+                }
+            } else {
+                if (componentName.equals(mPreferredPaymentService)
+                    && userId == mUserIdPreferredPaymentService && serviceClaimsPaymentAid) {
+                if (DBG) Log.d(TAG, "Prioritizing dpp services.");
                 resolveInfo.services.add(serviceAidInfo.service);
                 resolveInfo.category = CardEmulation.CATEGORY_PAYMENT;
                 matchedPayment = serviceAidInfo.service;
-            } else {
-                if (serviceClaimsPaymentAid) {
-                    // If this service claims it's a payment AID, don't route it,
-                    // because it's not the default. Otherwise, add it to the list
-                    // but not as default.
-                    if (DBG) Log.d(TAG, "resolveAidLocked: (Ignoring handling service " +
-                            serviceAidInfo.service.getComponent() +
-                            " because it's not the payment default.)");
                 } else {
-                    if (serviceAidInfo.service.isCategoryOtherServiceEnabled()) {
-                        if (DBG) Log.d(TAG, serviceAidInfo.service.getComponent() +
-                                " is selected other service");
-                        resolveInfo.services.add(serviceAidInfo.service);
-                    }
+                    nonDefaultResolution(serviceClaimsPaymentAid, serviceAidInfo, resolveInfo);
                 }
             }
         }
@@ -327,21 +417,30 @@
             if (DBG) Log.d(TAG, "resolveAidLocked: DECISION: routing to foreground preferred " +
                     matchedForeground);
             resolveInfo.defaultService = matchedForeground;
+
+        // Wallet Role Holder and the PreferredPaymentService are mutually exclusive. If the wallet
+        // role feature is enabled, the matched payment check should not take place at all.
+        } else if (mWalletRoleObserver.isWalletRoleFeatureEnabled() &&
+                !defaultWalletServices.isEmpty()) {
+            // 2nd priority: if there is a default wallet application with services that
+            // claim this AID, that application gets it.
+            if (DBG) Log.d(TAG, "resolveAidLocked: DECISION: routing to default wallet " +
+                    mDefaultWalletHolderPackageName);
+            // If the role holder has multiple services with the same AID type, then we select
+            // the first one. The services are sorted alphabetically based on their component
+            // names.
+            defaultWalletServices.sort((o1, o2) ->
+                    String.CASE_INSENSITIVE_ORDER.compare(o1.getComponent().toShortString(),
+                            o2.getComponent().toShortString()));
+            resolveInfo.defaultService = defaultWalletServices.get(0);
         } else if (matchedPayment != null) {
-            // 2nd priority: if there is a preferred payment service,
+            // 3d priority: if there is a preferred payment service,
             // and that service claims this as a payment AID, that service gets it
             if (DBG) Log.d(TAG, "resolveAidLocked: DECISION: routing to payment default " +
                     "default " + matchedPayment);
             resolveInfo.defaultService = matchedPayment;
         } else {
-            if (resolveInfo.services.size() == 1 && makeSingleServiceDefault) {
-                if (DBG) Log.d(TAG, "resolveAidLocked: DECISION: making single handling service " +
-                        resolveInfo.services.get(0).getComponent() + " default.");
-                resolveInfo.defaultService = resolveInfo.services.get(0);
-            } else {
-                // Nothing to do, all services already in list
-                if (DBG) Log.d(TAG, "resolveAidLocked: DECISION: routing to all matching services");
-            }
+            nonDefaultRouting(resolveInfo, makeSingleServiceDefault);
         }
         return resolveInfo;
     }
@@ -349,6 +448,7 @@
     class DefaultServiceInfo {
         ServiceAidInfo paymentDefault;
         ServiceAidInfo foregroundDefault;
+        List<ServiceAidInfo> walletDefaults = new ArrayList<>();
     }
 
     DefaultServiceInfo findDefaultServices(ArrayList<ServiceAidInfo> serviceAidInfos) {
@@ -364,7 +464,12 @@
             if (componentName.equals(mPreferredForegroundService) &&
                     userId == mUserIdPreferredForegroundService) {
                 defaultServiceInfo.foregroundDefault = serviceAidInfo;
-            } else if (componentName.equals(mPreferredPaymentService) &&
+            } else if(mWalletRoleObserver.isWalletRoleFeatureEnabled()) {
+                if(userId == mUserIdDefaultWalletHolder && componentName
+                        .getPackageName().equals(mDefaultWalletHolderPackageName)) {
+                    defaultServiceInfo.walletDefaults.add(serviceAidInfo);
+                }
+            }else if (componentName.equals(mPreferredPaymentService) &&
                     userId == mUserIdPreferredPaymentService &&
                     serviceClaimsPaymentAid) {
                 defaultServiceInfo.paymentDefault = serviceAidInfo;
@@ -373,6 +478,27 @@
         return defaultServiceInfo;
     }
 
+    private AidResolveInfo noChildrenAidsPreferred(ArrayList<ServiceAidInfo> aidServices,
+            ArrayList<ServiceAidInfo> conflictingServices) {
+        // No children that are preferred; add all services of the root
+        // make single service default if no children are present
+        if (DBG) Log.d(TAG, "No service has preference, adding all.");
+        AidResolveInfo resolveinfo =
+                resolveAidConflictLocked(aidServices, conflictingServices.isEmpty());
+        //If the AID is subsetAID check for conflicting prefix in all
+        //conflciting services and root services.
+        if (isSubset(aidServices.get(0).aid)) {
+            ArrayList<ApduServiceInfo> apduServiceList = new ArrayList<ApduServiceInfo>();
+            for (ServiceAidInfo serviceInfo : conflictingServices)
+                apduServiceList.add(serviceInfo.service);
+            for (ServiceAidInfo serviceInfo : aidServices)
+                apduServiceList.add(serviceInfo.service);
+            resolveinfo.prefixInfo = findPrefixConflictForSubsetAid(
+                    aidServices.get(0).aid, apduServiceList, false);
+        }
+        return resolveinfo;
+    }
+
     AidResolveInfo resolveAidConflictLocked(ArrayList<ServiceAidInfo> aidServices,
                                                   ArrayList<ServiceAidInfo> conflictingServices) {
         // Find defaults among the root AID services themselves
@@ -383,9 +509,11 @@
         AidResolveInfo resolveinfo;
         // Three conditions under which the root AID gets to be the default
         // 1. A service registering the root AID is the current foreground preferred
-        // 2. A service registering the root AID is the current tap & pay default AND
+        // 2. A service registering the root AID is the wallet role holder AND no child
+        //    child is the current foreground preferred
+        // 3. A service registering the root AID is the current tap & pay default AND
         //    no child is the current foreground preferred
-        // 3. There is only one service for the root AID, and there are no children
+        // 4. There is only one service for the root AID, and there are no children
         if (aidDefaultInfo.foregroundDefault != null) {
             if (DBG) Log.d(TAG, "Prefix AID service " +
                     aidDefaultInfo.foregroundDefault.service.getComponent() + " has foreground" +
@@ -399,6 +527,37 @@
                         List.of(resolveinfo.defaultService), true);
             }
              return resolveinfo;
+        } else if (mWalletRoleObserver.isWalletRoleFeatureEnabled()) {
+            if(!aidDefaultInfo.walletDefaults.isEmpty()) {
+                // Check if any of the conflicting services is foreground default
+                if (conflictingDefaultInfo.foregroundDefault != null) {
+                    // Conflicting AID registration is in foreground, trumps prefix tap&pay default
+                    if (DBG) Log.d(TAG, "One of the conflicting AID registrations is foreground " +
+                            "preferred, ignoring prefix.");
+                    return EMPTY_RESOLVE_INFO;
+                } else {
+                    // Prefix service is default wallet, treat as normal AID conflict for just prefix
+                    if (DBG) Log.d(TAG, "Default wallet app exists. ignoring conflicting AIDs.");
+                    resolveinfo = resolveAidConflictLocked(aidServices, true);
+                    //If the AID is subsetAID check for prefix in all services.
+                    if (isSubset(aidServices.get(0).aid)) {
+                        resolveinfo.prefixInfo = findPrefixConflictForSubsetAid(
+                                aidServices.get(0).aid,
+                                List.of(resolveinfo.defaultService), true);
+                    }
+                    return resolveinfo;
+                }
+            } else {
+                if (conflictingDefaultInfo.foregroundDefault != null ||
+                        !conflictingDefaultInfo.walletDefaults.isEmpty()) {
+                    if (DBG) Log.d(TAG,
+                            "One of the conflicting AID registrations is wallet holder " +
+                            "or foreground preferred, ignoring prefix.");
+                    return EMPTY_RESOLVE_INFO;
+                } else {
+                    return noChildrenAidsPreferred(aidServices, conflictingServices);
+                }
+            }
         } else if (aidDefaultInfo.paymentDefault != null) {
             // Check if any of the conflicting services is foreground default
             if (conflictingDefaultInfo.foregroundDefault != null) {
@@ -426,22 +585,7 @@
                         "default or foreground preferred, ignoring prefix.");
                 return EMPTY_RESOLVE_INFO;
             } else {
-                // No children that are preferred; add all services of the root
-                // make single service default if no children are present
-                if (DBG) Log.d(TAG, "No service has preference, adding all.");
-                resolveinfo = resolveAidConflictLocked(aidServices, conflictingServices.isEmpty());
-                //If the AID is subsetAID check for conflicting prefix in all
-                //conflciting services and root services.
-                if (isSubset(aidServices.get(0).aid)) {
-                    ArrayList<ApduServiceInfo> apduServiceList = new ArrayList<ApduServiceInfo>();
-                    for (ServiceAidInfo serviceInfo : conflictingServices)
-                        apduServiceList.add(serviceInfo.service);
-                    for (ServiceAidInfo serviceInfo : aidServices)
-                        apduServiceList.add(serviceInfo.service);
-                    resolveinfo.prefixInfo = findPrefixConflictForSubsetAid(
-                            aidServices.get(0).aid, apduServiceList, false);
-                }
-                return resolveinfo;
+                return noChildrenAidsPreferred(aidServices, conflictingServices);
             }
         }
     }
@@ -477,7 +621,7 @@
                 continue;
             }
             for (ApduServiceInfo service : entry.getValue()) {
-                if (DBG) Log.d(TAG, "generateServiceMap component: " + service.getComponent());
+                if (VDBG) Log.d(TAG, "generateServiceMap component: " + service.getComponent());
                 List<String> prefixAids = service.getPrefixAids();
                 List<String> subSetAids = service.getSubsetAids();
 
@@ -869,7 +1013,11 @@
             reversedQueue.removeAll(resolvedAids);
             resolvedAids.clear();
         }
-
+        if (DBG) {
+            for (String key : mAidCache.keySet()) {
+                Log.d(TAG, "aid cache entry" + key + " val:" + mAidCache.get(key).toString());
+            }
+        }
         updateRoutingLocked(false);
     }
 
@@ -1044,6 +1192,15 @@
         }
     }
 
+    public void onWalletRoleHolderChanged(String defaultWalletHolderPackageName, int userId) {
+        if (DBG) Log.d(TAG, "Default wallet holder changed for user:" + userId);
+        synchronized (mLock) {
+            mDefaultWalletHolderPackageName = defaultWalletHolderPackageName;
+            mUserIdDefaultWalletHolder = userId;
+            generateAidCacheLocked();
+        }
+    }
+
     public ComponentName getPreferredService() {
         if (mPreferredForegroundService != null) {
             // return current foreground service
@@ -1053,6 +1210,40 @@
             return mPreferredPaymentService;
         }
     }
+    public boolean isPreferredServicePackageNameForUser(String packageName, int userId) {
+        if (mPreferredForegroundService != null) {
+            if (mPreferredForegroundService.getPackageName().equals(packageName) &&
+                userId == mUserIdPreferredForegroundService) {
+                return true;
+            } else {
+                Log.i(TAG, "NfcService:" + packageName + "(" + userId
+                    + ") is not equal to the foreground service "
+                    + mPreferredForegroundService + "(" + mUserIdPreferredForegroundService +")" );
+                return false;
+            }
+        } else if(mWalletRoleObserver.isWalletRoleFeatureEnabled()) {
+            if (mDefaultWalletHolderPackageName != null &&
+                mDefaultWalletHolderPackageName.equals(packageName) &&
+                userId == mUserIdDefaultWalletHolder) {
+                return true;
+            } else {
+                Log.i(TAG, "NfcService:" + packageName + "(" + userId
+                    + ")  is not equal to the default wallet service "
+                    + mDefaultWalletHolderPackageName + "(" + mUserIdDefaultWalletHolder +")" );
+                return false;
+            }
+        } else if (mPreferredPaymentService != null &&
+            userId == mUserIdPreferredPaymentService &&
+            mPreferredPaymentService.getPackageName().equals(packageName)) {
+            return true;
+        } else {
+            Log.i(TAG, "NfcService:" + packageName + "(" + userId
+                    + ") is not equal to the default payment service "
+                    + mPreferredPaymentService + "(" + mUserIdPreferredPaymentService +")" );
+            return false;
+        }
+    }
+
 
     public void onNfcDisabled() {
         synchronized (mLock) {
diff --git a/src/com/android/nfc/cardemulation/RegisteredNfcFServicesCache.java b/src/com/android/nfc/cardemulation/RegisteredNfcFServicesCache.java
index fd96f30..2aeab82 100644
--- a/src/com/android/nfc/cardemulation/RegisteredNfcFServicesCache.java
+++ b/src/com/android/nfc/cardemulation/RegisteredNfcFServicesCache.java
@@ -62,7 +62,8 @@
 public class RegisteredNfcFServicesCache {
     static final String XML_INDENT_OUTPUT_FEATURE = "http://xmlpull.org/v1/doc/features.html#indent-output";
     static final String TAG = "RegisteredNfcFServicesCache";
-    static final boolean DBG = NfcProperties.debug_enabled().orElse(false);
+    static final boolean DBG = NfcProperties.debug_enabled().orElse(true);
+    private static final boolean VDBG = false; // turn on for local testing.
 
     final Context mContext;
     final AtomicReference<BroadcastReceiver> mReceiver;
@@ -288,14 +289,14 @@
             return;
         }
         ArrayList<NfcFServiceInfo> newServices = null;
+        ArrayList<NfcFServiceInfo> toBeAdded = new ArrayList<>();
+        ArrayList<NfcFServiceInfo> toBeRemoved = new ArrayList<>();
         synchronized (mLock) {
             UserServices userServices = findOrCreateUserLocked(userId);
 
             // Check update
             ArrayList<NfcFServiceInfo> cachedServices =
                     new ArrayList<NfcFServiceInfo>(userServices.services.values());
-            ArrayList<NfcFServiceInfo> toBeAdded = new ArrayList<NfcFServiceInfo>();
-            ArrayList<NfcFServiceInfo> toBeRemoved = new ArrayList<NfcFServiceInfo>();
             boolean matched = false;
             for (NfcFServiceInfo validService : validServices) {
                 for (NfcFServiceInfo cachedService : cachedServices) {
@@ -332,11 +333,9 @@
             // Update cache
             for (NfcFServiceInfo service : toBeAdded) {
                 userServices.services.put(service.getComponent(), service);
-                if (DBG) Log.d(TAG, "Added service: " + service.getComponent());
             }
             for (NfcFServiceInfo service : toBeRemoved) {
                 userServices.services.remove(service.getComponent());
-                if (DBG) Log.d(TAG, "Removed service: " + service.getComponent());
             }
             // Apply dynamic System Code mappings
             ArrayList<ComponentName> toBeRemovedDynamicSystemCode =
@@ -405,7 +404,16 @@
             newServices = new ArrayList<NfcFServiceInfo>(userServices.services.values());
         }
         mCallback.onNfcFServicesUpdated(userId, Collections.unmodifiableList(newServices));
-        if (DBG) dump(newServices);
+        if (VDBG) {
+            Log.i(TAG, "Services => ");
+            dump(newServices);
+        } else {
+            // dump only new services added or removed
+            Log.i(TAG, "New Services => ");
+            dump(toBeAdded);
+            Log.i(TAG, "Removed Services => ");
+            dump(toBeRemoved);
+        }
     }
 
     private void readDynamicSystemCodeNfcid2Locked() {
diff --git a/src/com/android/nfc/cardemulation/RegisteredServicesCache.java b/src/com/android/nfc/cardemulation/RegisteredServicesCache.java
index ce69f08..368127d 100644
--- a/src/com/android/nfc/cardemulation/RegisteredServicesCache.java
+++ b/src/com/android/nfc/cardemulation/RegisteredServicesCache.java
@@ -16,6 +16,8 @@
 
 package com.android.nfc.cardemulation;
 
+import android.annotation.TargetApi;
+import android.annotation.FlaggedApi;
 import android.app.ActivityManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -36,15 +38,17 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.sysprop.NfcProperties;
+import android.text.TextUtils;
 import android.util.AtomicFile;
 import android.util.Log;
 import android.util.SparseArray;
 import android.util.Xml;
 import android.util.proto.ProtoOutputStream;
 
+import androidx.annotation.VisibleForTesting;
+
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.FastXmlSerializer;
-import com.android.internal.util.XmlUtils;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -53,8 +57,10 @@
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
+import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -74,7 +80,11 @@
 public class RegisteredServicesCache {
     static final String XML_INDENT_OUTPUT_FEATURE = "http://xmlpull.org/v1/doc/features.html#indent-output";
     static final String TAG = "RegisteredServicesCache";
-    static final boolean DEBUG = NfcProperties.debug_enabled().orElse(false);
+    static final String AID_XML_PATH = "dynamic_aids.xml";
+    static final String OTHER_STATUS_PATH = "other_status.xml";
+    static final String PACKAGE_DATA = "package";
+    static final boolean DEBUG = NfcProperties.debug_enabled().orElse(true);
+    private static final boolean VDBG = false; // turn on for local testing.
 
     final Context mContext;
     final AtomicReference<BroadcastReceiver> mReceiver;
@@ -89,8 +99,10 @@
     // mUserServices holds the card emulation services that are running for each user
     final SparseArray<UserServices> mUserServices = new SparseArray<UserServices>();
     final Callback mCallback;
-    final AtomicFile mDynamicSettingsFile;
-    final AtomicFile mOthersFile;
+    final SettingsFile mDynamicSettingsFile;
+    final SettingsFile mOthersFile;
+    final ServiceParser mServiceParser;
+
     public interface Callback {
         /**
          * ServicesUpdated for specific userId.
@@ -103,7 +115,7 @@
         public final int uid;
         public final HashMap<String, AidGroup> aidGroups = new HashMap<>();
         public String offHostSE;
-        public boolean defaultToObserveMode = false;
+        public String shouldDefaultToObserveModeStr;
 
         DynamicSettings(int uid) {
             this.uid = uid;
@@ -120,7 +132,8 @@
         }
     };
 
-    private static class UserServices {
+    @VisibleForTesting
+    static class UserServices {
         /**
          * All services that have registered
          */
@@ -132,6 +145,60 @@
                 new HashMap<>();
     };
 
+    @VisibleForTesting
+    static class SettingsFile {
+        final AtomicFile mFile;
+        SettingsFile(Context context, String path) {
+            File dir = context.getFilesDir();
+            mFile = new AtomicFile(new File(dir, path));
+        }
+
+        boolean exists() {
+            return mFile.getBaseFile().exists();
+        }
+
+        InputStream openRead() throws FileNotFoundException {
+            return mFile.openRead();
+        }
+
+        void delete() {
+            mFile.delete();
+        }
+
+        FileOutputStream startWrite() throws IOException {
+            return mFile.startWrite();
+        }
+
+        void finishWrite(FileOutputStream fileOutputStream) {
+            mFile.finishWrite(fileOutputStream);
+        }
+
+        void failWrite(FileOutputStream fileOutputStream) {
+            mFile.failWrite(fileOutputStream);
+        }
+
+        File getBaseFile() {
+            return mFile.getBaseFile();
+        }
+    }
+
+    @VisibleForTesting
+    interface ServiceParser {
+        ApduServiceInfo parseApduService(PackageManager packageManager,
+                                         ResolveInfo resolveInfo,
+                                         boolean onHost) throws XmlPullParserException, IOException;
+    }
+
+    private static class RealServiceParser implements ServiceParser {
+
+        @Override
+        public ApduServiceInfo parseApduService(PackageManager packageManager,
+                                                ResolveInfo resolveInfo, boolean onHost)
+                throws XmlPullParserException, IOException {
+            return new ApduServiceInfo(packageManager, resolveInfo, onHost);
+        }
+    }
+
     private UserServices findOrCreateUserLocked(int userId) {
         UserServices services = mUserServices.get(userId);
         if (services == null) {
@@ -150,8 +217,16 @@
     }
 
     public RegisteredServicesCache(Context context, Callback callback) {
+        this(context, callback, new SettingsFile(context, AID_XML_PATH),
+                new SettingsFile(context, OTHER_STATUS_PATH), new RealServiceParser());
+    }
+
+    @VisibleForTesting
+    RegisteredServicesCache(Context context, Callback callback, SettingsFile dynamicSettings,
+                            SettingsFile otherSettings, ServiceParser serviceParser) {
         mContext = context;
         mCallback = callback;
+        mServiceParser = serviceParser;
 
         refreshUserProfilesLocked();
 
@@ -160,7 +235,7 @@
             public void onReceive(Context context, Intent intent) {
                 final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
                 String action = intent.getAction();
-                if (DEBUG) Log.d(TAG, "Intent action: " + action);
+                if (VDBG) Log.d(TAG, "Intent action: " + action);
 
                 if (RoutingOptionManager.getInstance().isRoutingTableOverrided()) {
                     if (DEBUG) Log.d(TAG, "Routing table overrided. Skip invalidateCache()");
@@ -169,7 +244,7 @@
                 if (uid != -1) {
                     boolean replaced = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false) &&
                             (Intent.ACTION_PACKAGE_ADDED.equals(action) ||
-                             Intent.ACTION_PACKAGE_REMOVED.equals(action));
+                                    Intent.ACTION_PACKAGE_REMOVED.equals(action));
                     if (!replaced) {
                         int currentUser = ActivityManager.getCurrentUser();
                         if (currentUser == getProfileParentId(UserHandle.
@@ -199,7 +274,7 @@
         intentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
         intentFilter.addAction(Intent.ACTION_PACKAGE_FIRST_LAUNCH);
         intentFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
-        intentFilter.addDataScheme("package");
+        intentFilter.addDataScheme(PACKAGE_DATA);
         mContext.registerReceiverForAllUsers(mReceiver.get(), intentFilter, null, null);
 
         // Register for events related to sdcard operations
@@ -208,9 +283,8 @@
         sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
         mContext.registerReceiverForAllUsers(mReceiver.get(), sdFilter, null, null);
 
-        File dataDir = mContext.getFilesDir();
-        mDynamicSettingsFile = new AtomicFile(new File(dataDir, "dynamic_aids.xml"));
-        mOthersFile = new AtomicFile(new File(dataDir, "other_status.xml"));
+        mDynamicSettingsFile = dynamicSettings;
+        mOthersFile = otherSettings;
     }
 
     void initialize() {
@@ -250,12 +324,18 @@
         mUserHandles.removeAll(removeUserHandles);
     }
 
-    void dump(ArrayList<ApduServiceInfo> services) {
+    void dump(List<ApduServiceInfo> services) {
         for (ApduServiceInfo service : services) {
             if (DEBUG) Log.d(TAG, service.toString());
         }
     }
 
+    void dump(ArrayList<ComponentName> services) {
+        for (ComponentName service : services) {
+            if (DEBUG) Log.d(TAG, service.toString());
+        }
+    }
+
     boolean containsServiceLocked(ArrayList<ApduServiceInfo> services, ComponentName serviceName) {
         for (ApduServiceInfo service : services) {
             if (service.getComponent().equals(serviceName)) return true;
@@ -314,7 +394,6 @@
                 new Intent(OffHostApduService.SERVICE_INTERFACE),
                 ResolveInfoFlags.of(PackageManager.GET_META_DATA), UserHandle.of(userId));
         resolvedServices.addAll(resolvedOffHostServices);
-
         for (ResolveInfo resolvedService : resolvedServices) {
             try {
                 boolean onHost = !resolvedOffHostServices.contains(resolvedService);
@@ -341,7 +420,8 @@
                             android.Manifest.permission.BIND_NFC_SERVICE);
                     continue;
                 }
-                ApduServiceInfo service = new ApduServiceInfo(pm, resolvedService, onHost);
+                ApduServiceInfo service = mServiceParser.parseApduService(pm, resolvedService,
+                        onHost);
                 if (service != null) {
                     validServices.add(service);
                 }
@@ -363,6 +443,8 @@
         if (validServices == null) {
             return;
         }
+        ArrayList<ApduServiceInfo> toBeAdded = new ArrayList<>();
+        ArrayList<ApduServiceInfo> toBeRemoved = new ArrayList<>();
         synchronized (mLock) {
             UserServices userServices = findOrCreateUserLocked(userId);
 
@@ -373,18 +455,17 @@
                 Map.Entry<ComponentName, ApduServiceInfo> entry =
                         (Map.Entry<ComponentName, ApduServiceInfo>) it.next();
                 if (!containsServiceLocked(validServices, entry.getKey())) {
-                    Log.d(TAG, "Service removed: " + entry.getKey());
+                    toBeRemoved.add(entry.getValue());
                     it.remove();
                 }
             }
             for (ApduServiceInfo service : validServices) {
-                if (DEBUG) Log.d(TAG, "Adding service: " + service.getComponent() +
-                        " AIDs: " + service.getAids());
+                toBeAdded.add(service);
                 userServices.services.put(service.getComponent(), service);
             }
 
             // Apply dynamic settings mappings
-            ArrayList<ComponentName> toBeRemoved = new ArrayList<ComponentName>();
+            ArrayList<ComponentName> toBeRemovedComponent = new ArrayList<ComponentName>();
             for (Map.Entry<ComponentName, DynamicSettings> entry :
                     userServices.dynamicSettings.entrySet()) {
                 // Verify component / uid match
@@ -392,7 +473,7 @@
                 DynamicSettings dynamicSettings = entry.getValue();
                 ApduServiceInfo serviceInfo = userServices.services.get(component);
                 if (serviceInfo == null || (serviceInfo.getUid() != dynamicSettings.uid)) {
-                    toBeRemoved.add(component);
+                    toBeRemovedComponent.add(component);
                     continue;
                 } else {
                     for (AidGroup group : dynamicSettings.aidGroups.values()) {
@@ -401,10 +482,15 @@
                     if (dynamicSettings.offHostSE != null) {
                         serviceInfo.setOffHostSecureElement(dynamicSettings.offHostSE);
                     }
+                    if (dynamicSettings.shouldDefaultToObserveModeStr != null) {
+                        serviceInfo.setShouldDefaultToObserveMode(
+                                convertValueToBoolean(dynamicSettings.shouldDefaultToObserveModeStr,
+                                false));
+                    }
                 }
             }
             if (toBeRemoved.size() > 0) {
-                for (ComponentName component : toBeRemoved) {
+                for (ComponentName component : toBeRemovedComponent) {
                     Log.d(TAG, "Removing dynamic AIDs registered by " + component);
                     userServices.dynamicSettings.remove(component);
                 }
@@ -419,11 +505,22 @@
 
         mCallback.onServicesUpdated(userId, Collections.unmodifiableList(validServices),
                 validateInstalled);
-        dump(validServices);
+        if (VDBG) {
+            Log.i(TAG, "Services => ");
+            dump(validServices);
+        } else {
+            // dump only new services added or removed
+            Log.i(TAG, "New Services => ");
+            dump(toBeAdded);
+            Log.i(TAG, "Removed Services => ");
+            dump(toBeRemoved);
+        }
     }
 
     private void invalidateOther(int userId, List<ApduServiceInfo> validOtherServices) {
         Log.d(TAG, "invalidate : " + userId);
+        ArrayList<ComponentName> toBeAdded = new ArrayList<>();
+        ArrayList<ComponentName> toBeRemoved = new ArrayList<>();
         // remove services
         synchronized (mLock) {
             UserServices userServices = findOrCreateUserLocked(userId);
@@ -435,7 +532,7 @@
                 Map.Entry<ComponentName, OtherServiceStatus> entry = it.next();
                 if (!containsServiceLocked((ArrayList<ApduServiceInfo>) validOtherServices,
                         entry.getKey())) {
-                    Log.d(TAG, "Service removed: " + entry.getKey());
+                    toBeRemoved.add(entry.getKey());
                     needToWrite = true;
                     it.remove();
                 }
@@ -452,8 +549,10 @@
             isChecked = true;
 
             for (ApduServiceInfo service : validOtherServices) {
-                Log.d(TAG, "update valid otherService: " + service.getComponent()
-                        + " AIDs: " + service.getAids());
+                if (VDBG) {
+                    Log.d(TAG, "update valid otherService: " + service.getComponent()
+                            + " AIDs: " + service.getAids());
+                }
                 if (!service.hasCategory(CardEmulation.CATEGORY_OTHER)) {
                     Log.e(TAG, "service does not have other category");
                     continue;
@@ -463,11 +562,9 @@
                 OtherServiceStatus status = userServices.others.get(component);
 
                 if (status == null) {
-                    Log.d(TAG, "New other service");
+                    toBeAdded.add(service.getComponent());
                     status = new OtherServiceStatus(service.getUid(), isChecked);
                     needToWrite = true;
-                } else {
-                    Log.d(TAG, "Existed other service");
                 }
                 service.setCategoryOtherServiceEnabled(status.checked);
                 userServices.others.put(component, status);
@@ -477,11 +574,37 @@
                 writeOthersLocked();
             }
         }
+        if (VDBG) {
+            Log.i(TAG, "Other Services => ");
+            dump(validOtherServices);
+        } else {
+            // dump only new services added or removed
+            Log.i(TAG, "New Other Services => ");
+            dump(toBeAdded);
+            Log.i(TAG, "Removed Other Services => ");
+            dump(toBeRemoved);
+        }
     }
+
+    private static final boolean convertValueToBoolean(CharSequence value, boolean defaultValue) {
+       boolean result = false;
+
+        if (TextUtils.isEmpty(value)) {
+            return defaultValue;
+        }
+
+        if (value.equals("1")
+        ||  value.equals("true")
+        ||  value.equals("TRUE"))
+            result = true;
+
+        return result;
+    }
+
     private void readDynamicSettingsLocked() {
-        FileInputStream fis = null;
+        InputStream fis = null;
         try {
-            if (!mDynamicSettingsFile.getBaseFile().exists()) {
+            if (!mDynamicSettingsFile.exists()) {
                 Log.d(TAG, "Dynamic AIDs file does not exist.");
                 return;
             }
@@ -499,7 +622,7 @@
                 ComponentName currentComponent = null;
                 int currentUid = -1;
                 String currentOffHostSE = null;
-                boolean defaultToObserveMode = false;
+                String shouldDefaultToObserveModeStr = null;
                 ArrayList<AidGroup> currentGroups = new ArrayList<AidGroup>();
                 while (eventType != XmlPullParser.END_DOCUMENT) {
                     tagName = parser.getName();
@@ -508,8 +631,8 @@
                             String compString = parser.getAttributeValue(null, "component");
                             String uidString = parser.getAttributeValue(null, "uid");
                             String offHostString = parser.getAttributeValue(null, "offHostSE");
-                            String defaultToObserveModeStr =
-                                parser.getAttributeValue(null, "defaultToObserveMode");
+                            shouldDefaultToObserveModeStr =
+                                parser.getAttributeValue(null, "shouldDefaultToObserveMode");
                             if (compString == null || uidString == null) {
                                 Log.e(TAG, "Invalid service attributes");
                             } else {
@@ -518,9 +641,6 @@
                                     currentComponent = ComponentName.unflattenFromString(compString);
                                     currentOffHostSE = offHostString;
                                     inService = true;
-                                    defaultToObserveMode =
-                                        XmlUtils.convertValueToBoolean(defaultToObserveModeStr,
-                                        false);
                                 } catch (NumberFormatException e) {
                                     Log.e(TAG, "Could not parse service uid");
                                 }
@@ -541,12 +661,13 @@
                                     (currentGroups.size() > 0 || currentOffHostSE != null)) {
                                 final int userId = UserHandle.
                                         getUserHandleForUid(currentUid).getIdentifier();
+                                Log.d(TAG, " ## user id - " + userId);
                                 DynamicSettings dynSettings = new DynamicSettings(currentUid);
                                 for (AidGroup group : currentGroups) {
                                     dynSettings.aidGroups.put(group.getCategory(), group);
                                 }
                                 dynSettings.offHostSE = currentOffHostSE;
-                                dynSettings.defaultToObserveMode = defaultToObserveMode;
+                                dynSettings.shouldDefaultToObserveModeStr = shouldDefaultToObserveModeStr;
                                 UserServices services = findOrCreateUserLocked(userId);
                                 services.dynamicSettings.put(currentComponent, dynSettings);
                             }
@@ -561,7 +682,7 @@
                 };
             }
         } catch (Exception e) {
-            Log.e(TAG, "Could not parse dynamic AIDs file, trashing.");
+            Log.e(TAG, "Could not parse dynamic AIDs file, trashing.", e);
             mDynamicSettingsFile.delete();
         } finally {
             if (fis != null) {
@@ -576,9 +697,9 @@
     private void readOthersLocked() {
         Log.d(TAG, "read others locked");
 
-        FileInputStream fis = null;
+        InputStream fis = null;
         try {
-            if (!mOthersFile.getBaseFile().exists()) {
+            if (!mOthersFile.exists()) {
                 Log.d(TAG, "Dynamic AIDs file does not exist.");
                 return;
             }
@@ -621,7 +742,8 @@
                             // See if we have a valid service
                             if (currentComponent != null && currentUid >= 0) {
                                 Log.d(TAG, " end of service tag");
-                                final int userId = UserHandle.getUserId(currentUid);
+                                final int userId =
+                                    UserHandle.getUserHandleForUid(currentUid).getIdentifier();
                                 OtherServiceStatus status =
                                         new OtherServiceStatus(currentUid, checked);
                                 Log.d(TAG, " ## user id - " + userId);
@@ -637,7 +759,7 @@
                 }
             }
         } catch (Exception e) {
-            Log.e(TAG, "Could not parse others AIDs file, trashing.");
+            Log.e(TAG, "Could not parse others AIDs file, trashing.", e);
             mOthersFile.delete();
         } finally {
             if (fis != null) {
@@ -668,8 +790,10 @@
                     if(service.getValue().offHostSE != null) {
                         out.attribute(null, "offHostSE", service.getValue().offHostSE);
                     }
-                    out.attribute(null,"defaultToObserveMode",
-                        Boolean.toString(service.getValue().defaultToObserveMode));
+                    if (service.getValue().shouldDefaultToObserveModeStr != null) {
+                        out.attribute(null, "shouldDefaultToObserveMode",
+                                service.getValue().shouldDefaultToObserveModeStr);
+                    }
                     for (AidGroup group : service.getValue().aidGroups.values()) {
                         group.writeAsXml(out);
                     }
@@ -740,7 +864,7 @@
             mOthersFile.finishWrite(fos);
             return true;
         } catch (Exception e) {
-            Log.e(TAG, "Error writing dynamic AIDs", e);
+            Log.e(TAG, "Error writing other status", e);
             if (fos != null) {
                 mOthersFile.failWrite(fos);
             }
@@ -833,7 +957,7 @@
         return true;
     }
 
-    public boolean setServiceObserveModeDefault(int userId, int uid,
+    public boolean setShouldDefaultToObserveModeForService(int userId, int uid,
             ComponentName componentName, boolean enable) {
         synchronized (mLock) {
             UserServices services = findOrCreateUserLocked(userId);
@@ -851,17 +975,136 @@
                 Log.e(TAG, "UID mismatch.");
                 return false;
             }
+            serviceInfo.setShouldDefaultToObserveMode(enable);
             DynamicSettings dynSettings = services.dynamicSettings.get(componentName);
             if (dynSettings == null) {
                 dynSettings = new DynamicSettings(uid);
                 dynSettings.offHostSE = null;
                 services.dynamicSettings.put(componentName, dynSettings);
             }
-            dynSettings.defaultToObserveMode = enable;
+            dynSettings.shouldDefaultToObserveModeStr =  Boolean.toString(enable);
         }
         return true;
     }
 
+    @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+    public boolean registerPollingLoopFilterForService(int userId, int uid,
+            ComponentName componentName, String pollingLoopFilter,
+            boolean autoTransact) {
+        ArrayList<ApduServiceInfo> newServices = null;
+        synchronized (mLock) {
+            UserServices services = findOrCreateUserLocked(userId);
+            // Check if we can find this service
+            ApduServiceInfo serviceInfo = getService(userId, componentName);
+            if (serviceInfo == null) {
+                Log.e(TAG, "Service " + componentName + " does not exist.");
+                return false;
+            }
+            if (serviceInfo.getUid() != uid) {
+                // This is probably a good indication something is wrong here.
+                // Either newer service installed with different uid (but then
+                // we should have known about it), or somebody calling us from
+                // a different uid.
+                Log.e(TAG, "UID mismatch.");
+                return false;
+            }
+            if (!serviceInfo.isOnHost() && !autoTransact) {
+                return false;
+            }
+            serviceInfo.addPollingLoopFilter(pollingLoopFilter, autoTransact);
+            newServices = new ArrayList<ApduServiceInfo>(services.services.values());
+        }
+        mCallback.onServicesUpdated(userId, newServices, true);
+        return true;
+    }
+
+    @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+    public boolean removePollingLoopFilterForService(int userId, int uid,
+            ComponentName componentName, String pollingLoopFilter) {
+        ArrayList<ApduServiceInfo> newServices = null;
+        synchronized (mLock) {
+            UserServices services = findOrCreateUserLocked(userId);
+            // Check if we can find this service
+            ApduServiceInfo serviceInfo = getService(userId, componentName);
+            if (serviceInfo == null) {
+                Log.e(TAG, "Service " + componentName + " does not exist.");
+                return false;
+            }
+            if (serviceInfo.getUid() != uid) {
+                // This is probably a good indication something is wrong here.
+                // Either newer service installed with different uid (but then
+                // we should have known about it), or somebody calling us from
+                // a different uid.
+                Log.e(TAG, "UID mismatch.");
+                return false;
+            }
+            serviceInfo.removePollingLoopFilter(pollingLoopFilter);
+            newServices = new ArrayList<ApduServiceInfo>(services.services.values());
+        }
+        mCallback.onServicesUpdated(userId, newServices, true);
+        return true;
+    }
+
+    @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+    public boolean registerPollingLoopPatternFilterForService(int userId, int uid,
+            ComponentName componentName, String pollingLoopPatternFilter,
+            boolean autoTransact) {
+        ArrayList<ApduServiceInfo> newServices = null;
+        synchronized (mLock) {
+            UserServices services = findOrCreateUserLocked(userId);
+            // Check if we can find this service
+            ApduServiceInfo serviceInfo = getService(userId, componentName);
+            if (serviceInfo == null) {
+                Log.e(TAG, "Service " + componentName + " does not exist.");
+                return false;
+            }
+            if (serviceInfo.getUid() != uid) {
+                // This is probably a good indication something is wrong here.
+                // Either newer service installed with different uid (but then
+                // we should have known about it), or somebody calling us from
+                // a different uid.
+                Log.e(TAG, "UID mismatch.");
+                return false;
+            }
+            if (!serviceInfo.isOnHost() && !autoTransact) {
+                return false;
+            }
+            serviceInfo.addPollingLoopPatternFilter(pollingLoopPatternFilter, autoTransact);
+            newServices = new ArrayList<ApduServiceInfo>(services.services.values());
+        }
+        mCallback.onServicesUpdated(userId, newServices, true);
+        return true;
+    }
+
+    @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+    public boolean removePollingLoopPatternFilterForService(int userId, int uid,
+            ComponentName componentName, String pollingLoopPatternFilter) {
+        ArrayList<ApduServiceInfo> newServices = null;
+        synchronized (mLock) {
+            UserServices services = findOrCreateUserLocked(userId);
+            // Check if we can find this service
+            ApduServiceInfo serviceInfo = getService(userId, componentName);
+            if (serviceInfo == null) {
+                Log.e(TAG, "Service " + componentName + " does not exist.");
+                return false;
+            }
+            if (serviceInfo.getUid() != uid) {
+                // This is probably a good indication something is wrong here.
+                // Either newer service installed with different uid (but then
+                // we should have known about it), or somebody calling us from
+                // a different uid.
+                Log.e(TAG, "UID mismatch.");
+                return false;
+            }
+            serviceInfo.removePollingLoopPatternFilter(pollingLoopPatternFilter);
+            newServices = new ArrayList<ApduServiceInfo>(services.services.values());
+        }
+        mCallback.onServicesUpdated(userId, newServices, true);
+        return true;
+    }
+
+
+
     public boolean registerAidGroupForService(int userId, int uid,
             ComponentName componentName, AidGroup aidGroup) {
         ArrayList<ApduServiceInfo> newServices = null;
@@ -1008,10 +1251,14 @@
         return success;
     }
 
-    boolean doesServiceDefaultToObserveMode(int userId, ComponentName service) {
+    boolean doesServiceShouldDefaultToObserveMode(int userId, ComponentName service) {
         UserServices services = findOrCreateUserLocked(userId);
-        DynamicSettings dynSettings = services.dynamicSettings.get(service);
-        return dynSettings != null && dynSettings.defaultToObserveMode;
+        ApduServiceInfo serviceInfo = services.services.get(service);
+        if (serviceInfo == null) {
+            Log.d(TAG, "serviceInfo is null");
+            return false;
+        }
+        return serviceInfo.shouldDefaultToObserveMode();
     }
 
     private boolean updateOtherServiceStatus(int userId, ApduServiceInfo service, boolean checked) {
diff --git a/src/com/android/nfc/cardemulation/RegisteredT3tIdentifiersCache.java b/src/com/android/nfc/cardemulation/RegisteredT3tIdentifiersCache.java
index 78923f4..e4188ed 100644
--- a/src/com/android/nfc/cardemulation/RegisteredT3tIdentifiersCache.java
+++ b/src/com/android/nfc/cardemulation/RegisteredT3tIdentifiersCache.java
@@ -38,7 +38,7 @@
 public class RegisteredT3tIdentifiersCache {
     static final String TAG = "RegisteredT3tIdentifiersCache";
 
-    static final boolean DBG = NfcProperties.debug_enabled().orElse(false);
+    static final boolean DBG = NfcProperties.debug_enabled().orElse(true);
 
     // All NFC-F services that have registered
     final Map<Integer, List<NfcFServiceInfo>> mUserNfcFServiceInfo =
diff --git a/src/com/android/nfc/cardemulation/SystemCodeRoutingManager.java b/src/com/android/nfc/cardemulation/SystemCodeRoutingManager.java
index f1b92bb..d530cf9 100644
--- a/src/com/android/nfc/cardemulation/SystemCodeRoutingManager.java
+++ b/src/com/android/nfc/cardemulation/SystemCodeRoutingManager.java
@@ -31,7 +31,7 @@
 public class SystemCodeRoutingManager {
     static final String TAG = "SystemCodeRoutingManager";
 
-    static final boolean DBG = NfcProperties.debug_enabled().orElse(false);
+    static final boolean DBG = NfcProperties.debug_enabled().orElse(true);
 
     final Object mLock = new Object();
 
diff --git a/src/com/android/nfc/cardemulation/WalletRoleObserver.java b/src/com/android/nfc/cardemulation/WalletRoleObserver.java
new file mode 100644
index 0000000..00c8fed
--- /dev/null
+++ b/src/com/android/nfc/cardemulation/WalletRoleObserver.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.nfc.cardemulation;
+
+import android.app.role.OnRoleHoldersChangedListener;
+import android.app.role.RoleManager;
+import android.content.Context;
+import android.os.Binder;
+import android.os.UserHandle;
+import android.permission.flags.Flags;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.List;
+
+public class WalletRoleObserver {
+
+    public interface Callback {
+        void onWalletRoleHolderChanged(String holder, int userId);
+    }
+    private Context mContext;
+    private RoleManager mRoleManager;
+    @VisibleForTesting
+    final OnRoleHoldersChangedListener mOnRoleHoldersChangedListener;
+    private Callback mCallback;
+
+    public WalletRoleObserver(Context context, RoleManager roleManager,
+            Callback callback) {
+        this.mContext = context;
+        this.mRoleManager = roleManager;
+        this.mCallback = callback;
+        this.mOnRoleHoldersChangedListener = (roleName, user) -> {
+            if (!roleName.equals(RoleManager.ROLE_WALLET)) {
+                return;
+            }
+            List<String> roleHolders = roleManager.getRoleHolders(RoleManager.ROLE_WALLET);
+            String roleHolder = roleHolders.isEmpty() ? null : roleHolders.get(0);
+            callback.onWalletRoleHolderChanged(roleHolder, user.getIdentifier());
+        };
+        this.mRoleManager.addOnRoleHoldersChangedListenerAsUser(context.getMainExecutor(),
+                mOnRoleHoldersChangedListener, UserHandle.ALL);
+    }
+
+    public String getDefaultWalletRoleHolder(int userId) {
+        final long token = Binder.clearCallingIdentity();
+        try {
+            if (!mRoleManager.isRoleAvailable(RoleManager.ROLE_WALLET)) {
+                return null;
+            }
+            List<String> roleHolders = mRoleManager.getRoleHoldersAsUser(RoleManager.ROLE_WALLET,
+                    UserHandle.of(userId));
+            return roleHolders.isEmpty() ? null : roleHolders.get(0);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+     boolean isWalletRoleFeatureEnabled() {
+        final long token = Binder.clearCallingIdentity();
+        try {
+            return Flags.walletRoleEnabled();
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    public void onUserSwitched(int userId) {
+        String roleHolder = getDefaultWalletRoleHolder(userId);
+        mCallback.onWalletRoleHolderChanged(roleHolder, userId);
+    }
+}
diff --git a/src/com/android/nfc/cardemulation/util/StatsdUtils.java b/src/com/android/nfc/cardemulation/util/StatsdUtils.java
index cedd5cd..26a8b37 100644
--- a/src/com/android/nfc/cardemulation/util/StatsdUtils.java
+++ b/src/com/android/nfc/cardemulation/util/StatsdUtils.java
@@ -15,8 +15,14 @@
  */
 package com.android.nfc.cardemulation.util;
 
+import static com.android.nfc.NfcStatsLog.NFC_POLLING_LOOP_NOTIFICATION_REPORTED__PROPRIETARY_FRAME_TYPE__ECP_V1;
+import static com.android.nfc.NfcStatsLog.NFC_POLLING_LOOP_NOTIFICATION_REPORTED__PROPRIETARY_FRAME_TYPE__ECP_V2;
+import static com.android.nfc.NfcStatsLog.NFC_POLLING_LOOP_NOTIFICATION_REPORTED__PROPRIETARY_FRAME_TYPE__PROPRIETARY_FRAME_UNKNOWN;
+
 import android.annotation.FlaggedApi;
 import android.nfc.cardemulation.CardEmulation;
+import android.nfc.cardemulation.PollingFrame;
+import android.os.Bundle;
 import android.os.SystemClock;
 import android.sysprop.NfcProperties;
 import android.util.Log;
@@ -24,9 +30,12 @@
 import com.android.nfc.NfcStatsLog;
 import com.android.nfc.flags.Flags;
 
+import java.util.HashMap;
+import java.util.Objects;
+
 @FlaggedApi(Flags.FLAG_STATSD_CE_EVENTS_FLAG)
 public class StatsdUtils {
-    static final boolean DBG = NfcProperties.debug_enabled().orElse(false);
+    static final boolean DBG = NfcProperties.debug_enabled().orElse(true);
     private final String TAG = "StatsdUtils";
 
     public static final String SE_NAME_HCE = "HCE";
@@ -77,6 +86,14 @@
     /** Current transaction's uid to log in statsd */
     private int mTransactionUid = -1;
 
+    private static final byte FRAME_HEADER_ECP = 0x6A;
+    private static final byte FRAME_ECP_V1 = 0x01;
+    private static final byte FRAME_ECP_V2 = 0x02;
+    private static final int FRAME_ECP_MIN_SIZE = 5;
+
+    private static final int NO_GAIN_INFORMATION = -1;
+    private int mLastGainLevel = NO_GAIN_INFORMATION;
+
     /** Result constants for statsd usage */
     static enum StatsdResult {
         SUCCESS,
@@ -249,4 +266,108 @@
         };
         logCardEmulationEvent(statsdCategory);
     }
+
+    public void logFieldChanged(boolean isOn, int fieldStrength) {
+        NfcStatsLog.write(NfcStatsLog.NFC_FIELD_CHANGED,
+                isOn ? NfcStatsLog.NFC_FIELD_CHANGED__FIELD_STATUS__FIELD_ON
+                : NfcStatsLog.NFC_FIELD_CHANGED__FIELD_STATUS__FIELD_OFF, fieldStrength);
+
+        if (!isOn) {
+            mLastGainLevel = NO_GAIN_INFORMATION;
+        }
+    }
+
+    public void logObserveModeStateChanged(boolean enabled, int triggerSource, int latency) {
+        NfcStatsLog.write(NfcStatsLog.NFC_OBSERVE_MODE_STATE_CHANGED,
+                enabled ? NfcStatsLog.NFC_OBSERVE_MODE_STATE_CHANGED__STATE__OBSERVE_MODE_ENABLED
+                        : NfcStatsLog.NFC_OBSERVE_MODE_STATE_CHANGED__STATE__OBSERVE_MODE_DISABLED,
+                triggerSource, latency);
+    }
+
+    private final HashMap<String, PollingFrameLog> pollingFrameMap = new HashMap<>();
+
+    public void tallyPollingFrame(String frameDataHex, PollingFrame frame) {
+        int type = frame.getType();
+
+        int gainLevel = frame.getVendorSpecificGain();
+        if (gainLevel != -1) {
+            if (mLastGainLevel != gainLevel) {
+                logFieldChanged(true, gainLevel);
+                mLastGainLevel = gainLevel;
+            }
+        }
+
+        if (type == PollingFrame.POLLING_LOOP_TYPE_UNKNOWN) {
+            byte[] data = frame.getData();
+
+            PollingFrameLog log = pollingFrameMap.getOrDefault(frameDataHex, null);
+
+            if (log == null) {
+                PollingFrameLog frameLog = new PollingFrameLog(data);
+
+                pollingFrameMap.put(frameDataHex, frameLog);
+            } else {
+                log.repeatCount++;
+            }
+        }
+    }
+
+    public void logPollingFrames() {
+        for (PollingFrameLog log : pollingFrameMap.values()) {
+            writeToStatsd(log);
+        }
+        pollingFrameMap.clear();
+    }
+
+    protected static int getFrameType(byte[] data) {
+        int frameType =
+          NFC_POLLING_LOOP_NOTIFICATION_REPORTED__PROPRIETARY_FRAME_TYPE__PROPRIETARY_FRAME_UNKNOWN;
+
+        if (data != null && data.length >= FRAME_ECP_MIN_SIZE && data[0] == FRAME_HEADER_ECP) {
+            frameType = switch (data[1]) {
+                case FRAME_ECP_V1 ->
+                        NFC_POLLING_LOOP_NOTIFICATION_REPORTED__PROPRIETARY_FRAME_TYPE__ECP_V1;
+                case FRAME_ECP_V2 ->
+                        NFC_POLLING_LOOP_NOTIFICATION_REPORTED__PROPRIETARY_FRAME_TYPE__ECP_V2;
+                default -> frameType;
+            };
+        }
+        return frameType;
+    }
+
+    protected void writeToStatsd(PollingFrameLog frameLog) {
+        NfcStatsLog.write(NfcStatsLog.NFC_POLLING_LOOP_NOTIFICATION_REPORTED,
+                frameLog.frameType,
+                frameLog.repeatCount);
+    }
+
+    protected static class PollingFrameLog {
+        int repeatCount = 1;
+        final int frameType;
+
+        public PollingFrameLog(byte[] data) {
+            frameType = getFrameType(data);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (!(o instanceof PollingFrameLog)) return false;
+            PollingFrameLog that = (PollingFrameLog) o;
+            return repeatCount == that.repeatCount && frameType == that.frameType;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(repeatCount, frameType);
+        }
+
+        @Override
+        public String toString() {
+            return "PollingFrameLog{" +
+                    "repeatCount=" + repeatCount +
+                    ", frameType=" + frameType +
+                    '}';
+        }
+    }
 }
diff --git a/src/com/android/nfc/handover/BluetoothPeripheralHandover.java b/src/com/android/nfc/handover/BluetoothPeripheralHandover.java
index 3540499..c41aec1 100644
--- a/src/com/android/nfc/handover/BluetoothPeripheralHandover.java
+++ b/src/com/android/nfc/handover/BluetoothPeripheralHandover.java
@@ -55,7 +55,7 @@
  */
 public class BluetoothPeripheralHandover implements BluetoothProfile.ServiceListener {
     static final String TAG = "BluetoothPeripheralHandover";
-    static final boolean DBG = NfcProperties.debug_enabled().orElse(false);
+    static final boolean DBG = NfcProperties.debug_enabled().orElse(true);
 
     static final String ACTION_ALLOW_CONNECT = "com.android.nfc.handover.action.ALLOW_CONNECT";
     static final String ACTION_DENY_CONNECT = "com.android.nfc.handover.action.DENY_CONNECT";
diff --git a/src/com/android/nfc/handover/HandoverDataParser.java b/src/com/android/nfc/handover/HandoverDataParser.java
index 5853047..8363f75 100644
--- a/src/com/android/nfc/handover/HandoverDataParser.java
+++ b/src/com/android/nfc/handover/HandoverDataParser.java
@@ -42,7 +42,7 @@
 public class HandoverDataParser {
     private static final String TAG = "NfcHandover";
     private static final boolean DBG =
-            NfcProperties.debug_enabled().orElse(false);
+            NfcProperties.debug_enabled().orElse(true);
 
     private static final byte[] TYPE_BT_OOB = "application/vnd.bluetooth.ep.oob"
             .getBytes(StandardCharsets.US_ASCII);
diff --git a/src/com/android/nfc/wlc/NfcCharging.java b/src/com/android/nfc/wlc/NfcCharging.java
new file mode 100644
index 0000000..8b063ca
--- /dev/null
+++ b/src/com/android/nfc/wlc/NfcCharging.java
@@ -0,0 +1,1083 @@
+/*
+ *  Copyright (C) 2023 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.
+ *
+ *  Provide extensions for the implementation of the Nfc Charging
+ */
+
+package com.android.nfc.wlc;
+
+import android.app.Activity;
+import android.content.Context;
+import android.nfc.NdefMessage;
+import android.nfc.NdefRecord;
+import android.nfc.NfcAdapter;
+import android.os.Bundle;
+import android.sysprop.NfcProperties;
+import android.util.Log;
+
+import com.android.nfc.DeviceHost;
+import com.android.nfc.DeviceHost.TagEndpoint;
+import com.android.nfc.NfcService;
+
+import java.math.*;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+public class NfcCharging {
+    static final boolean DBG = NfcProperties.debug_enabled().orElse(true);
+    private static final String TAG = "NfcWlcChargingActivity";
+
+    static final String VERSION = "1.0.0";
+
+    private Context mContext;
+    public static final byte[] WLCCAP = {0x57, 0x4c, 0x43, 0x43, 0x41, 0x50};
+    public static final byte[] WLCCTL = {0x57, 0x4c, 0x43, 0x43, 0x54, 0x4C};
+    public static final byte[] WLCSTAI = {0x57, 0x4c, 0x43, 0x53, 0x54, 0x41, 0x49};
+    public static final byte[] USIWLC = {0x75, 0x73, 0x69, 0x3A, 0x77, 0x6C, 0x63};
+    public static final byte[] WLCPI = {0x57, 0x4c, 0x43, 0x49, 0x4e, 0x46};
+
+    public static final String BatteryLevel = "Battery Level";
+    public static final String ReceivePower = "Receive Power";
+    public static final String ReceiveVoltage = "Receive Voltage";
+    public static final String ReceiveCurrent = "Receive Current";
+    public static final String TemperatureBattery = "Temperature Battery";
+    public static final String TemperatureListener = "Temperature Listener";
+    public static final String VendorId = "Vendor Id";
+    public static final String State = "State";
+    public static final int DISCONNECTED = 0;
+    public static final int CONNECTED_NOT_CHARGING = 1;
+    public static final int CONNECTED_CHARGING = 2;
+
+    static final byte MODE_REQ_STATIC = 0;
+    static final byte MODE_REQ_NEGOTIATED = 1;
+    static final byte MODE_REQ_BATTERY_FULL = 2;
+
+    static final int MODE_NON_AUTONOMOUS_WLCP = 0;
+
+    int mWatchdogTimeout = 1;
+    int mUpdatedBatteryLevel = -1;
+    int mLastState = -1;
+
+    int WLCState = 0;
+
+    // WLCCAP
+    int WlcCap_ModeReq = 0;
+    int Nwt_max = 0;
+    int WlcCap_NegoWait = 0;
+    int WlcCap_RdConf = 0;
+    int TNdefRdWt = 0;
+
+    int WlcCap_NdefRdWt = 0;
+    int WlcCap_CapWt = 0;
+    int TCapWt = 0;
+    int WlcCap_NdefWrTo = 0;
+    int TNdefWrTo = 0;
+    int WlcCap_NdefWrWt = 0;
+    int TNdefWrWt = 0;
+
+    int mNwcc_retry = 0;
+    int mNretry = 0;
+
+    // WLCCTL
+    int WlcCtl_ErrorFlag = 0;
+    int WlcCtl_BatteryStatus = 0;
+    int mCnt = -1;
+    int WlcCtl_Cnt_new = 0;
+    int WlcCtl_WptReq = 0;
+    int WlcCtl_WptDuration = 0;
+    int TWptDuration = 0;
+    int WlcCtl_WptInfoReq = 0;
+    int WlcCtl_PowerAdjReq = 0;
+    int WlcCtl_BatteryLevel = 0xFF;
+    int WlcCtl_HoldOffWt = 0;
+    int THoldOffWt = 0;
+
+    int WlcCtl_ReceivePower = 0;
+    int WlcCtl_ReceiveVoltage = 0;
+    int WlcCtl_TemperatureBattery = 0;
+    int WlcCtl_TemperatureWlcl = 0;
+
+    // WLCINF
+    int Ptx = 100;
+
+    // state machine
+    private static final int STATE_2 = 0; // Read WLC_CAP
+    private static final int STATE_6 = 1; // Static WPT
+    private static final int STATE_8 = 2; // Handle NEGO_WAIT
+    private static final int STATE_11 = 3; // Write WLCP_INFO
+    private static final int STATE_12 = 4; // Read WLCL_CTL
+    private static final int STATE_16 = 5; // Read confirmation
+    private static final int STATE_17 = 6; // Check WPT requested
+    private static final int STATE_21 = 7; // Handle WPT
+    private static final int STATE_22 = 8; // Handle INFO_REQ
+    private static final int STATE_24 = 9; // Handle removal detection
+    private static final int STATE_21_1 = 10; // Handle WPT time completed
+    private static final int STATE_21_2 = 11; // Handle FOD detection/removal
+
+    private DeviceHost mNativeNfcManager;
+    NdefMessage mNdefMessage;
+    byte[] mNdefPayload;
+    byte[] mNdefPayload2;
+    byte[] mNdefType;
+    TagEndpoint TagHandler;
+
+    public boolean NfcChargingOnGoing = false;
+    public boolean NfcChargingMode = false;
+    public boolean WLCL_Presence = false;
+
+    public boolean mFirstOccurrence = true;
+
+    Map<String, Integer> WlcDeviceInfo = new HashMap<>();
+
+    private native boolean startWlcPowerTransfer(int power_adj_req, int wpt_time_int);
+
+    private native boolean enableWlc(int enable);
+
+    private PresenceCheckWatchdog mWatchdogWlc;
+
+    public NfcCharging(Context context, DeviceHost mDeviceHost) {
+        if (DBG) Log.d(TAG, "NfcCharging - Constructor");
+        mContext = context;
+        mNativeNfcManager = mDeviceHost;
+
+        resetInternalValues();
+
+        mNdefMessage = null;
+        mNdefPayload = null;
+        mNdefPayload2 = null;
+    }
+
+    private static final char[] hexArray = "0123456789ABCDEF".toCharArray();
+
+    public static String bytesToHex(byte[] bytes) {
+        char[] hexChars = new char[bytes.length * 2];
+        for (int j = 0; j < bytes.length; j++) {
+            int v = bytes[j] & 0xFF;
+            hexChars[j * 2] = hexArray[v >>> 4];
+            hexChars[j * 2 + 1] = hexArray[v & 0x0F];
+        }
+        return new String(hexChars);
+    }
+
+    public void resetInternalValues() {
+        if (DBG) Log.d(TAG, "resetInternalValues");
+        mCnt = -1;
+        mNretry = 0;
+        WlcCap_ModeReq = 0;
+        WlcCtl_BatteryLevel = -1;
+        WlcCtl_ReceivePower = -1;
+        WlcCtl_ReceiveVoltage = -1;
+        WlcCtl_TemperatureBattery = -1;
+        WlcCtl_TemperatureWlcl = -1;
+        mUpdatedBatteryLevel = -1;
+        mLastState = -1;
+        WlcDeviceInfo.put(BatteryLevel, -1);
+        WlcDeviceInfo.put(ReceivePower, -1);
+        WlcDeviceInfo.put(ReceiveVoltage, -1);
+        WlcDeviceInfo.put(ReceiveCurrent, -1);
+        WlcDeviceInfo.put(TemperatureBattery, -1);
+        WlcDeviceInfo.put(TemperatureListener, -1);
+        WlcDeviceInfo.put(VendorId, -1);
+        WlcDeviceInfo.put(State, -1);
+
+        WlcCtl_ErrorFlag = 0;
+        mFirstOccurrence = true;
+    }
+
+    DeviceHost.TagDisconnectedCallback callbackTagDisconnection =
+            new DeviceHost.TagDisconnectedCallback() {
+                @Override
+                public void onTagDisconnected(long handle) {
+                    Log.d(TAG, "onTagDisconnected");
+                    disconnectNfcCharging();
+                    WLCState = STATE_2;
+                    NfcChargingOnGoing = false;
+                    if (WLCL_Presence == true) {
+                        WLCL_Presence = false;
+                        if (DBG) Log.d(TAG, "Nfc Charging Listener lost");
+                    }
+                    NfcService.getInstance().sendScreenMessageAfterNfcCharging();
+                }
+            };
+
+    public boolean startNfcCharging(TagEndpoint t) {
+        if (DBG) Log.d(TAG, "startNfcCharging " + VERSION);
+        boolean NfcChargingEnabled = false;
+
+        TagHandler = t;
+        NfcChargingEnabled = enableWlc(MODE_NON_AUTONOMOUS_WLCP);
+        if (DBG) Log.d(TAG, "NfcChargingEnabled is " + NfcChargingEnabled);
+
+        if (NfcChargingEnabled) {
+            WLCL_Presence = true;
+            WLCState = STATE_2;
+            startNfcChargingPresenceChecking(50);
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    public void stopNfcCharging() {
+        if (DBG) Log.d(TAG, "stopNfcCharging " + VERSION);
+
+        NfcChargingOnGoing = false;
+        resetInternalValues();
+
+        mLastState = DISCONNECTED;
+        WlcDeviceInfo.put(State, mLastState);
+        NfcService.getInstance().onWlcData(WlcDeviceInfo);
+        disconnectPresenceCheck();
+
+        NfcChargingMode = false;
+
+        // Restart the polling loop
+
+        TagHandler.disconnect();
+        // Disable discovery and restart polling loop only if not screen state change pending
+        if (!NfcService.getInstance().sendScreenMessageAfterNfcCharging()) {
+            if (DBG) Log.d(TAG, "No pending screen state change, stop Nfc charging presence check");
+            stopNfcChargingPresenceChecking();
+        }
+    }
+
+    public boolean checkWlcCapMsg(NdefMessage ndefMsg) {
+        if (DBG) Log.d(TAG, "checkWlcCapMsg: enter");
+        boolean status = true;
+        NdefRecord[] ndefRecords = null;
+        long mDeviceId = 0;
+        int mVendorId = 0;
+        Byte ControlByte = 0;
+        if (ndefMsg != null) {
+            mNdefMessage = ndefMsg;
+            try {
+                ndefRecords = mNdefMessage.getRecords();
+                if (ndefRecords != null && ndefRecords.length > 0) {
+                    if (DBG)
+                        Log.d(TAG, "checkWlcCapMsg: number of ndefRecords = " + ndefRecords.length);
+                    mNdefType = ndefRecords[0].getType();
+
+                    if (mNdefType != null) {
+                        mNdefPayload = ndefRecords[0].getPayload();
+                        if (mNdefPayload != null && mNdefType != null) {
+                            if (!Arrays.equals(mNdefType, WLCCAP)) {
+                                if (DBG) Log.d(TAG, "NdefType not WLC_CAP");
+                                return (status = false);
+                            }
+                            if (DBG) Log.d(TAG, "mNdefType = " + bytesToHex(mNdefType));
+                        } else {
+                            return (status = false);
+                        }
+                    } else {
+                        Log.e(TAG, "NdefType null");
+                        return (status = false);
+                    }
+                } else {
+                    Log.e(TAG, "ndefRecords == null or ndefRecords.length = 0)");
+                    return (status = false);
+                }
+            } catch (Exception e) {
+                Log.e(TAG, "Error in getRecords " + e);
+                NfcChargingOnGoing = false;
+                TagHandler.startPresenceChecking(125, callbackTagDisconnection);
+            }
+
+            if ((mNdefPayload[1] & 0xC0) == 0xC0) {
+                if (DBG) Log.d(TAG, "Wrong Mode Req");
+                return (status = false);
+            }
+
+            WlcCap_ModeReq = (mNdefPayload[1] >> 6) & 0x3;
+            Nwt_max = (mNdefPayload[1] >> 2) & 0xF;
+            WlcCap_NegoWait = (mNdefPayload[1] >> 1) & 0x1;
+            if (DBG) Log.d(TAG, "WlcCap_NegoWait = " + WlcCap_NegoWait);
+            if (DBG) Log.d(TAG, "Nwt_max = " + Nwt_max);
+            WlcCap_RdConf = mNdefPayload[1] & 0x1;
+
+            WlcCap_CapWt = (mNdefPayload[2] & 0x1F);
+            if (WlcCap_CapWt > 0x13) WlcCap_CapWt = 0x13;
+            TCapWt = (int) Math.pow(2, (WlcCap_CapWt + 3));
+            if (TCapWt < 250) TCapWt = 250;
+            if (DBG) Log.d(TAG, "TCapWt = " + TCapWt);
+            TNdefRdWt = (int) (mNdefPayload[3] & 0xFF) * 10;
+            if (mNdefPayload[3] == 0 || mNdefPayload[3] == (byte)0xFF) TNdefRdWt = 2540;
+            if (DBG) Log.d(TAG, "TNdefRdWt = " + TNdefRdWt);
+            WlcCap_NdefWrTo = mNdefPayload[4];
+            if (WlcCap_NdefWrTo == 0 || WlcCap_NdefWrTo > 4) WlcCap_NdefWrTo = 4;
+            TNdefWrTo = (int) Math.pow(2, (WlcCap_NdefWrTo + 5));
+            if (DBG) Log.d(TAG, "TNdefWrTo = " + TNdefWrTo);
+            TNdefWrWt = mNdefPayload[5];
+            if (TNdefWrWt > 0x0A) TNdefWrWt = 0x0A;
+            if (DBG) Log.d(TAG, "TNdefWrWt = " + TNdefWrWt);
+
+            Log.d(TAG, " " + ndefRecords.length + " NdefRecords");
+            if (ndefRecords != null && ndefRecords.length > 1) {
+                for (int i = 1; i < ndefRecords.length; i++) {
+                    mNdefType = ndefRecords[i].getType();
+                    if (DBG) Log.d(TAG, "mNdefType = " + bytesToHex(mNdefType));
+                    mNdefPayload2 = ndefRecords[i].getPayload();
+                    if (mNdefPayload2 != null && mNdefType != null) {
+                        if (Arrays.equals(mNdefType, WLCSTAI)) {
+                            checkWlcStaiMsg(mNdefPayload2);
+                        } else if (Arrays.equals(mNdefType, USIWLC)) {
+                            if (DBG)
+                                Log.d(
+                                        TAG,
+                                        "mNdefPayload USIWLC = "
+                                                + bytesToHex(mNdefPayload2)
+                                                + " length = "
+                                                + mNdefPayload2.length);
+
+                            if (mNdefPayload2.length > 8) {
+                                mVendorId = (mNdefPayload2[8] << 8 | mNdefPayload2[7]) >> 4;
+                                Log.d(TAG, "VendorId = " + Integer.toHexString(mVendorId));
+                                WlcDeviceInfo.put(VendorId, mVendorId);
+                                mDeviceId = (long) ((mNdefPayload2[7] & 0x0F)) << 48;
+                                for (int j = 6; j > 0; j--) {
+                                    mDeviceId |= (long) (mNdefPayload2[j] & 0xFF) << ((j - 1) * 8);
+                                }
+                                if (DBG) Log.d(TAG, "DeviceId = " + Long.toHexString(mDeviceId));
+                            }
+                        }
+                    }
+                }
+            }
+            NfcChargingOnGoing = true;
+        } else {
+            status = false;
+        }
+        if (WlcDeviceInfo.get(BatteryLevel) > (mUpdatedBatteryLevel + 5)) {
+            NfcService.getInstance().onWlcData(WlcDeviceInfo);
+            mUpdatedBatteryLevel = WlcDeviceInfo.get(BatteryLevel);
+        }
+        if (DBG) Log.d(TAG, "checkWlcCapMsg: exit, status = " + status);
+        return status;
+    }
+
+    public boolean checkWlcCtlMsg(NdefMessage mNdefMessage) {
+        if (DBG) Log.d(TAG, "checkWlcCtlMsg: enter");
+
+        boolean status = true;
+        NdefRecord[] ndefRecords = null;
+
+        if (mNdefMessage != null) {
+            if (DBG) Log.d(TAG, "ndefMessage non null");
+            try {
+                ndefRecords = mNdefMessage.getRecords();
+                if (ndefRecords != null && ndefRecords.length > 0) {
+                    mNdefType = ndefRecords[0].getType();
+                    mNdefPayload = ndefRecords[0].getPayload();
+                    if (mNdefPayload != null && mNdefType != null) {
+                        if (!Arrays.equals(mNdefType, NfcCharging.WLCCTL)) {
+                            return (status = false);
+                        }
+                        if (DBG) Log.d(TAG, "mNdefType = " + bytesToHex(mNdefType));
+                    } else {
+                        return (status = false);
+                    }
+                } else {
+                    return (status = false);
+                }
+            } catch (Exception e) {
+                Log.e(TAG, "Error in getRecords " + e);
+                NfcChargingOnGoing = false;
+                TagHandler.startPresenceChecking(125, callbackTagDisconnection);
+            }
+            WlcCtl_ErrorFlag = (mNdefPayload[0] >> 7);
+            WlcCtl_BatteryStatus = (mNdefPayload[0] & 0x18) >> 3;
+            WlcCtl_Cnt_new = (mNdefPayload[0] & 0x7);
+            WlcCtl_WptReq = (mNdefPayload[1] & 0xC0) >> 6;
+            if (WlcCtl_WptReq > 1) WlcCtl_WptReq = 0;
+
+            WlcCtl_WptDuration = (mNdefPayload[1] & 0x3e) >> 1;
+            if (WlcCtl_WptDuration > 0x13) WlcCtl_WptReq = 0x13;
+            if (DBG) Log.d(TAG, "WlcCtl_WptDuration = " + WlcCtl_WptDuration);
+            TWptDuration = (int) Math.pow(2, (WlcCtl_WptDuration + 3));
+            WlcCtl_WptInfoReq = (mNdefPayload[1] & 0x1);
+            if (WlcCtl_WptReq == 0) WlcCtl_WptInfoReq = 0;
+
+            if ((mNdefPayload[2] <= 0x14) || (mNdefPayload[2] >= (byte)0xF6)) {
+                WlcCtl_PowerAdjReq = mNdefPayload[2];
+            } else {
+                WlcCtl_PowerAdjReq = 0;
+            }
+
+            if (DBG) Log.d(TAG, "checkWlcCtlMsg WlcCtl_PowerAdjReq = " + WlcCtl_PowerAdjReq);
+
+            if ((mNdefPayload[3] < 0x64) && (WlcCtl_BatteryStatus == 0x1)) {
+                WlcCtl_BatteryLevel = mNdefPayload[3];
+                WlcDeviceInfo.put(BatteryLevel, WlcCtl_BatteryLevel);
+                if (DBG) Log.d(TAG, "checkWlcCtlMsg WlcCtl_BatteryLevel = " + WlcCtl_BatteryLevel);
+            }
+
+            if (mNdefPayload[5] > 0xF) {
+                WlcCtl_HoldOffWt = 0xF;
+            } else {
+                WlcCtl_HoldOffWt = mNdefPayload[5];
+            }
+            THoldOffWt = (int) WlcCtl_HoldOffWt * 2;
+
+            if (DBG) Log.d(TAG, " " + ndefRecords.length + " NdefRecords");
+            if (ndefRecords != null && ndefRecords.length > 1) {
+                for (int i = 1; i < ndefRecords.length; i++) {
+                    mNdefType = ndefRecords[i].getType();
+                    if (DBG) Log.d(TAG, "mNdefType = " + bytesToHex(mNdefType));
+                    mNdefPayload2 = ndefRecords[i].getPayload();
+                    if (mNdefPayload2 != null && mNdefType != null) {
+                        if (Arrays.equals(mNdefType, WLCSTAI)) {
+                            checkWlcStaiMsg(mNdefPayload2);
+                        }
+                    }
+                }
+            }
+
+        } else {
+            status = false;
+        }
+
+        if (WlcDeviceInfo.get(BatteryLevel) > (mUpdatedBatteryLevel + 5)) {
+            NfcService.getInstance().onWlcData(WlcDeviceInfo);
+            mUpdatedBatteryLevel = WlcDeviceInfo.get(BatteryLevel);
+        }
+        if (DBG) Log.d(TAG, "checkWlcCtlMsg status = " + status);
+        return status;
+    }
+
+    public void checkWlcStaiMsg(byte[] mPayload) {
+        Byte ControlByte = 0;
+        if (DBG) Log.d(TAG, "mNdefPayload WLCSTAI = " + bytesToHex(mPayload));
+        ControlByte = mPayload[0];
+        int pos = 0;
+        if (((ControlByte & 0x01) == 0x01) && pos < mPayload.length) {
+            pos++;
+            WlcCtl_BatteryLevel = mPayload[pos];
+            WlcDeviceInfo.put(BatteryLevel, (int) mPayload[pos]);
+            if (DBG) Log.d(TAG, "WlcCtl_BatteryLevel = " + WlcDeviceInfo.get(BatteryLevel));
+        }
+        if (((ControlByte & 0x02) == 0x02) && pos < mPayload.length) {
+            pos++;
+            WlcCtl_ReceivePower = mPayload[pos];
+            WlcDeviceInfo.put(ReceivePower, (int) mPayload[pos]);
+        }
+        if (((ControlByte & 0x04) == 0x04) && pos < mPayload.length) {
+            pos++;
+            WlcCtl_ReceiveVoltage = mPayload[pos];
+            WlcDeviceInfo.put(ReceiveVoltage, (int) mPayload[pos]);
+        }
+        if (((ControlByte & 0x08) == 0x08) && pos < mPayload.length) {
+            pos++;
+            WlcCtl_TemperatureBattery = mPayload[pos];
+            WlcDeviceInfo.put(TemperatureBattery, (int) mPayload[pos]);
+        }
+        if (((ControlByte & 0x10) == 0x10) && pos < mPayload.length) {
+            pos++;
+            WlcCtl_TemperatureWlcl = mPayload[pos];
+            WlcDeviceInfo.put(TemperatureListener, (int) mPayload[pos]);
+        }
+    }
+
+    public void sendWLCPI(TagEndpoint tag, NdefMessage ndefMsg) {
+        NdefMessage WLCP_INFO =
+                constructWLCPI(
+                        (byte) Ptx,
+                        (byte) 0x00,
+                        (byte) 0x00,
+                        (byte) 0x00,
+                        (byte) 0x00,
+                        (byte) 0x00);
+        if (tag.writeNdef(WLCP_INFO.toByteArray())) {
+            Log.d(TAG, "Write NDEF success");
+        } else {
+            Log.d(TAG, "Write NDEF Error");
+        }
+    }
+
+    public NdefMessage constructWLCPI(
+            byte ptx, byte power_class, byte tps, byte cps, byte nmsi, byte nmsd) {
+        byte[] WLCPI_payload = {ptx, power_class, tps, cps, nmsi, nmsd};
+
+        NdefRecord WLCP_INFO_RECORD =
+                new NdefRecord(NdefRecord.TNF_WELL_KNOWN, WLCPI, new byte[] {}, WLCPI_payload);
+
+        NdefMessage WLCP_INFO = new NdefMessage(WLCP_INFO_RECORD);
+
+        return WLCP_INFO;
+    }
+
+    public void sendEmptyNdef() {
+        NdefRecord WLCP_RD_CONF_RECORD = new NdefRecord(NdefRecord.TNF_EMPTY, null, null, null);
+
+        NdefMessage WLCP_RD_CONF = new NdefMessage(WLCP_RD_CONF_RECORD);
+        if (TagHandler.writeNdef(WLCP_RD_CONF.toByteArray())) {
+            Log.d(TAG, "Write NDEF success");
+        } else {
+            Log.d(TAG, "Write NDEF Error");
+        }
+    }
+
+    public synchronized void stopNfcChargingPresenceChecking() {
+        if (mWatchdogWlc != null) {
+            mWatchdogWlc.end(true);
+        }
+    }
+
+    public synchronized void startNfcChargingPresenceChecking(int presenceCheckDelay) {
+        // Once we start presence checking, we allow the upper layers
+        // to know the tag is in the field.
+        if (mWatchdogWlc != null) {
+            if (DBG) Log.d(TAG, "mWatchDog non null");
+        }
+        if (mWatchdogWlc == null) {
+            if (DBG) Log.d(TAG, "mWatchdogWlc about to start...");
+            mWatchdogWlc = new PresenceCheckWatchdog(presenceCheckDelay);
+            mWatchdogWlc.start();
+        }
+    }
+
+    class PresenceCheckWatchdog extends Thread {
+        private int watchdogTimeout;
+
+        private boolean isPresent = true;
+        private boolean isStopped = false;
+        private boolean isPaused = false;
+        private boolean doCheck = true;
+        private boolean isFull = false;
+
+        public PresenceCheckWatchdog(int presenceCheckDelay) {
+            watchdogTimeout = presenceCheckDelay;
+        }
+
+        public synchronized void pause() {
+            isPaused = true;
+            doCheck = false;
+            this.notifyAll();
+            if (DBG) Log.d(TAG, "pause - isPaused = " + isPaused);
+        }
+
+        public synchronized void setTimeout(int timeout) {
+            if (DBG) Log.d(TAG, "PresenceCheckWatchdog watchdogTimeout " + timeout);
+            watchdogTimeout = timeout;
+        }
+
+        public synchronized void full() {
+            isFull = true;
+            this.notifyAll();
+        }
+
+        public synchronized void lost() {
+            isPresent = false;
+            if (DBG) Log.d(TAG, "PresenceCheckWatchdog isPresent " + isPresent);
+            doCheck = false;
+            this.notifyAll();
+        }
+
+        public synchronized void doResume() {
+            isPaused = false;
+            // We don't want to resume presence checking immediately,
+            // but go through at least one more wait period.
+            doCheck = false;
+            this.notifyAll();
+            if (DBG) Log.d(TAG, "doResume - isPaused = " + isPaused);
+        }
+
+        public synchronized void end(boolean disableCallback) {
+            isStopped = true;
+            if (DBG) Log.d(TAG, "PresenceCheckWatchdog end isStopped = " + isStopped);
+            doCheck = false;
+            if (disableCallback) {
+                //  tagDisconnectedCallback = null;
+            }
+            this.notifyAll();
+        }
+
+        @Override
+        public void run() {
+            synchronized (this) {
+                if (DBG) Log.d(TAG, "Starting WLC flow");
+                while (isPresent && !isStopped && !isFull) {
+                    if (DBG)
+                        Log.d(
+                                TAG,
+                                "isPresent= "
+                                        + isPresent
+                                        + " isStopped= "
+                                        + isStopped
+                                        + " isFull= "
+                                        + isFull);
+                    try {
+                        if (watchdogTimeout > 0) {
+                            this.wait(watchdogTimeout);
+                        }
+
+                        watchdogTimeout = HandleWLCState();
+                        if (DBG) Log.d(TAG, "Next watchdog timeout : " + watchdogTimeout);
+                    } catch (InterruptedException e) {
+                        // Activity detected, loop
+                        if (DBG) Log.d(TAG, "Interrupted thread: " + WLCState);
+                    }
+                }
+            }
+            synchronized (NfcCharging.this) {
+                isPresent = false;
+                NfcChargingOnGoing = false;
+                if (DBG)
+                    Log.d(
+                            TAG,
+                            "WLC state machine interrupted, NfcChargingOnGoing is "
+                                    + NfcChargingOnGoing);
+                resetInternalValues();
+            }
+            mLastState = DISCONNECTED;
+            WlcDeviceInfo.put(State, mLastState);
+            NfcService.getInstance().onWlcData(WlcDeviceInfo);
+            disconnectPresenceCheck();
+            if (DBG) Log.d(TAG, "disconnectPresenceCheck done");
+
+            // Restart the polling loop
+            NfcChargingMode = false;
+            TagHandler.disconnect();
+            // Disable discovery and restart polling loop only if not screen state change pending
+            if (!NfcService.getInstance().sendScreenMessageAfterNfcCharging()) {
+                if (DBG)
+                    Log.d(TAG, "No pending screen state change, stop Nfc charging presence check");
+                stopNfcChargingPresenceChecking();
+            }
+
+            if (DBG) Log.d(TAG, "Stopping background presence check");
+        }
+    }
+
+    public boolean disconnectPresenceCheck() {
+        boolean result = false;
+        PresenceCheckWatchdog watchdog;
+        if (DBG) Log.d(TAG, "disconnectPresenceCheck");
+        synchronized (this) {
+            watchdog = mWatchdogWlc;
+        }
+        if (watchdog != null) {
+            // Watchdog has already disconnected or will do it
+            watchdog.end(false);
+            synchronized (this) {
+                mWatchdogWlc = null;
+            }
+        }
+        result = true;
+        return result;
+    }
+
+    public int HandleWLCState() {
+        int wt = 1;
+        switch (WLCState) {
+            case STATE_2:
+                { // SM2
+                    if (DBG)
+                        Log.d(
+                                TAG,
+                                "HandleWLCState: STATE_2 (" + convert_state_2_str(STATE_2) + ")");
+                    if (mLastState != CONNECTED_CHARGING) {
+                        mLastState = CONNECTED_CHARGING;
+                        WlcDeviceInfo.put(State, mLastState);
+                        NfcService.getInstance().onWlcData(WlcDeviceInfo);
+                    }
+                    if (TagHandler != null) {
+                        if (!mFirstOccurrence) {
+                            mNdefMessage = TagHandler.getNdef();
+                        }
+
+                        if (mNdefMessage != null) {
+                            if (!mFirstOccurrence) {
+                                if (checkWlcCapMsg(mNdefMessage) == false) {
+                                    if (mWatchdogWlc != null) {
+                                        mWatchdogWlc.lost();
+                                    }
+                                    WLCL_Presence = false;
+                                    Log.d(TAG, " WLC_CAP : Presence Check FAILED ");
+                                    break;
+                                }
+                            } else {
+                                mFirstOccurrence = false;
+                            }
+
+                            if (WlcCap_ModeReq == MODE_REQ_BATTERY_FULL) {
+                                mWatchdogWlc.full();
+                                NfcChargingOnGoing = false;
+                                if (DBG)
+                                    Log.d(
+                                            TAG,
+                                            "MODE_REQ is BATTERY_FULL, NfcChargingOnGoing is "
+                                                    + NfcChargingOnGoing);
+                                wt = TCapWt;
+
+                                WLCState = STATE_24;
+                                WlcDeviceInfo.put(BatteryLevel, 0x64);
+                                mUpdatedBatteryLevel = WlcDeviceInfo.get(BatteryLevel);
+                                WlcDeviceInfo.put(State, mLastState);
+                                mLastState = CONNECTED_NOT_CHARGING;
+                                NfcService.getInstance().onWlcData(WlcDeviceInfo);
+                                if (DBG) Log.d(TAG, " Battery full");
+                                break;
+
+                            } else if (WlcCap_ModeReq == MODE_REQ_STATIC
+                                    || mNativeNfcManager.isMultiTag() == true) {
+                                if (DBG) Log.d(TAG, " Static mode");
+                                wt = 0; // TCapWt;
+
+                                WLCState = STATE_6;
+                                break;
+
+                            } else {
+                                if (DBG) Log.d(TAG, " Negotiated mode");
+                                wt = 5;
+
+                                WLCState = STATE_8;
+                                break;
+                            }
+                        } else {
+                            if (mWatchdogWlc != null) {
+                                mWatchdogWlc.lost();
+                            }
+                            WLCL_Presence = false;
+                            if (DBG) Log.d(TAG, " WLC_CAP: Presence Check FAILED");
+                        }
+                    }
+                    break;
+                }
+
+            case STATE_6:
+                { // SM6
+                    if (DBG)
+                        Log.d(
+                                TAG,
+                                "HandleWLCState: STATE_6 (" + convert_state_2_str(STATE_6) + ")");
+
+                    WLCState = STATE_2;
+                    wt = TCapWt + 5000;
+                    startWlcPowerTransfer(WlcCtl_PowerAdjReq, WlcCap_CapWt);
+                    break;
+                }
+
+            case STATE_8:
+                { // SM8
+                    if (DBG)
+                        Log.d(
+                                TAG,
+                                "HandleWLCState: STATE_8 (" + convert_state_2_str(STATE_8) + ")");
+
+                    if (WlcCap_NegoWait == 1) {
+                        if (mNretry > Nwt_max) {
+                            if (mWatchdogWlc != null) {
+                                mWatchdogWlc.lost();
+                            }
+                            WLCL_Presence = false;
+                            if (DBG) Log.d(TAG, " WLCCAP :too much retry, conclude procedure ");
+                            WLCState = STATE_2;
+                            wt = 1;
+                            break;
+                        } else {
+                            mNretry += 1;
+                            if (DBG) Log.d(TAG, "mNretry = " + mNretry);
+                            wt = TCapWt;
+                            WLCState = STATE_2;
+                            break;
+                        }
+                    }
+                    WLCState = STATE_11;
+                    wt = 5;
+
+                    break;
+                }
+
+            case STATE_11:
+                { // SM11
+                    if (DBG)
+                        Log.d(
+                                TAG,
+                                "HandleWLCState: STATE_11 (" + convert_state_2_str(STATE_11) + ")");
+
+                    sendWLCPI(TagHandler, null);
+                    if (DBG) Log.d(TAG, "end writing WLCP_INFO");
+                    wt = TNdefRdWt + 20;
+                    WLCState = STATE_12;
+                    break;
+                }
+
+            case STATE_12:
+                { // SM12-SM15
+                    if (DBG)
+                        Log.d(
+                                TAG,
+                                "HandleWLCState: STATE_12 (" + convert_state_2_str(STATE_12) + ")");
+
+                    if (TagHandler != null) {
+                        mNdefMessage = TagHandler.getNdef();
+                        if (mNdefMessage != null) {
+                            if (checkWlcCtlMsg(mNdefMessage)) {
+                                if (DBG)
+                                    Log.d(
+                                            TAG,
+                                            " WlcCtl_Cnt_new: "
+                                                    + WlcCtl_Cnt_new
+                                                    + "(mCnt +1)%8) = "
+                                                    + ((mCnt + 1) % 7));
+
+                                if (mCnt == -1) {
+                                    mCnt = WlcCtl_Cnt_new;
+                                } else if (WlcCtl_Cnt_new == mCnt) {
+                                    if (mNwcc_retry < 3) {
+                                        wt = 30; // Twcc,retry
+                                        mNwcc_retry++;
+                                        break;
+                                    } else if (mNwcc_retry == 3) {
+                                        // go to error
+                                        if (DBG) Log.d(TAG, " WLCL_CTL : Max mNwcc_retry reached");
+                                        mNwcc_retry = 0;
+                                        if (mWatchdogWlc != null) {
+                                            mWatchdogWlc.lost();
+                                        }
+                                        break;
+                                    }
+                                }
+                                mNwcc_retry = 0;
+                                mCnt = WlcCtl_Cnt_new;
+                                if (WlcCap_RdConf == 1) {
+                                    WLCState = STATE_16;
+                                    wt = TNdefWrWt;
+                                    break;
+                                }
+                                wt = 1;
+                                WLCState = STATE_17;
+                            } else {
+                                if (mNwcc_retry < 3) {
+                                    wt = 30; // Twcc,retry
+                                    mNwcc_retry++;
+                                    break;
+                                } else if (mNwcc_retry == 3) {
+                                    // go to error
+                                    if (DBG)
+                                        Log.d(TAG, " WLCL_CTL not valid: Max mNwcc_retry reached");
+                                    mNwcc_retry = 0;
+                                    if (mWatchdogWlc != null) {
+                                        mWatchdogWlc.lost();
+                                    }
+                                    break;
+                                }
+
+                                WLCL_Presence = false;
+                                if (DBG) Log.d(TAG, " WLCL_CTL : Presence Check Failed ");
+                            }
+                        } else {
+                            // no more tag
+                            if (mWatchdogWlc != null) {
+                                mWatchdogWlc.lost();
+                            }
+                            WLCL_Presence = false;
+                            if (DBG) Log.d(TAG, " WLCL_CTL : Presence Check Failed ");
+                        }
+                    } else {
+                        // conclude - go to error
+                    }
+                    break;
+                }
+
+            case STATE_16:
+                { // SM16
+                    if (DBG)
+                        Log.d(
+                                TAG,
+                                "HandleWLCState: STATE_16 (" + convert_state_2_str(STATE_16) + ")");
+
+                    sendEmptyNdef();
+                    WLCState = STATE_17;
+                    wt = 1;
+                    break;
+                }
+
+            case STATE_17:
+                { // SM17
+                    if (DBG)
+                        Log.d(
+                                TAG,
+                                "HandleWLCState: STATE_17 (" + convert_state_2_str(STATE_17) + ")");
+
+                    if (WlcCtl_WptReq == 0x0) {
+                        // No Power transfer Required
+                        if (DBG) Log.d(TAG, "No power transfer required");
+                        // go to presence check SM24
+                        WLCState = STATE_24;
+                        wt = TWptDuration;
+                        if (TWptDuration > 4000) {
+                            TagHandler.startPresenceChecking(200, callbackTagDisconnection);
+                        }
+                        break;
+                    }
+
+                    // Adjust WPT
+                    WLCState = STATE_21;
+                    wt = 1 + THoldOffWt;
+                    break;
+                }
+
+            case STATE_21:
+                { // SM21
+                    if (DBG)
+                        Log.d(
+                                TAG,
+                                "HandleWLCState: STATE_21 (" + convert_state_2_str(STATE_21) + ")");
+
+                    startWlcPowerTransfer(WlcCtl_PowerAdjReq, WlcCtl_WptDuration);
+                    WLCState = STATE_22;
+                    wt = TWptDuration + 5000;
+                    break;
+                }
+
+            case STATE_22:
+                { // SM22
+                    if (DBG)
+                        Log.d(
+                                TAG,
+                                "HandleWLCState: STATE_22 (" + convert_state_2_str(STATE_22) + ")");
+
+                    if (WlcCtl_WptInfoReq == 1) {
+                        WLCState = STATE_11;
+                        break;
+                    }
+                    WLCState = STATE_12;
+                    wt = 0;
+                    break;
+                }
+
+            case STATE_24:
+                { // SM24
+                    if (DBG)
+                        Log.d(
+                                TAG,
+                                "HandleWLCState: STATE_24 (" + convert_state_2_str(STATE_24) + ")");
+
+                    TagHandler.stopPresenceChecking();
+                    WLCState = STATE_2;
+                    NfcChargingOnGoing = false;
+                    if (mWatchdogWlc != null) {
+                        mWatchdogWlc.lost();
+                    }
+                    wt = 1;
+                    break;
+                }
+            case STATE_21_1:
+                { // Stop WPT
+                    if (DBG) Log.d(TAG, "HandleWLCState Time completed");
+                    WLCState = STATE_22;
+                    wt = 0;
+                    break;
+                }
+            case STATE_21_2:
+                { // Stop WPT
+                    if (DBG) Log.d(TAG, "HandleWLCState: STATE_21_2 (exit)");
+                    WLCState = STATE_2;
+                    NfcChargingOnGoing = false;
+
+                    if (mWatchdogWlc != null) {
+                        mWatchdogWlc.lost();
+                    }
+                    wt = 0;
+                    break;
+                }
+        }
+
+        return wt;
+    }
+
+    public void disconnectNfcCharging() {
+        Log.d(TAG, "disconnectNfcCharging");
+        NfcChargingOnGoing = false;
+        NfcChargingMode = false;
+        resetInternalValues();
+        disconnectPresenceCheck();
+        if (TagHandler != null) {
+            TagHandler.disconnect();
+        }
+    }
+
+    public void onWlcStopped(int wpt_end_condition) {
+        Log.d(TAG, "onWlcStopped");
+
+        switch (wpt_end_condition) {
+            case 0x0:
+                // Time completed
+                mWatchdogWlc.setTimeout(0);
+                if (WlcCap_ModeReq == MODE_REQ_NEGOTIATED) {
+                    WLCState = STATE_21_1;
+                } else {
+                    WLCState = STATE_2;
+                }
+                mWatchdogWlc.interrupt();
+                if (DBG) Log.d(TAG, "Time completed");
+                break;
+
+            case 0x1:
+                // FOD detection or Removal
+                mWatchdogWlc.setTimeout(0);
+                if (WlcCap_ModeReq == MODE_REQ_NEGOTIATED) {
+                    WLCState = STATE_21_2;
+                } else {
+                    WLCState = STATE_2;
+                }
+                mWatchdogWlc.interrupt();
+                if (DBG) Log.d(TAG, "FOD detection or removal");
+                break;
+
+            case 0x3:
+            default:
+                // Error
+                mWatchdogWlc.setTimeout(0);
+                WLCState = STATE_21_2;
+                mWatchdogWlc.interrupt();
+                if (DBG) Log.d(TAG, "FOD error detection");
+                break;
+        }
+    }
+
+    public String convert_state_2_str(int state) {
+        switch (state) {
+            case STATE_2:
+                return "Read WLC_CAP";
+            case STATE_6:
+                return "Static WPT";
+            case STATE_8:
+                return "Handle NEGO_WAIT?";
+            case STATE_11:
+                return "Write WLCP_INFO";
+            case STATE_12:
+                return "Read WLCL_CTL";
+            case STATE_16:
+                return "Read confirmation?";
+            case STATE_17:
+                return "Check WPT requested?";
+            case STATE_21:
+                return "Handle WPT";
+            case STATE_22:
+                return "Handle INFO_REQ?";
+            case STATE_24:
+                return "Handle removal detection";
+            case STATE_21_1:
+                return "Handle WPT time completed";
+            case STATE_21_2:
+                return "Handle FOD detection/removal";
+
+            default:
+                return "Unknown";
+        }
+    }
+}
diff --git a/tests/instrumentation/Android.bp b/tests/instrumentation/Android.bp
index d592542..548d005 100644
--- a/tests/instrumentation/Android.bp
+++ b/tests/instrumentation/Android.bp
@@ -21,6 +21,8 @@
         "androidx.test.rules",
         "androidx.test.ext.junit",
         "truth",
+        "androidx.test.espresso.core",
+        "androidx.test.espresso.intents-nodeps",
     ],
 
     // Include all test java files.
diff --git a/tests/instrumentation/src/com/android/nfc/cardemulation/AppChooserActivityTest.java b/tests/instrumentation/src/com/android/nfc/cardemulation/AppChooserActivityTest.java
new file mode 100644
index 0000000..9e942ae
--- /dev/null
+++ b/tests/instrumentation/src/com/android/nfc/cardemulation/AppChooserActivityTest.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.nfc.cardemulation;
+
+import static android.app.Activity.RESULT_CANCELED;
+import static android.nfc.cardemulation.CardEmulation.CATEGORY_PAYMENT;
+import static android.view.WindowManager.LayoutParams.FLAG_BLUR_BEHIND;
+import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+import static com.android.nfc.cardemulation.AppChooserActivity.EXTRA_CATEGORY;
+import static com.android.nfc.cardemulation.AppChooserActivity.EXTRA_APDU_SERVICES;
+import static com.android.nfc.cardemulation.AppChooserActivity.EXTRA_FAILED_COMPONENT;
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.nfc.cardemulation.ApduServiceInfo;
+import android.widget.ListView;
+import android.view.Window;
+import android.view.View;
+
+import androidx.lifecycle.Lifecycle;
+import androidx.test.core.app.ActivityScenario;
+import androidx.test.espresso.UiController;
+import androidx.test.espresso.ViewAction;
+import androidx.test.espresso.matcher.ViewMatchers;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.nfc.R;
+
+import java.lang.IllegalStateException;
+import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.hamcrest.Matcher;
+
+import org.junit.Rule;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class AppChooserActivityTest {
+  private static final String UNKNOWN_LABEL = "unknown";
+  private Context context;
+
+  @Before
+  public void setUp() throws Exception {
+    context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+  }
+
+  @Test
+  public void testNoFailedComponentAndNoAlternatives() throws Exception {
+    ActivityScenario<AppChooserActivity> scenario
+        = ActivityScenario.launch(getIntent(/*isPayment = */ true,
+                                            /* withFailedComponent = */ false,
+                                            /* withServices = */ false));
+
+    assertThat(scenario.getState()).isEqualTo(Lifecycle.State.DESTROYED);
+  }
+
+  @Test
+  public void testExistingFailedComponentAndNoAlternatives() throws Exception {
+    ActivityScenario<AppChooserActivity> scenario
+        = ActivityScenario.launch(getIntent(/*isPayment = */ true,
+                                            /* withFailedComponent = */ true,
+                                            /* withServices = */ false));
+
+    assertThat(scenario.getState()).isAtLeast(Lifecycle.State.CREATED);
+    String expectedText
+        = String.format(context.getString(R.string.transaction_failure), UNKNOWN_LABEL);
+    onView(withId(R.id.appchooser_text)).check(matches(withText(expectedText)));
+    scenario.onActivity(activity -> {
+      int flags = activity.getWindow().getAttributes().flags;
+      assertThat(flags & FLAG_BLUR_BEHIND).isEqualTo(FLAG_BLUR_BEHIND);
+      assertThat(flags & FLAG_DISMISS_KEYGUARD).isEqualTo(FLAG_DISMISS_KEYGUARD);
+    });
+  }
+
+  @Test
+  public void testNonPayment() throws Exception {
+    ActivityScenario<AppChooserActivity> scenario
+        = ActivityScenario.launch(getIntent(/*isPayment = */ false,
+                                            /* withFailedComponent = */ true,
+                                            /* withServices = */ true));
+
+    scenario.onActivity(activity -> {
+      ListView listView = (ListView) activity.findViewById(R.id.resolver_list);
+      assertThat(listView.getDividerHeight()).isEqualTo(-1);
+      assertThat(listView.getPaddingEnd()).isEqualTo(0);
+      assertThat(listView.getPaddingLeft()).isEqualTo(0);
+      assertThat(listView.getPaddingRight()).isEqualTo(0);
+      assertThat(listView.getPaddingStart()).isEqualTo(0);
+    });
+  }
+
+  @Test
+  public void testExistingFailedComponentAndExistingAlternatives() throws Exception {
+    ActivityScenario<AppChooserActivity> scenario
+        = ActivityScenario.launch(getIntent(/*isPayment = */ true,
+                                                    /* withFailedComponent = */ true,
+                                                    /* withServices = */ true));
+
+    assertThat(scenario.getState()).isAtLeast(Lifecycle.State.CREATED);
+    String expectedText
+        = String.format(context.getString(R.string.could_not_use_app), UNKNOWN_LABEL);
+    onView(withId(R.id.appchooser_text)).check(matches(withText(expectedText)));
+    scenario.onActivity(activity -> {
+      int flags = activity.getWindow().getAttributes().flags;
+      assertThat(flags & FLAG_BLUR_BEHIND).isEqualTo(FLAG_BLUR_BEHIND);
+      assertThat(flags & FLAG_DISMISS_KEYGUARD).isEqualTo(FLAG_DISMISS_KEYGUARD);
+
+      ListView listView = (ListView) activity.findViewById(R.id.resolver_list);
+      assertThat(listView.getDivider()).isNotNull();
+      assertThat((int) listView.getDividerHeight())
+          .isEqualTo((int) (context.getResources().getDisplayMetrics().density * 16));
+      assertThat(listView.getAdapter()).isNotNull();
+    });
+
+    // Test that onItemClick() does not throw an Exception
+    onView(withId(R.id.resolver_list)).perform(customClick());
+  }
+
+  @Test
+  public void testNoFailedComponentAndExistingAlternatives() throws Exception {
+    ActivityScenario<AppChooserActivity> scenario
+        = ActivityScenario.launch(getIntent(/*isPayment = */ true,
+                                                    /* withFailedComponent = */ false,
+                                                    /* withServices = */ true));
+
+    assertThat(scenario.getState()).isAtLeast(Lifecycle.State.CREATED);
+    String expectedText = context.getString(R.string.appchooser_description);
+    onView(withId(R.id.appchooser_text)).check(matches(withText(expectedText)));
+    scenario.onActivity(activity -> {
+      int flags = activity.getWindow().getAttributes().flags;
+      assertThat(flags & FLAG_BLUR_BEHIND).isEqualTo(FLAG_BLUR_BEHIND);
+      assertThat(flags & FLAG_DISMISS_KEYGUARD).isEqualTo(FLAG_DISMISS_KEYGUARD);
+
+      ListView listView = (ListView) activity.findViewById(R.id.resolver_list);
+      assertThat(listView.getDivider()).isNotNull();
+      assertThat((int) listView.getDividerHeight())
+          .isEqualTo((int) (context.getResources().getDisplayMetrics().density * 16));
+      assertThat(listView.getAdapter()).isNotNull();
+    });
+
+    // Test that onItemClick() does not throw an Exception
+    onView(withId(R.id.resolver_list)).perform(customClick());
+  }
+
+  private Intent getIntent(boolean isPayment, boolean withFailedComponent, boolean withServices) {
+    Intent intent = new Intent(context, AppChooserActivity.class);
+    if (isPayment) {
+      intent.putExtra(EXTRA_CATEGORY, CATEGORY_PAYMENT);
+    } else {
+      intent.putExtra(EXTRA_CATEGORY, "");
+    }
+
+    ArrayList<ApduServiceInfo> services = new ArrayList<ApduServiceInfo>();
+    if (withServices) {
+      ServiceInfo serviceInfo = new ServiceInfo();
+      serviceInfo.packageName = "com.nfc.test";
+      serviceInfo.name = "hce_service";
+      serviceInfo.applicationInfo = new ApplicationInfo();
+      ResolveInfo resolveInfo = new ResolveInfo();
+      resolveInfo.serviceInfo = serviceInfo;
+      ApduServiceInfo service
+          = new ApduServiceInfo(resolveInfo,
+                                /* onHost = */ false,
+                                /* description = */ "",
+                                /* staticAidGroups = */ new ArrayList<>(),
+                                /* dynamicAidGroups = */ new ArrayList<>(),
+                                /* requiresUnlock = */ false,
+                                /* bannerResource = */ 0,
+                                /* uid = */ 0,
+                                /* settingsActivityName = */ "",
+                                /* offHost = */ "",
+                                /* staticOffHost = */ "");
+      services.add(service);
+    }
+    intent.putParcelableArrayListExtra(EXTRA_APDU_SERVICES, services);
+
+    if (withFailedComponent) {
+      ComponentName failedComponent
+          = new ComponentName("com.android.test.walletroleholder",
+                              "com.android.test.walletroleholder.WalletRoleHolderApduService");
+      intent.putExtra(EXTRA_FAILED_COMPONENT, failedComponent);
+    }
+    return intent;
+  }
+
+  // Bypasses the view.getGlobalVisibleRect() requirement on the default click() action
+  private ViewAction customClick() {
+    return new ViewAction() {
+      @Override
+      public Matcher<View> getConstraints() {
+        return ViewMatchers.isEnabled();
+      }
+
+      @Override
+      public String getDescription() {
+        return "";
+      }
+
+      @Override
+      public void perform(UiController uiController, View view) {
+        view.performClick();
+      }
+    };
+  }
+}
diff --git a/tests/instrumentation/src/com/android/nfc/cardemulation/TapAgainDialogTest.java b/tests/instrumentation/src/com/android/nfc/cardemulation/TapAgainDialogTest.java
new file mode 100644
index 0000000..d69ee03
--- /dev/null
+++ b/tests/instrumentation/src/com/android/nfc/cardemulation/TapAgainDialogTest.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.nfc.cardemulation;
+
+import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static com.android.nfc.cardemulation.TapAgainDialog.EXTRA_APDU_SERVICE;
+import static com.android.nfc.cardemulation.TapAgainDialog.EXTRA_CATEGORY;
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.nfc.cardemulation.ApduServiceInfo;
+import android.view.Window;
+
+import androidx.lifecycle.Lifecycle;
+import androidx.test.core.app.ActivityScenario;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.nfc.R;
+
+import java.util.ArrayList;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class TapAgainDialogTest {
+  private static final int ALERT_DIALOG_ID = 16909366;
+
+  @Test
+  public void testOnCreate() throws Exception {
+    ActivityScenario<TapAgainDialog> scenario = ActivityScenario.launch(getStartIntent());
+
+    assertThat(scenario.getState()).isAtLeast(Lifecycle.State.CREATED);
+
+    scenario.onActivity(activity -> {
+      int flags = activity.getWindow().getAttributes().flags;
+      assertThat(flags & FLAG_DISMISS_KEYGUARD).isEqualTo(FLAG_DISMISS_KEYGUARD);
+    });
+  }
+
+  @Test
+  public void testOnClick() throws Exception {
+    ActivityScenario.launch(getStartIntent());
+
+    onView(withId(R.id.tap_again_toolbar)).perform(click());
+
+    onView(withId(ALERT_DIALOG_ID)).check(matches(isDisplayed()));
+    onView(withId(R.id.tap_again_toolbar)).check(matches(isDisplayed()));
+    onView(withId(R.id.tap_again_appicon)).check(matches(isDisplayed()));
+  }
+
+  @Test
+  public void testOnDestroy() throws Exception {
+    ActivityScenario<TapAgainDialog> scenario = ActivityScenario.launch(getStartIntent());
+    scenario.moveToState(Lifecycle.State.DESTROYED);
+
+    assertThat(scenario.getState()).isEqualTo(Lifecycle.State.DESTROYED);
+  }
+
+  @Test
+  public void testOnStop() throws Exception {
+    ActivityScenario<TapAgainDialog> scenario = ActivityScenario.launch(getStartIntent());
+
+    // activity's onPause() and onStop() methods are called
+    scenario.moveToState(Lifecycle.State.CREATED);
+
+    assertThat(scenario.getState()).isAtLeast(Lifecycle.State.CREATED);
+  }
+
+  private Intent getStartIntent() {
+    Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+    Intent intent = new Intent(context, TapAgainDialog.class);
+    intent.putExtra(EXTRA_CATEGORY, "");
+
+    ServiceInfo serviceInfo = new ServiceInfo();
+    serviceInfo.packageName = "com.nfc.test";
+    serviceInfo.name = "hce_service";
+    serviceInfo.applicationInfo = new ApplicationInfo();
+    ResolveInfo resolveInfo = new ResolveInfo();
+    resolveInfo.serviceInfo = serviceInfo;
+    ApduServiceInfo service
+        = new ApduServiceInfo(resolveInfo,
+        /* onHost = */ false,
+        /* description = */ "",
+        /* staticAidGroups = */ new ArrayList<>(),
+        /* dynamicAidGroups = */ new ArrayList<>(),
+        /* requiresUnlock = */ false,
+        /* bannerResource = */ 0,
+        /* uid = */ 0,
+        /* settingsActivityName = */ "",
+        /* offHost = */ "",
+        /* staticOffHost = */ "");
+    intent.putExtra(EXTRA_APDU_SERVICE, service);
+    return intent;
+  }
+}
diff --git a/tests/unit/Android.bp b/tests/unit/Android.bp
index 09cc17e..0c2efc9 100644
--- a/tests/unit/Android.bp
+++ b/tests/unit/Android.bp
@@ -26,13 +26,16 @@
         "androidx.annotation_annotation",
         "androidx.appcompat_appcompat",
         "com.google.android.material_material",
+        "nfc-event-log-proto",
         "nfc_flags_lib",
         "flag-junit",
         "platform-test-annotations",
+        "testables",
     ],
 
     jni_libs: [
         // Required for ExtendedMockito
+        "libnfc_nci_jni",
         "libdexmakerjvmtiagent",
         "libstaticjvmtiagent",
     ],
diff --git a/tests/unit/AndroidManifest.xml b/tests/unit/AndroidManifest.xml
index f80ef8d..9a45b16 100644
--- a/tests/unit/AndroidManifest.xml
+++ b/tests/unit/AndroidManifest.xml
@@ -26,6 +26,11 @@
     <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
     <application android:debuggable="true">
         <uses-library android:name="android.test.runner" />
+        <activity
+            android:name="com.android.nfc.cardemulation.AppChooserActivity"
+            android:theme="@style/Theme.AppCompat"
+            android:exported="false"
+            />
     </application>
 
     <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
diff --git a/tests/unit/assets/dynamic_aids.xml b/tests/unit/assets/dynamic_aids.xml
new file mode 100644
index 0000000..9b3420a
--- /dev/null
+++ b/tests/unit/assets/dynamic_aids.xml
@@ -0,0 +1,28 @@
+<services>
+    <service component="com.android.test.walletroleholder/com.android.test.walletroleholder.OnHostApduService"
+        uid="1"
+        offHostSE="offhostse1"
+        shouldDefaultToObserveMode="true">
+        <aid-group category="payment">
+            <aid value="A000000004101011"/>
+            <aid value="A000000004101012"/>
+            <aid value="A000000004101013"/>
+        </aid-group>
+    </service>
+    <service component="com.android.test.nonpaymentnfc/com.android.test.nonpaymentnfc.NonPaymentApduService"
+        uid="1"
+        offHostSE="offhostse2"
+        shouldDefaultToObserveMode="false">
+        <aid-group category="other">
+            <aid value="F053414950454D"/>
+        </aid-group>
+    </service>
+    <service component="com.android.test.another/com.android.test.another.OffHostApduService"
+        uid="1"
+        offHostSE="offhostse3"
+        shouldDefaultToObserveMode="false">
+        <aid-group category="other">
+            <aid value="F053414950454D"/>
+        </aid-group>
+    </service>
+</services>
\ No newline at end of file
diff --git a/tests/unit/assets/other_status.xml b/tests/unit/assets/other_status.xml
new file mode 100644
index 0000000..58106f6
--- /dev/null
+++ b/tests/unit/assets/other_status.xml
@@ -0,0 +1,6 @@
+<services>
+    <service component="com.android.test.another/com.android.test.another.OffHostApduService"
+        uid="1"
+        checked="true">
+    </service>
+</services>
\ No newline at end of file
diff --git a/tests/unit/res/layout/activity_layout_test.xml b/tests/unit/res/layout/activity_layout_test.xml
new file mode 100644
index 0000000..d442e09
--- /dev/null
+++ b/tests/unit/res/layout/activity_layout_test.xml
@@ -0,0 +1,33 @@
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="horizontal"
+    android:gravity="center"
+    android:layout_height="72dp"
+    android:layout_width="match_parent">
+
+  <ImageView android:id="@+id/appicon"
+      android:contentDescription="App Icon"
+      android:layout_width="32dp"
+      android:layout_height="32dp"
+      android:layout_marginEnd="16dp"
+      android:layout_marginStart="24dp"
+      android:scaleType="fitCenter"
+      android:gravity="center_vertical"/>
+
+  <TextView android:id="@+id/applabel"
+      android:textAppearance="?android:attr/textAppearanceMedium"
+      android:textColor="?android:attr/textColorPrimary"
+      android:layout_width="match_parent"
+      android:layout_height="32dp"
+      android:gravity="center_vertical|start"
+      android:minLines="2"
+      android:maxLines="2"
+      android:paddingStart="4dip"
+      android:paddingEnd="4dip" />
+  <ImageView android:id="@+id/banner"
+      android:contentDescription="Banner"
+      android:layout_height="32dp"
+      android:layout_width="32dp"
+      android:layout_marginEnd="16dp"
+      android:layout_marginStart="24dp"
+      android:gravity="center" />
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/unit/src/com/android/nfc/NfcEventLogTest.java b/tests/unit/src/com/android/nfc/NfcEventLogTest.java
new file mode 100644
index 0000000..9ca3cb9
--- /dev/null
+++ b/tests/unit/src/com/android/nfc/NfcEventLogTest.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.nfc;
+
+import static com.android.nfc.NfcEventLog.FORMATTER;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.test.TestLooper;
+import android.util.AtomicFile;
+
+import com.android.nfc.proto.NfcEventProto;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.AdditionalMatchers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import java.io.FileOutputStream;
+import java.time.LocalDateTime;
+import java.util.ArrayDeque;
+
+@RunWith(AndroidJUnit4.class)
+public class NfcEventLogTest {
+    @Mock Context mContext;
+    @Mock NfcInjector mNfcInjector;
+    @Mock AtomicFile mLogFile;
+    @Mock Resources mResources;
+    @Mock FileOutputStream mFileOutputStream;
+    TestLooper mLooper;
+    NfcEventLog mNfcEventLog;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mLooper = new TestLooper();
+        when(mContext.getResources()).thenReturn(mResources);
+        when(mResources.getInteger(R.integer.max_event_log_num)).thenReturn(10);
+        when(mLogFile.startWrite()).thenReturn(mFileOutputStream);
+        mNfcEventLog = new NfcEventLog(mContext, mNfcInjector, mLooper.getLooper(), mLogFile);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+    }
+
+    @Test
+    public void testLogEvent() throws Exception {
+        NfcEventProto.EventType eventType =
+                NfcEventProto.EventType.newBuilder()
+                        .setBootupState(NfcEventProto.NfcBootupState.newBuilder()
+                                .setEnabled(true)
+                                .build())
+                        .build();
+        LocalDateTime localDateTime = LocalDateTime.MIN;
+        when(mNfcInjector.getLocalDateTime()).thenReturn(localDateTime);
+        mNfcEventLog.logEvent(eventType);
+        mLooper.dispatchAll();
+
+        NfcEventProto.Event expectedEvent = NfcEventProto.Event.newBuilder()
+                .setTimestamp(localDateTime.format(FORMATTER))
+                .setEventType(eventType)
+                .build();
+        NfcEventProto.EventList expectedEventList =
+                NfcEventProto.EventList.newBuilder().addEvents(expectedEvent).build();
+        verify(mFileOutputStream).write(AdditionalMatchers.aryEq(expectedEventList.toByteArray()));
+    }
+
+    @Test
+    public void testMultipleLogEvents() throws Exception {
+        NfcEventProto.EventType eventType =
+                NfcEventProto.EventType.newBuilder()
+                        .setBootupState(NfcEventProto.NfcBootupState.newBuilder()
+                                .setEnabled(true)
+                                .build())
+                        .build();
+        LocalDateTime localDateTime = LocalDateTime.MIN;
+        when(mNfcInjector.getLocalDateTime()).thenReturn(localDateTime);
+
+        // Log the event twice.
+        mNfcEventLog.logEvent(eventType);
+        mLooper.dispatchAll();
+
+        mNfcEventLog.logEvent(eventType);
+        mLooper.dispatchAll();
+
+        NfcEventProto.Event expectedEvent = NfcEventProto.Event.newBuilder()
+                .setTimestamp(localDateTime.format(FORMATTER))
+                .setEventType(eventType)
+                .build();
+        NfcEventProto.EventList expectedEventList =
+                NfcEventProto.EventList.newBuilder()
+                        .addEvents(expectedEvent)
+                        .addEvents(expectedEvent)
+                        .build();
+        verify(mFileOutputStream).write(AdditionalMatchers.aryEq(expectedEventList.toByteArray()));
+    }
+
+    @Test
+    public void testReadEventsFromLogFile() throws Exception {
+        LocalDateTime localDateTime = LocalDateTime.MIN;
+        NfcEventProto.EventType eventType =
+                NfcEventProto.EventType.newBuilder()
+                        .setBootupState(NfcEventProto.NfcBootupState.newBuilder()
+                                .setEnabled(true)
+                                .build())
+                        .build();
+        NfcEventProto.Event event = NfcEventProto.Event.newBuilder()
+                .setTimestamp(localDateTime.format(FORMATTER))
+                .setEventType(eventType)
+                .build();
+        NfcEventProto.EventList eventList =
+                NfcEventProto.EventList.newBuilder()
+                        .addEvents(event)
+                        .addEvents(event)
+                        .build();
+
+        when(mLogFile.readFully()).thenReturn(eventList.toByteArray());
+        // Recreate the instance to simulate reading the log file.
+        mNfcEventLog = new NfcEventLog(mContext, mNfcInjector, mLooper.getLooper(), mLogFile);
+        mLooper.dispatchAll();
+
+        ArrayDeque<NfcEventProto.Event> expectedEventList = new ArrayDeque<NfcEventProto.Event>();
+        expectedEventList.add(event);
+        expectedEventList.add(event);
+
+        ArrayDeque<NfcEventProto.Event> retrievedEventsList = mNfcEventLog.getEventsList();
+        assertThat(retrievedEventsList.toString()).isEqualTo(expectedEventList.toString());
+
+    }
+}
diff --git a/tests/unit/src/com/android/nfc/NfcServiceTest.java b/tests/unit/src/com/android/nfc/NfcServiceTest.java
new file mode 100644
index 0000000..af97fbb
--- /dev/null
+++ b/tests/unit/src/com/android/nfc/NfcServiceTest.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.nfc;
+
+import static com.android.nfc.NfcService.PREF_NFC_ON;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.app.Application;
+import android.app.KeyguardManager;
+import android.app.backup.BackupManager;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.nfc.NfcServiceManager;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.PowerManager;
+import android.os.UserManager;
+import android.os.test.TestLooper;
+import android.telephony.TelephonyManager;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoSession;
+
+@RunWith(AndroidJUnit4.class)
+public final class NfcServiceTest {
+    private static final String PKG_NAME = "com.test";
+    @Mock Application mApplication;
+    @Mock NfcInjector mNfcInjector;
+    @Mock DeviceHost mDeviceHost;
+    @Mock NfcEventLog mNfcEventLog;
+    @Mock NfcDispatcher mNfcDispatcher;
+    @Mock NfcUnlockManager mNfcUnlockManager;
+    @Mock SharedPreferences mPreferences;
+    @Mock SharedPreferences.Editor mPreferencesEditor;
+    @Mock PowerManager mPowerManager;
+    @Mock PackageManager mPackageManager;
+    @Mock ScreenStateHelper mScreenStateHelper;
+    @Mock Resources mResources;
+    @Mock KeyguardManager mKeyguardManager;
+    @Mock UserManager mUserManager;
+    @Mock ActivityManager mActivityManager;
+    @Mock NfcServiceManager.ServiceRegisterer mNfcManagerRegisterer;
+    @Mock NfcDiagnostics mNfcDiagnostics;
+    @Mock DeviceConfigFacade mDeviceConfigFacade;
+    @Mock ContentResolver mContentResolver;
+    @Mock Bundle mUserRestrictions;
+    @Mock BackupManager mBackupManager;
+    @Captor ArgumentCaptor<DeviceHost.DeviceHostListener> mDeviceHostListener;
+    @Captor ArgumentCaptor<BroadcastReceiver> mGlobalReceiver;
+    TestLooper mLooper;
+    NfcService mNfcService;
+    private MockitoSession mStaticMockSession;
+
+    @Before
+    public void setUp() {
+        mLooper = new TestLooper();
+        MockitoAnnotations.initMocks(this);
+        AsyncTask.setDefaultExecutor(new HandlerExecutor(new Handler(mLooper.getLooper())));
+
+        when(mNfcInjector.getMainLooper()).thenReturn(mLooper.getLooper());
+        when(mNfcInjector.getNfcEventLog()).thenReturn(mNfcEventLog);
+        when(mNfcInjector.makeDeviceHost(any())).thenReturn(mDeviceHost);
+        when(mNfcInjector.getScreenStateHelper()).thenReturn(mScreenStateHelper);
+        when(mNfcInjector.getNfcDiagnostics()).thenReturn(mNfcDiagnostics);
+        when(mNfcInjector.getDeviceConfigFacade()).thenReturn(mDeviceConfigFacade);
+        when(mNfcInjector.getNfcManagerRegisterer()).thenReturn(mNfcManagerRegisterer);
+        when(mNfcInjector.getBackupManager()).thenReturn(mBackupManager);
+        when(mNfcInjector.getNfcDispatcher()).thenReturn(mNfcDispatcher);
+        when(mNfcInjector.getNfcUnlockManager()).thenReturn(mNfcUnlockManager);
+        when(mApplication.getSharedPreferences(anyString(), anyInt())).thenReturn(mPreferences);
+        when(mApplication.getSystemService(PowerManager.class)).thenReturn(mPowerManager);
+        when(mApplication.getSystemService(UserManager.class)).thenReturn(mUserManager);
+        when(mApplication.getSystemService(ActivityManager.class)).thenReturn(mActivityManager);
+        when(mApplication.getSystemService(KeyguardManager.class)).thenReturn(mKeyguardManager);
+        when(mApplication.getPackageManager()).thenReturn(mPackageManager);
+        when(mApplication.getResources()).thenReturn(mResources);
+        when(mApplication.createContextAsUser(any(), anyInt())).thenReturn(mApplication);
+        when(mApplication.getContentResolver()).thenReturn(mContentResolver);
+        when(mUserManager.getUserRestrictions()).thenReturn(mUserRestrictions);
+        when(mResources.getStringArray(R.array.nfc_allow_list)).thenReturn(new String[0]);
+        when(mPreferences.edit()).thenReturn(mPreferencesEditor);
+        when(mPowerManager.newWakeLock(anyInt(), anyString()))
+                .thenReturn(mock(PowerManager.WakeLock.class));
+        createNfcService();
+    }
+
+    @After
+    public void tearDown() {
+    }
+
+    private void createNfcService() {
+        mNfcService = new NfcService(mApplication, mNfcInjector);
+        mLooper.dispatchAll();
+        verify(mNfcInjector).makeDeviceHost(mDeviceHostListener.capture());
+        verify(mApplication).registerReceiverForAllUsers(
+                mGlobalReceiver.capture(),
+                argThat(intent -> intent.hasAction(Intent.ACTION_SCREEN_ON)), any(), any());
+        clearInvocations(mDeviceHost, mNfcInjector, mApplication);
+    }
+
+    private void enableAndVerify() throws Exception {
+        when(mDeviceHost.initialize()).thenReturn(true);
+        when(mPreferences.getBoolean(eq(PREF_NFC_ON), anyBoolean())).thenReturn(true);
+        mNfcService.mNfcAdapter.enable(PKG_NAME);
+        verify(mPreferencesEditor).putBoolean(PREF_NFC_ON, true);
+        mLooper.dispatchAll();
+        verify(mDeviceHost).initialize();
+        clearInvocations(mDeviceHost, mPreferencesEditor);
+    }
+
+    private void disableAndVerify() throws Exception {
+        when(mDeviceHost.deinitialize()).thenReturn(true);
+        when(mPreferences.getBoolean(eq(PREF_NFC_ON), anyBoolean())).thenReturn(false);
+        mNfcService.mNfcAdapter.disable(true, PKG_NAME);
+        verify(mPreferencesEditor).putBoolean(PREF_NFC_ON, false);
+        mLooper.dispatchAll();
+        verify(mDeviceHost).deinitialize();
+        verify(mNfcDispatcher).resetForegroundDispatch();
+        clearInvocations(mDeviceHost, mPreferencesEditor, mNfcDispatcher);
+    }
+
+    @Test
+    public void testEnable() throws Exception {
+        enableAndVerify();
+    }
+
+    @Test
+    public void testDisable() throws Exception {
+        enableAndVerify();
+        disableAndVerify();
+    }
+
+    @Test
+    public void testSimStateChange() throws Exception {
+        when(mResources.getBoolean(R.bool.restart_on_sim_change)).thenReturn(true);
+        createNfcService();
+
+        enableAndVerify();
+        Intent intent = new Intent(TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED)
+                .putExtra(TelephonyManager.EXTRA_SIM_STATE, TelephonyManager.SIM_STATE_LOADED);
+        mGlobalReceiver.getValue().onReceive(mApplication, intent);
+        mLooper.dispatchAll();
+        verify(mDeviceHost).deinitialize();
+        verify(mDeviceHost).initialize();
+
+        enableAndVerify();
+        intent = new Intent(TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED)
+                .putExtra(TelephonyManager.EXTRA_SIM_STATE, TelephonyManager.SIM_STATE_ABSENT);
+        mGlobalReceiver.getValue().onReceive(mApplication, intent);
+        mLooper.dispatchAll();
+        verify(mDeviceHost).deinitialize();
+        verify(mDeviceHost).initialize();
+    }
+
+    @Test
+    public void testSimStateChangeWhenNfcIsDisabled() throws Exception {
+        when(mResources.getBoolean(R.bool.restart_on_sim_change)).thenReturn(true);
+        createNfcService();
+
+        Intent intent = new Intent(TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED)
+                .putExtra(TelephonyManager.EXTRA_SIM_STATE, TelephonyManager.SIM_STATE_LOADED);
+        mGlobalReceiver.getValue().onReceive(mApplication, intent);
+        mLooper.dispatchAll();
+        verifyNoMoreInteractions(mDeviceHost);
+    }
+}
diff --git a/tests/unit/src/com/android/nfc/RegisteredAidCacheTest.java b/tests/unit/src/com/android/nfc/RegisteredAidCacheTest.java
index b4faa8d..c145430 100644
--- a/tests/unit/src/com/android/nfc/RegisteredAidCacheTest.java
+++ b/tests/unit/src/com/android/nfc/RegisteredAidCacheTest.java
@@ -28,6 +28,7 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
 import org.mockito.MockitoSession;
 import org.mockito.quality.Strictness;
 
@@ -37,6 +38,7 @@
 import com.android.dx.mockito.inline.extended.ExtendedMockito;
 import com.android.nfc.cardemulation.AidRoutingManager;
 import com.android.nfc.cardemulation.RegisteredAidCache;
+import com.android.nfc.cardemulation.WalletRoleObserver;
 
 @RunWith(AndroidJUnit4.class)
 public class RegisteredAidCacheTest {
@@ -46,6 +48,8 @@
     private MockitoSession mStaticMockSession;
     private RegisteredAidCache mRegisteredAidCache;
     private Context mockContext;
+    @Mock
+    private WalletRoleObserver mWalletRoleObserver;
 
     @Before
     public void setUp() throws Exception {
@@ -67,7 +71,8 @@
 
         AidRoutingManager routingManager = mock(AidRoutingManager.class);
         InstrumentationRegistry.getInstrumentation().runOnMainSync(
-                () -> mRegisteredAidCache = new RegisteredAidCache(mockContext, routingManager));
+                () -> mRegisteredAidCache = new RegisteredAidCache(
+                        mockContext, mWalletRoleObserver, routingManager));
         Assert.assertNotNull(mRegisteredAidCache);
     }
 
diff --git a/tests/unit/src/com/android/nfc/cardemulation/AidRoutingManagerTest.java b/tests/unit/src/com/android/nfc/cardemulation/AidRoutingManagerTest.java
new file mode 100644
index 0000000..30aa829
--- /dev/null
+++ b/tests/unit/src/com/android/nfc/cardemulation/AidRoutingManagerTest.java
@@ -0,0 +1,630 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.nfc.cardemulation;
+
+import static com.android.nfc.cardemulation.AidRoutingManager.AID_MATCHING_EXACT_ONLY;
+import static com.android.nfc.cardemulation.AidRoutingManager.AID_MATCHING_EXACT_OR_PREFIX;
+import static com.android.nfc.cardemulation.AidRoutingManager.AID_MATCHING_EXACT_OR_SUBSET_OR_PREFIX;
+import static com.android.nfc.cardemulation.AidRoutingManager.AID_MATCHING_PREFIX_ONLY;
+import static com.android.nfc.cardemulation.AidRoutingManager.ROUTE_HOST;
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.nfc.NfcService;
+import com.android.nfc.NfcStatsLog;
+import com.android.nfc.cardemulation.AidRoutingManager.AidEntry;
+
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.HashSet;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+@RunWith(AndroidJUnit4.class)
+public class AidRoutingManagerTest {
+
+  private AidRoutingManager manager;
+  private MockitoSession mStaticMockSession;
+
+  @Mock
+  private RoutingOptionManager mRoutingOptionManager;
+  @Mock
+  private NfcService mNfcService;
+  @Mock
+  private PrintWriter mPw;
+
+  @Captor
+  private ArgumentCaptor<String> unroutedAidsCaptor;
+  @Captor
+  private ArgumentCaptor<String> routedAidsCaptor;
+  @Captor
+  private ArgumentCaptor<Integer> routeCaptor;
+  @Captor
+  private ArgumentCaptor<Integer> aidTypeCaptor;
+  @Captor
+  private ArgumentCaptor<Integer> powerCaptor;
+  @Captor
+  private ArgumentCaptor<Integer> codeCaptor;
+  @Captor
+  private ArgumentCaptor<Integer> arg1Captor;
+  @Captor
+  private ArgumentCaptor<Integer> arg2Captor;
+  @Captor
+  private ArgumentCaptor<Integer> arg3Captor;
+
+  private static final int DEFAULT_ROUTE = 0;
+  private static final int OVERRIDE_DEFAULT_ROUTE = 10;
+  private static final int DEFAULT_OFFHOST_ROUTE = 20;
+  private static final int OVERRIDE_ISODEP_ROUTE = 30;
+  private static final byte[] OFFHOST_ROUTE_UICC = new byte[] {5, 6, 7, 8};
+  private static final byte[] OFFHOST_ROUTE_ESE = new byte[] {1, 2, 3, 4};
+  private static final int FIRST_AID_ENTRY_POWER = 1;
+  private static final int FIRST_AID_ENTRY_AID_INFO = 2;
+  private static final int SECOND_AID_ENTRY_POWER = 3;
+  private static final int SECOND_AID_ENTRY_AID_INFO = 4;
+  private static final int THIRD_AID_ENTRY_POWER = 5;
+  private static final int THIRD_AID_ENTRY_AID_INFO = 6;
+  private static final int FOURTH_AID_ENTRY_POWER = 7;
+  private static final int FOURTH_AID_ENTRY_AID_INFO = 8;
+
+  @Before
+  public void setUp() {
+    mStaticMockSession = ExtendedMockito.mockitoSession()
+        .mockStatic(RoutingOptionManager.class)
+        .mockStatic(NfcService.class)
+        .mockStatic(NfcStatsLog.class)
+        .strictness(Strictness.LENIENT)
+        .startMocking();
+    MockitoAnnotations.initMocks(this);
+    when(RoutingOptionManager.getInstance()).thenReturn(mRoutingOptionManager);
+    when(NfcService.getInstance()).thenReturn(mNfcService);
+  }
+
+  @After
+  public void tearDown() {
+    mStaticMockSession.finishMocking();
+  }
+
+  @Test
+  public void testConstructor() {
+    manager = new AidRoutingManager();
+  }
+
+  @Test
+  public void testSupportsAidPrefixRouting_ReturnsTrue() {
+    when(mRoutingOptionManager.getAidMatchingSupport()).thenReturn(AID_MATCHING_EXACT_OR_PREFIX);
+    manager = new AidRoutingManager();
+
+    boolean result = manager.supportsAidPrefixRouting();
+
+    assertThat(result).isTrue();
+  }
+
+  @Test
+  public void testSupportsAidPrefixRouting_ReturnsFalse() {
+    when(mRoutingOptionManager.getAidMatchingSupport()).thenReturn(AID_MATCHING_EXACT_ONLY);
+    manager = new AidRoutingManager();
+
+    boolean result = manager.supportsAidPrefixRouting();
+
+    assertThat(result).isFalse();
+  }
+
+  @Test
+  public void testSupportsAidSubsetRouting_ReturnsTrue() {
+    when(mRoutingOptionManager.getAidMatchingSupport())
+        .thenReturn(AID_MATCHING_EXACT_OR_SUBSET_OR_PREFIX);
+    manager = new AidRoutingManager();
+
+    boolean result = manager.supportsAidSubsetRouting();
+
+    assertThat(result).isTrue();
+  }
+
+  @Test
+  public void testSupportsAidSubsetRouting_ReturnsFalse() {
+    when(mRoutingOptionManager.getAidMatchingSupport()).thenReturn(AID_MATCHING_EXACT_OR_PREFIX);
+    manager = new AidRoutingManager();
+
+    boolean result = manager.supportsAidSubsetRouting();
+
+    assertThat(result).isFalse();
+  }
+
+  @Test
+  public void testCalculateAidRouteSizeWithEmptyCache() {
+    manager = new AidRoutingManager();
+
+    int result = manager.calculateAidRouteSize(new HashMap<String, AidEntry>());
+
+    assertThat(result).isEqualTo(0);
+  }
+
+  @Test
+  public void testCalculateAidRouteSizeWithNonEmptyCache() {
+    String firstAidEntry = "0000000000";
+    String secondAidEntry = "000000000000000";
+    HashMap<String, AidEntry> cache = new HashMap<>();
+    cache.put(firstAidEntry + "*", null);
+    cache.put(secondAidEntry, null);
+    manager = new AidRoutingManager();
+
+    int result = manager.calculateAidRouteSize(cache);
+
+    int expected = (firstAidEntry.length() / 2) + 4 + (secondAidEntry.length() / 2) + 4;
+    assertThat(result).isEqualTo(expected);
+  }
+
+  @Test
+  public void testConfigureRoutingWithEmptyMap_ReturnsFalse() {
+    manager = new AidRoutingManager();
+
+    boolean result = manager.configureRouting(/* aidMap = */ new HashMap<String, AidEntry>(),
+        /* force = */ false);
+
+    assertThat(result).isFalse();
+  }
+
+  /**
+   * Tests the case wherein:
+   *  (1) The default route (mDefaultRoute) is overridden to the value OVERRIDE_DEFAULT_ROUTE.
+   *  (2) Both mOffHostRouteUicc and mOffHostRouteEse are non-null.
+   *  (3) mAidMatchingSupport is equal to AID_MATCHING_PREFIX_ONLY
+   *  (4) mDefaultIsoDepRoute is equal to ROUTE_HOST (so that the default route is registered)
+   *  (5) NCI Version 2 is used.
+   *
+   *  Ultimately, the contents of aidMap should be committed.
+   */
+  @Test
+  public void testConfigureRoutingTestCase1_CommitsCache() {
+    when(mRoutingOptionManager.isRoutingTableOverrided()).thenReturn(true);
+    when(mRoutingOptionManager.getDefaultOffHostRoute()).thenReturn(DEFAULT_OFFHOST_ROUTE);
+    when(mRoutingOptionManager.getOverrideDefaultRoute()).thenReturn(OVERRIDE_DEFAULT_ROUTE);
+    when(mRoutingOptionManager.getOffHostRouteUicc()).thenReturn(OFFHOST_ROUTE_UICC);
+    when(mRoutingOptionManager.getOffHostRouteEse()).thenReturn(OFFHOST_ROUTE_ESE);
+    when(mRoutingOptionManager.getAidMatchingSupport()).thenReturn(AID_MATCHING_PREFIX_ONLY);
+    when(mRoutingOptionManager.getDefaultIsoDepRoute()).thenReturn(ROUTE_HOST);
+    when(mNfcService.getNciVersion()).thenReturn(NfcService.NCI_VERSION_2_0);
+    when(mNfcService.getAidRoutingTableSize()).thenReturn(0);
+    manager = new AidRoutingManager();
+    manager.mRouteForAid.put("first*", 0);
+    manager.mRouteForAid.put("second#", 0);
+    manager.mRouteForAid.put("third", 0);
+
+    boolean result = manager.configureRouting(getAidMap(), /* force = */ false);
+
+    assertThat(result).isTrue();
+    verify(mNfcService, times(4)).unrouteAids(unroutedAidsCaptor.capture());
+    assertThat(unroutedAidsCaptor.getAllValues().contains("first")).isTrue();
+    assertThat(unroutedAidsCaptor.getAllValues().contains("second#")).isTrue();
+    assertThat(unroutedAidsCaptor.getAllValues().contains("third")).isTrue();
+    assertThat(unroutedAidsCaptor.getAllValues().contains("")).isTrue();
+    verify(mNfcService, times(3)).routeAids(routedAidsCaptor.capture(),
+                                            routeCaptor.capture(),
+                                            aidTypeCaptor.capture(),
+                                            powerCaptor.capture());
+    assertThat(routedAidsCaptor.getAllValues().get(0)).isEqualTo("");
+    assertThat(routedAidsCaptor.getAllValues().get(1)).isEqualTo("firstAidEntry");
+    assertThat(routedAidsCaptor.getAllValues().get(2)).isEqualTo("fourthAidEntry");
+    assertThat(routeCaptor.getAllValues().get(0)).isEqualTo(OVERRIDE_DEFAULT_ROUTE);
+    assertThat(routeCaptor.getAllValues().get(1)).isEqualTo(OFFHOST_ROUTE_ESE[1]);
+    assertThat(routeCaptor.getAllValues().get(2)).isEqualTo(OFFHOST_ROUTE_UICC[0]);
+    assertThat(aidTypeCaptor.getAllValues().get(0))
+        .isEqualTo(RegisteredAidCache.AID_ROUTE_QUAL_PREFIX);
+    assertThat(aidTypeCaptor.getAllValues().get(1)).isEqualTo(FIRST_AID_ENTRY_AID_INFO);
+    assertThat(aidTypeCaptor.getAllValues().get(2)).isEqualTo(FOURTH_AID_ENTRY_AID_INFO);
+    assertThat(powerCaptor.getAllValues().get(0)).isEqualTo(RegisteredAidCache.POWER_STATE_ALL);
+    assertThat(powerCaptor.getAllValues().get(1)).isEqualTo(FIRST_AID_ENTRY_POWER);
+    assertThat(powerCaptor.getAllValues().get(2)).isEqualTo(FOURTH_AID_ENTRY_POWER);
+    verify(mNfcService).commitRouting();
+    assertThat(manager.mDefaultRoute).isEqualTo(OVERRIDE_DEFAULT_ROUTE);
+    assertThat(manager.mRouteForAid.size()).isEqualTo(3);
+    assertThat(manager.mPowerForAid.size()).isEqualTo(3);
+    assertThat(manager.mAidRoutingTable.size()).isEqualTo(3);
+  }
+
+  /**
+   * Tests the case wherein:
+   *  (1) The default route (mDefaultRoute) is unmodified (DEFAULT_ROUTE).
+   *  (2) Both mOffHostRouteUicc and mOffHostRouteEse are non-null.
+   *  (3) mAidMatchingSupport is equal to AID_MATCHING_PREFIX_ONLY
+   *  (4) mDefaultIsoDepRoute is equal to ROUTE_HOST (so that the default route is registered)
+   *  (5) NCI Version 1 is used.
+   *
+   *  Ultimately, nothing is committed and an error message is written to NfcStatsLog.
+   */
+  @Test
+  public void testConfigureRoutingTestCase2_WritesError() {
+    when(mRoutingOptionManager.isRoutingTableOverrided()).thenReturn(false);
+    when(mRoutingOptionManager.getDefaultOffHostRoute()).thenReturn(DEFAULT_OFFHOST_ROUTE);
+    when(mRoutingOptionManager.getDefaultRoute()).thenReturn(DEFAULT_ROUTE);
+    when(mRoutingOptionManager.getOffHostRouteUicc()).thenReturn(OFFHOST_ROUTE_UICC);
+    when(mRoutingOptionManager.getOffHostRouteEse()).thenReturn(OFFHOST_ROUTE_ESE);
+    when(mRoutingOptionManager.getAidMatchingSupport()).thenReturn(AID_MATCHING_PREFIX_ONLY);
+    when(mRoutingOptionManager.getDefaultIsoDepRoute()).thenReturn(ROUTE_HOST);
+    when(mNfcService.getNciVersion()).thenReturn(NfcService.NCI_VERSION_1_0);
+    when(mNfcService.getAidRoutingTableSize()).thenReturn(0);
+    manager = new AidRoutingManager();
+
+    boolean result = manager.configureRouting(getAidMap(), /* force = */ false);
+
+    assertThat(result).isTrue();
+    verify(mNfcService, never()).unrouteAids(anyString());
+    verify(mNfcService, never()).routeAids(anyString(), anyInt(), anyInt(), anyInt());
+    verify(mNfcService, never()).commitRouting();
+    ExtendedMockito.verify(() -> NfcStatsLog.write(codeCaptor.capture(),
+                                                   arg1Captor.capture(),
+                                                   arg2Captor.capture(),
+                                                   arg3Captor.capture()));
+    assertThat(codeCaptor.getValue()).isEqualTo(NfcStatsLog.NFC_ERROR_OCCURRED);
+    assertThat(arg1Captor.getValue()).isEqualTo(NfcStatsLog.NFC_ERROR_OCCURRED__TYPE__AID_OVERFLOW);
+    assertThat(arg2Captor.getValue()).isEqualTo(0);
+    assertThat(arg3Captor.getValue()).isEqualTo(0);
+    assertThat(manager.mDefaultRoute).isEqualTo(OFFHOST_ROUTE_ESE[1]);
+    assertThat(manager.mRouteForAid.size()).isEqualTo(3);
+    assertThat(manager.mPowerForAid.size()).isEqualTo(3);
+    assertThat(manager.mAidRoutingTable.size()).isEqualTo(3);
+  }
+
+  /**
+   * Tests the case wherein:
+   *  (1) The default route (mDefaultRoute) is unmodified (DEFAULT_ROUTE).
+   *  (2) Both mOffHostRouteUicc and mOffHostRouteEse are non-null.
+   *  (3) mAidMatchingSupport is equal to AID_MATCHING_ONLY
+   *  (4) mDefaultIsoDepRoute is equal to OVERRIDE_ISODEP_ROUTE (so that the default route is not
+   *  registered)
+   *  (5) NCI Version 2 is used.
+   *
+   *  Ultimately, the routing table is not updated and no other action is taken.
+   */
+  @Test
+  public void testConfigureRoutingTestCase3_DoNothing() {
+    when(mRoutingOptionManager.isRoutingTableOverrided()).thenReturn(false);
+    when(mRoutingOptionManager.getDefaultOffHostRoute()).thenReturn(DEFAULT_OFFHOST_ROUTE);
+    when(mRoutingOptionManager.getDefaultRoute()).thenReturn(DEFAULT_ROUTE);
+    when(mRoutingOptionManager.getOffHostRouteUicc()).thenReturn(OFFHOST_ROUTE_UICC);
+    when(mRoutingOptionManager.getOffHostRouteEse()).thenReturn(OFFHOST_ROUTE_ESE);
+    when(mRoutingOptionManager.getAidMatchingSupport()).thenReturn(AID_MATCHING_EXACT_ONLY);
+    when(mRoutingOptionManager.getDefaultIsoDepRoute()).thenReturn(OVERRIDE_ISODEP_ROUTE);
+    when(mNfcService.getNciVersion()).thenReturn(NfcService.NCI_VERSION_1_0);
+    when(mNfcService.getAidRoutingTableSize()).thenReturn(0);
+    manager = new AidRoutingManager();
+    manager.mRouteForAid.put("first*", 0);
+    manager.mRouteForAid.put("second#", 0);
+    manager.mRouteForAid.put("third", 0);
+    // Create a HashMap with only one AidEntry
+    HashMap<String, AidEntry> aidMap = new HashMap<>();
+    AidEntry aidEntry = manager.new AidEntry();
+    aidEntry.isOnHost = false;
+    aidEntry.offHostSE = "eSE2";
+    aidEntry.power = 1;
+    aidEntry.aidInfo = 2;
+    aidMap.put("firstAidEntry*", aidEntry);
+
+    boolean result = manager.configureRouting(aidMap, /* force = */ false);
+
+    assertThat(result).isTrue();
+
+    verify(mNfcService, times(3)).unrouteAids(unroutedAidsCaptor.capture());
+    assertThat(unroutedAidsCaptor.getAllValues().contains("first*")).isTrue();
+    assertThat(unroutedAidsCaptor.getAllValues().contains("second#")).isTrue();
+    assertThat(unroutedAidsCaptor.getAllValues().contains("third")).isTrue();
+    ExtendedMockito.verify(() ->
+        NfcStatsLog.write(anyInt(), anyInt(), anyInt(), anyInt()), times(0));
+    assertThat(manager.mDefaultRoute).isEqualTo(DEFAULT_ROUTE);
+    assertThat(manager.mRouteForAid.size()).isEqualTo(1);
+    assertThat(manager.mPowerForAid.size()).isEqualTo(1);
+    assertThat(manager.mAidRoutingTable.size()).isEqualTo(1);
+  }
+
+  /**
+   * Tests the case wherein:
+   *  (1) The default route (mDefaultRoute) is unmodified (DEFAULT_ROUTE).
+   *  (2) Both mOffHostRouteUicc and mOffHostRouteEse are non-null.
+   *  (3) mAidMatchingSupport is equal to AID_MATCHING_ONLY
+   *  (4) mDefaultIsoDepRoute is equal to OVERRIDE_ISODEP_ROUTE (so that the default route is not
+   *  registered)
+   *  (5) NCI Version 2 is used.
+   *
+   *  This case is identical to Test Case 3, with the exception of the value of the force variable,
+   *  which causes the cache to be committed.
+   */
+  @Test
+  public void testConfigureRoutingTestCase4_CommitsCache() {
+    when(mRoutingOptionManager.isRoutingTableOverrided()).thenReturn(false);
+    when(mRoutingOptionManager.getDefaultOffHostRoute()).thenReturn(DEFAULT_OFFHOST_ROUTE);
+    when(mRoutingOptionManager.getDefaultRoute()).thenReturn(DEFAULT_ROUTE);
+    when(mRoutingOptionManager.getOffHostRouteUicc()).thenReturn(OFFHOST_ROUTE_UICC);
+    when(mRoutingOptionManager.getOffHostRouteEse()).thenReturn(OFFHOST_ROUTE_ESE);
+    when(mRoutingOptionManager.getAidMatchingSupport()).thenReturn(AID_MATCHING_EXACT_ONLY);
+    when(mRoutingOptionManager.getDefaultIsoDepRoute()).thenReturn(OVERRIDE_ISODEP_ROUTE);
+    when(mNfcService.getNciVersion()).thenReturn(NfcService.NCI_VERSION_1_0);
+    when(mNfcService.getAidRoutingTableSize()).thenReturn(0);
+    manager = new AidRoutingManager();
+    manager.mRouteForAid.put("first*", 0);
+    manager.mRouteForAid.put("second#", 0);
+    manager.mRouteForAid.put("third", 0);
+    // Create a HashMap with only one AidEntry
+    HashMap<String, AidEntry> aidMap = new HashMap<>();
+    AidEntry aidEntry = manager.new AidEntry();
+    aidEntry.isOnHost = false;
+    aidEntry.offHostSE = "eSE2";
+    aidEntry.power = 1;
+    aidEntry.aidInfo = 2;
+    aidMap.put("firstAidEntry*", aidEntry);
+
+    boolean result = manager.configureRouting(aidMap, /* force = */ true);
+
+    assertThat(result).isTrue();
+    verify(mNfcService).commitRouting();
+  }
+
+  /**
+   * Tests the case wherein:
+   *  (1) The default route (mDefaultRoute) is overridden to the value OVERRIDE_DEFAULT_ROUTE.
+   *  (2) Both mOffHostRouteUicc and mOffHostRouteEse are null.
+   *  (3) mAidMatchingSupport is equal to AID_MATCHING_EXACT_OR_PREFIX
+   *  (4) mDefaultIsoDepRoute is equal to ROUTE_HOST (so that the default route is registered)
+   *  (5) NCI Version 2 is used.
+   *
+   *  Ultimately, the contents of aidMap should be committed.
+   */
+  @Test
+  public void testConfigureRoutingTestCase5_CommitsCache() {
+    when(mRoutingOptionManager.isRoutingTableOverrided()).thenReturn(true);
+    when(mRoutingOptionManager.getDefaultOffHostRoute()).thenReturn(DEFAULT_OFFHOST_ROUTE);
+    when(mRoutingOptionManager.getOverrideDefaultRoute()).thenReturn(OVERRIDE_DEFAULT_ROUTE);
+    when(mRoutingOptionManager.getOffHostRouteUicc()).thenReturn(null);
+    when(mRoutingOptionManager.getOffHostRouteEse()).thenReturn(null);
+    when(mRoutingOptionManager.getAidMatchingSupport()).thenReturn(AID_MATCHING_EXACT_OR_PREFIX);
+    when(mRoutingOptionManager.getDefaultIsoDepRoute()).thenReturn(ROUTE_HOST);
+    when(mNfcService.getNciVersion()).thenReturn(NfcService.NCI_VERSION_2_0);
+    when(mNfcService.getAidRoutingTableSize()).thenReturn(0);
+    manager = new AidRoutingManager();
+    manager.mRouteForAid.put("first*", 0);
+    manager.mRouteForAid.put("second#", 0);
+    manager.mRouteForAid.put("third", 0);
+
+    boolean result = manager.configureRouting(getAidMap(), /* force = */ false);
+
+    assertThat(result).isTrue();
+    verify(mNfcService, times(4)).unrouteAids(unroutedAidsCaptor.capture());
+    assertThat(unroutedAidsCaptor.getAllValues().contains("first")).isTrue();
+    assertThat(unroutedAidsCaptor.getAllValues().contains("second#")).isTrue();
+    assertThat(unroutedAidsCaptor.getAllValues().contains("third")).isTrue();
+    assertThat(unroutedAidsCaptor.getAllValues().contains("")).isTrue();
+    verify(mNfcService, times(4)).routeAids(routedAidsCaptor.capture(),
+                                            routeCaptor.capture(),
+                                            aidTypeCaptor.capture(),
+                                            powerCaptor.capture());
+    assertThat(routedAidsCaptor.getAllValues().get(0)).isEqualTo("");
+    assertThat(routedAidsCaptor.getAllValues().get(1)).isEqualTo("fourthAidEntry");
+    assertThat(routedAidsCaptor.getAllValues().get(2)).isEqualTo("thirdAidEntry");
+    assertThat(routedAidsCaptor.getAllValues().get(3)).isEqualTo("firstAidEntry");
+    assertThat(routeCaptor.getAllValues().get(0)).isEqualTo(OVERRIDE_DEFAULT_ROUTE);
+    assertThat(routeCaptor.getAllValues().get(1)).isEqualTo(DEFAULT_OFFHOST_ROUTE);
+    assertThat(routeCaptor.getAllValues().get(2)).isEqualTo(DEFAULT_OFFHOST_ROUTE);
+    assertThat(routeCaptor.getAllValues().get(3)).isEqualTo(DEFAULT_OFFHOST_ROUTE);
+    assertThat(aidTypeCaptor.getAllValues().get(0))
+        .isEqualTo(RegisteredAidCache.AID_ROUTE_QUAL_PREFIX);
+    assertThat(aidTypeCaptor.getAllValues().get(1)).isEqualTo(FOURTH_AID_ENTRY_AID_INFO);
+    assertThat(aidTypeCaptor.getAllValues().get(2)).isEqualTo(THIRD_AID_ENTRY_AID_INFO);
+    assertThat(aidTypeCaptor.getAllValues().get(3)).isEqualTo(FIRST_AID_ENTRY_AID_INFO);
+    assertThat(powerCaptor.getAllValues().get(0)).isEqualTo(RegisteredAidCache.POWER_STATE_ALL);
+    assertThat(powerCaptor.getAllValues().get(1)).isEqualTo(FOURTH_AID_ENTRY_POWER);
+    assertThat(powerCaptor.getAllValues().get(2)).isEqualTo(THIRD_AID_ENTRY_POWER);
+    assertThat(powerCaptor.getAllValues().get(3)).isEqualTo(FIRST_AID_ENTRY_POWER);
+    verify(mNfcService).commitRouting();
+    assertThat(manager.mDefaultRoute).isEqualTo(OVERRIDE_DEFAULT_ROUTE);
+    assertThat(manager.mRouteForAid.size()).isEqualTo(4);
+    assertThat(manager.mPowerForAid.size()).isEqualTo(4);
+    assertThat(manager.mAidRoutingTable.size()).isEqualTo(2);
+  }
+
+  /**
+   * Tests the case wherein:
+   *  (1) The default route (mDefaultRoute) is overridden to the value OVERRIDE_DEFAULT_ROUTE.
+   *  (2) Both mOffHostRouteUicc and mOffHostRouteEse are null.
+   *  (3) mAidMatchingSupport is equal to AID_MATCHING_EXACT_OR_SUBSET_OR_PREFIX
+   *  (4) mDefaultIsoDepRoute is equal to ROUTE_HOST (so that the default route is registered)
+   *  (5) NCI Version 2 is used.
+   *
+   *  Ultimately, the contents of aidMap should be committed.
+   */
+  @Test
+  public void testConfigureRoutingTestCase6_CommitsCache() {
+    when(mRoutingOptionManager.isRoutingTableOverrided()).thenReturn(true);
+    when(mRoutingOptionManager.getDefaultOffHostRoute()).thenReturn(DEFAULT_OFFHOST_ROUTE);
+    when(mRoutingOptionManager.getOverrideDefaultRoute()).thenReturn(OVERRIDE_DEFAULT_ROUTE);
+    when(mRoutingOptionManager.getOffHostRouteUicc()).thenReturn(null);
+    when(mRoutingOptionManager.getOffHostRouteEse()).thenReturn(null);
+    when(mRoutingOptionManager.getAidMatchingSupport())
+        .thenReturn(AID_MATCHING_EXACT_OR_SUBSET_OR_PREFIX);
+    when(mRoutingOptionManager.getDefaultIsoDepRoute()).thenReturn(ROUTE_HOST);
+    when(mNfcService.getNciVersion()).thenReturn(NfcService.NCI_VERSION_2_0);
+    when(mNfcService.getAidRoutingTableSize()).thenReturn(0);
+    manager = new AidRoutingManager();
+    manager.mRouteForAid.put("first*", 0);
+    manager.mRouteForAid.put("second#", 0);
+    manager.mRouteForAid.put("third", 0);
+
+    boolean result = manager.configureRouting(getAidMap(), /* force = */ false);
+
+    assertThat(result).isTrue();
+    verify(mNfcService, times(4)).unrouteAids(unroutedAidsCaptor.capture());
+    assertThat(unroutedAidsCaptor.getAllValues().contains("first")).isTrue();
+    assertThat(unroutedAidsCaptor.getAllValues().contains("second")).isTrue();
+    assertThat(unroutedAidsCaptor.getAllValues().contains("third")).isTrue();
+    assertThat(unroutedAidsCaptor.getAllValues().contains("")).isTrue();
+    verify(mNfcService, times(5)).routeAids(routedAidsCaptor.capture(),
+                                            routeCaptor.capture(),
+                                            aidTypeCaptor.capture(),
+                                            powerCaptor.capture());
+    assertThat(routedAidsCaptor.getAllValues().get(0)).isEqualTo("");
+    assertThat(routedAidsCaptor.getAllValues().get(1)).isEqualTo("secondAidEntry");
+    assertThat(routedAidsCaptor.getAllValues().get(2)).isEqualTo("fourthAidEntry");
+    assertThat(routedAidsCaptor.getAllValues().get(3)).isEqualTo("thirdAidEntry");
+    assertThat(routedAidsCaptor.getAllValues().get(4)).isEqualTo("firstAidEntry");
+    assertThat(routeCaptor.getAllValues().get(0)).isEqualTo(OVERRIDE_DEFAULT_ROUTE);
+    assertThat(routeCaptor.getAllValues().get(1)).isEqualTo(DEFAULT_ROUTE);
+    assertThat(routeCaptor.getAllValues().get(2)).isEqualTo(DEFAULT_OFFHOST_ROUTE);
+    assertThat(routeCaptor.getAllValues().get(3)).isEqualTo(DEFAULT_OFFHOST_ROUTE);
+    assertThat(routeCaptor.getAllValues().get(4)).isEqualTo(DEFAULT_OFFHOST_ROUTE);
+    assertThat(aidTypeCaptor.getAllValues().get(0))
+        .isEqualTo(RegisteredAidCache.AID_ROUTE_QUAL_PREFIX);
+    assertThat(aidTypeCaptor.getAllValues().get(1)).isEqualTo(SECOND_AID_ENTRY_AID_INFO);
+    assertThat(aidTypeCaptor.getAllValues().get(2)).isEqualTo(FOURTH_AID_ENTRY_AID_INFO);
+    assertThat(aidTypeCaptor.getAllValues().get(3)).isEqualTo(THIRD_AID_ENTRY_AID_INFO);
+    assertThat(aidTypeCaptor.getAllValues().get(4)).isEqualTo(FIRST_AID_ENTRY_AID_INFO);
+    assertThat(powerCaptor.getAllValues().get(0)).isEqualTo(RegisteredAidCache.POWER_STATE_ALL);
+    assertThat(powerCaptor.getAllValues().get(1)).isEqualTo(SECOND_AID_ENTRY_POWER);
+    assertThat(powerCaptor.getAllValues().get(2)).isEqualTo(FOURTH_AID_ENTRY_POWER);
+    assertThat(powerCaptor.getAllValues().get(3)).isEqualTo(THIRD_AID_ENTRY_POWER);
+    assertThat(powerCaptor.getAllValues().get(4)).isEqualTo(FIRST_AID_ENTRY_POWER);
+    verify(mNfcService).commitRouting();
+    assertThat(manager.mDefaultRoute).isEqualTo(OVERRIDE_DEFAULT_ROUTE);
+    assertThat(manager.mRouteForAid.size()).isEqualTo(4);
+    assertThat(manager.mPowerForAid.size()).isEqualTo(4);
+    assertThat(manager.mAidRoutingTable.size()).isEqualTo(2);
+  }
+
+  /**
+   * Tests the case wherein:
+   *  (1) The default route (mDefaultRoute) is unmodified (DEFAULT_ROUTE).
+   *  (2) Both mOffHostRouteUicc and mOffHostRouteEse are null.
+   *  (3) mAidMatchingSupport is equal to AID_MATCHING_PREFIX_ONLY
+   *  (4) mDefaultIsoDepRoute is equal to ROUTE_HOST (so that the default route is registered)
+   *  (5) NCI Version 1 is used.
+   *
+   *  Ultimately, due to the value of mAidRoutingTableSize, the contents of the cache are committed.
+   */
+  @Test
+  public void testConfigureRoutingTestCase7_CommitsCache() {
+    when(mRoutingOptionManager.isRoutingTableOverrided()).thenReturn(false);
+    when(mRoutingOptionManager.getDefaultOffHostRoute()).thenReturn(DEFAULT_OFFHOST_ROUTE);
+    when(mRoutingOptionManager.getDefaultRoute()).thenReturn(DEFAULT_ROUTE);
+    when(mRoutingOptionManager.getOffHostRouteUicc()).thenReturn(null);
+    when(mRoutingOptionManager.getOffHostRouteEse()).thenReturn(null);
+    when(mRoutingOptionManager.getAidMatchingSupport()).thenReturn(AID_MATCHING_PREFIX_ONLY);
+    when(mRoutingOptionManager.getDefaultIsoDepRoute()).thenReturn(ROUTE_HOST);
+    when(mNfcService.getNciVersion()).thenReturn(NfcService.NCI_VERSION_1_0);
+    when(mNfcService.getAidRoutingTableSize()).thenReturn(100);
+    manager = new AidRoutingManager();
+
+    boolean result = manager.configureRouting(getAidMap(), /* force = */ false);
+
+    assertThat(result).isTrue();
+    verify(mNfcService, never()).unrouteAids(anyString());
+    verify(mNfcService, times(3)).routeAids(routedAidsCaptor.capture(),
+                                            routeCaptor.capture(),
+                                            aidTypeCaptor.capture(),
+                                            powerCaptor.capture());
+    assertThat(routedAidsCaptor.getAllValues().get(0)).isEqualTo("fourthAidEntry");
+    assertThat(routedAidsCaptor.getAllValues().get(1)).isEqualTo("firstAidEntry");
+    assertThat(routedAidsCaptor.getAllValues().get(2)).isEqualTo("thirdAidEntry");
+    assertThat(routeCaptor.getAllValues().get(0)).isEqualTo(DEFAULT_OFFHOST_ROUTE);
+    assertThat(routeCaptor.getAllValues().get(1)).isEqualTo(DEFAULT_OFFHOST_ROUTE);
+    assertThat(routeCaptor.getAllValues().get(2)).isEqualTo(DEFAULT_OFFHOST_ROUTE);
+    assertThat(aidTypeCaptor.getAllValues().get(0)).isEqualTo(FOURTH_AID_ENTRY_AID_INFO);
+    assertThat(aidTypeCaptor.getAllValues().get(1)).isEqualTo(FIRST_AID_ENTRY_AID_INFO);
+    assertThat(aidTypeCaptor.getAllValues().get(2)).isEqualTo(THIRD_AID_ENTRY_AID_INFO);
+    assertThat(powerCaptor.getAllValues().get(0)).isEqualTo(FOURTH_AID_ENTRY_POWER);
+    assertThat(powerCaptor.getAllValues().get(1)).isEqualTo(FIRST_AID_ENTRY_POWER);
+    assertThat(powerCaptor.getAllValues().get(2)).isEqualTo(THIRD_AID_ENTRY_POWER);
+    verify(mNfcService).commitRouting();
+    assertThat(manager.mDefaultRoute).isEqualTo(DEFAULT_ROUTE);
+    assertThat(manager.mRouteForAid.size()).isEqualTo(4);
+    assertThat(manager.mPowerForAid.size()).isEqualTo(4);
+    assertThat(manager.mAidRoutingTable.size()).isEqualTo(2);
+  }
+
+  @Test
+  public void testOnNfccRoutingTableCleared() {
+    manager = new AidRoutingManager();
+    manager.mAidRoutingTable.put(0, new HashSet<String>());
+    manager.mRouteForAid.put("", 0);
+    manager.mPowerForAid.put("", 0);
+
+    manager.onNfccRoutingTableCleared();
+
+    assertThat(manager.mAidRoutingTable.size()).isEqualTo(0);
+    assertThat(manager.mRouteForAid.isEmpty()).isTrue();
+    assertThat(manager.mPowerForAid.isEmpty()).isTrue();
+  }
+
+  @Test
+  public void testDump() {
+    manager = new AidRoutingManager();
+    HashSet<String> routingTableSet = new HashSet<>();
+    routingTableSet.add("");
+    manager.mAidRoutingTable.put(0, routingTableSet);
+
+    manager.dump(/* fd = */ null, mPw, /* args = */ null);
+
+    verify(mPw, times(4)).println(anyString());
+  }
+
+  private HashMap<String, AidEntry> getAidMap() {
+    HashMap<String, AidEntry> aidMap = new HashMap<>();
+    AidEntry firstAidEntry = manager.new AidEntry();
+    firstAidEntry.isOnHost = false;
+    firstAidEntry.offHostSE = "eSE2";
+    firstAidEntry.power = FIRST_AID_ENTRY_POWER;
+    firstAidEntry.aidInfo = FIRST_AID_ENTRY_AID_INFO;
+    aidMap.put("firstAidEntry*", firstAidEntry);
+
+    AidEntry secondAidEntry = manager.new AidEntry();
+    secondAidEntry.isOnHost = true;
+    secondAidEntry.power = SECOND_AID_ENTRY_POWER;
+    secondAidEntry.aidInfo = SECOND_AID_ENTRY_AID_INFO;
+    aidMap.put("secondAidEntry#", secondAidEntry);
+
+    AidEntry thirdAidEntry = manager.new AidEntry();
+    thirdAidEntry.isOnHost = false;
+    thirdAidEntry.offHostSE = "invalid SE";
+    thirdAidEntry.power = THIRD_AID_ENTRY_POWER;
+    thirdAidEntry.aidInfo = THIRD_AID_ENTRY_AID_INFO;
+    aidMap.put("thirdAidEntry", thirdAidEntry);
+
+    AidEntry fourthAidEntry = manager.new AidEntry();
+    fourthAidEntry.isOnHost = false;
+    fourthAidEntry.offHostSE = "SIM1";
+    fourthAidEntry.power = FOURTH_AID_ENTRY_POWER;
+    fourthAidEntry.aidInfo = FOURTH_AID_ENTRY_AID_INFO;
+    aidMap.put("fourthAidEntry", fourthAidEntry);
+
+    return aidMap;
+  }
+}
\ No newline at end of file
diff --git a/tests/unit/src/com/android/nfc/cardemulation/CardEmulationManagerTest.java b/tests/unit/src/com/android/nfc/cardemulation/CardEmulationManagerTest.java
new file mode 100644
index 0000000..0fbb036
--- /dev/null
+++ b/tests/unit/src/com/android/nfc/cardemulation/CardEmulationManagerTest.java
@@ -0,0 +1,1956 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.nfc.cardemulation;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.res.Resources;
+import android.nfc.NfcAdapter;
+import android.nfc.cardemulation.AidGroup;
+import android.nfc.cardemulation.ApduServiceInfo;
+import android.nfc.cardemulation.CardEmulation;
+import android.nfc.cardemulation.NfcFServiceInfo;
+import android.nfc.cardemulation.PollingFrame;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.nfc.ForegroundUtils;
+import com.android.nfc.NfcPermissions;
+import com.android.nfc.NfcService;
+import com.android.nfc.R;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+import java.util.List;
+
+public class CardEmulationManagerTest {
+
+    private static final int USER_ID = 0;
+    private static final UserHandle USER_HANDLE = UserHandle.of(USER_ID);
+    private static final byte[] TEST_DATA_1 = new byte[] {(byte) 0xd2};
+    private static final byte[] TEST_DATA_2 = new byte[] {(byte) 0xd3};
+    private static final byte[] PROPER_SKIP_DATA_NDF1_HEADER = new byte[]
+            {0x00, (byte) 0xa4, 0x04, 0x00, (byte)0x07, (byte) 0xd2, 0x76, 0x00, 0x00,
+                    (byte) 0x85, 0x01, 0x00};
+    private static final byte[] PROPER_SKIP_DATA_NDF2_HEADER = new byte[]
+            {0x00, (byte) 0xa4, 0x04, 0x00, (byte)0x07, (byte) 0xd2, 0x76, 0x00, 0x00,
+                    (byte) 0x85, 0x01, 0x01};
+    private static final String WALLET_HOLDER_PACKAGE_NAME = "com.android.test.walletroleholder";
+    private static final List<PollingFrame> POLLING_LOOP_FRAMES = List.of();
+    private static final List<ApduServiceInfo> UPDATED_SERVICES = List.of();
+    private static final List<NfcFServiceInfo> UPDATED_NFC_SERVICES = List.of();
+    private static final ComponentName WALLET_PAYMENT_SERVICE
+            = new ComponentName(WALLET_HOLDER_PACKAGE_NAME,
+            "com.android.test.walletroleholder.WalletRoleHolderApduService");
+    private static final String PAYMENT_AID_1 = "A000000004101012";
+
+    @Mock
+    private Context mContext;
+    @Mock
+    private Resources mResources;
+    @Mock
+    private ForegroundUtils mForegroundUtils;
+    @Mock
+    private WalletRoleObserver mWalletRoleObserver;
+    @Mock
+    private RegisteredAidCache mRegisteredAidCache;
+    @Mock
+    private RegisteredT3tIdentifiersCache mRegisteredT3tIdentifiersCache;
+    @Mock
+    private HostEmulationManager mHostEmulationManager;
+    @Mock
+    private HostNfcFEmulationManager mHostNfcFEmulationManager;
+    @Mock
+    private RegisteredServicesCache mRegisteredServicesCache;
+    @Mock
+    private RegisteredNfcFServicesCache mRegisteredNfcFServicesCache;
+    @Mock
+    private PreferredServices mPreferredServices;
+    @Mock
+    private EnabledNfcFServices mEnabledNfcFServices;
+    @Mock
+    private RoutingOptionManager mRoutingOptionManager;
+    @Mock
+    private PowerManager mPowerManager;
+    @Mock
+    private NfcService mNfcService;
+    @Mock
+    private UserManager mUserManager;
+    @Mock
+    private NfcAdapter mNfcAdapter;
+    @Captor
+    private ArgumentCaptor<List<PollingFrame>> mPollingLoopFrameCaptor;
+    @Captor
+    private ArgumentCaptor<byte[]> mDataCaptor;
+    @Captor
+    private ArgumentCaptor<List<ApduServiceInfo>> mServiceListCaptor;
+    @Captor
+    private ArgumentCaptor<List<NfcFServiceInfo>> mNfcServiceListCaptor;
+    private MockitoSession mStaticMockSession;
+    private CardEmulationManager mCardEmulationManager;
+    @Before
+    public void setUp() {
+        mStaticMockSession = ExtendedMockito.mockitoSession()
+                .mockStatic(ActivityManager.class)
+                .mockStatic(NfcPermissions.class)
+                .mockStatic(android.nfc.Flags.class)
+                .strictness(Strictness.LENIENT)
+                .mockStatic(NfcService.class)
+                .mockStatic(Binder.class)
+                .mockStatic(UserHandle.class)
+                .mockStatic(NfcAdapter.class)
+                .startMocking();
+        MockitoAnnotations.initMocks(this);
+        when(NfcAdapter.getDefaultAdapter(mContext)).thenReturn(mNfcAdapter);
+        when(NfcService.getInstance()).thenReturn(mNfcService);
+        when(ActivityManager.getCurrentUser()).thenReturn(USER_ID);
+        when(UserHandle.getUserHandleForUid(anyInt())).thenReturn(USER_HANDLE);
+        when(mContext.createContextAsUser(
+                any(), anyInt())).thenReturn(mContext);
+        when(mContext.getResources()).thenReturn(mResources);
+        when(mContext.getSystemService(eq(UserManager.class))).thenReturn(mUserManager);
+        mCardEmulationManager = createInstanceWithMockParams();
+    }
+
+    @After
+    public void tearDown() {
+        mStaticMockSession.finishMocking();
+    }
+
+    @Test
+    public void testConstructor() {
+        assertConstructorMethodCalls();
+    }
+
+    private void assertConstructorMethodCalls() {
+        verify(mRoutingOptionManager).getOffHostRouteEse();
+        verify(mRoutingOptionManager).getOffHostRouteUicc();
+        verify(mRegisteredServicesCache).initialize();
+        verify(mRegisteredNfcFServicesCache).initialize();
+        verify(mWalletRoleObserver).isWalletRoleFeatureEnabled();
+        verify(mWalletRoleObserver).getDefaultWalletRoleHolder(eq(USER_ID));
+        verify(mPreferredServices).onWalletRoleHolderChanged(eq(WALLET_HOLDER_PACKAGE_NAME),
+                eq(USER_ID));
+        verify(mRegisteredAidCache).onWalletRoleHolderChanged(eq(WALLET_HOLDER_PACKAGE_NAME),
+                eq(USER_ID));
+    }
+
+    @Test
+    public void testGetters() {
+        Assert.assertNotNull(mCardEmulationManager.getNfcCardEmulationInterface());
+        Assert.assertNotNull(mCardEmulationManager.getNfcFCardEmulationInterface());
+    }
+
+    @Test
+    public void testPollingLoopDetected() {
+        mCardEmulationManager.onPollingLoopDetected(POLLING_LOOP_FRAMES);
+
+        verify(mHostEmulationManager).onPollingLoopDetected(mPollingLoopFrameCaptor.capture());
+        Assert.assertEquals(mPollingLoopFrameCaptor.getValue(), POLLING_LOOP_FRAMES);
+    }
+
+    @Test
+    public void testOnHostCardEmulationActivated_technologyApdu() {
+        mCardEmulationManager.onHostCardEmulationActivated(CardEmulationManager.NFC_HCE_APDU);
+
+        verify(mPowerManager).userActivity(anyLong(), eq(PowerManager.USER_ACTIVITY_EVENT_TOUCH),
+                eq(PowerManager.USER_ACTIVITY_FLAG_INDIRECT));
+        verify(mHostEmulationManager).onHostEmulationActivated();
+        verify(mPreferredServices).onHostEmulationActivated();
+        Assert.assertFalse(mCardEmulationManager.mNotSkipAid);
+        verifyZeroInteractions(mHostNfcFEmulationManager);
+        verifyZeroInteractions(mEnabledNfcFServices);
+    }
+
+    @Test
+    public void testOnHostCardEmulationActivated_technologyNfcf() {
+        mCardEmulationManager.onHostCardEmulationActivated(CardEmulationManager.NFC_HCE_NFCF);
+
+        assertConstructorMethodCalls();
+        verify(mPowerManager).userActivity(anyLong(), eq(PowerManager.USER_ACTIVITY_EVENT_TOUCH),
+                eq(PowerManager.USER_ACTIVITY_FLAG_INDIRECT));
+        verify(mHostNfcFEmulationManager).onHostEmulationActivated();
+        verify(mRegisteredNfcFServicesCache).onHostEmulationActivated();
+        verify(mEnabledNfcFServices).onHostEmulationActivated();
+        verifyZeroInteractions(mHostEmulationManager);
+        verifyZeroInteractions(mPreferredServices);
+    }
+
+    @Test
+    public void testSkipAid_nullData_isFalse() {
+        mCardEmulationManager.mNotSkipAid = false;
+        Assert.assertFalse(mCardEmulationManager.isSkipAid(null));
+    }
+
+    @Test
+    public void testSkipAid_notSkipTrue_isFalse() {
+        mCardEmulationManager.mNotSkipAid = true;
+        Assert.assertFalse(mCardEmulationManager.isSkipAid(TEST_DATA_1));
+    }
+
+    @Test
+    public void testSkipAid_wrongData_isFalse() {
+        mCardEmulationManager.mNotSkipAid = false;
+        Assert.assertFalse(mCardEmulationManager.isSkipAid(TEST_DATA_1));
+    }
+
+    @Test
+    public void testSkipAid_ndf1_isTrue() {
+        mCardEmulationManager.mNotSkipAid = false;
+        Assert.assertTrue(mCardEmulationManager.isSkipAid(PROPER_SKIP_DATA_NDF1_HEADER));
+    }
+
+    @Test
+    public void testSkipAid_ndf2_isTrue() {
+        mCardEmulationManager.mNotSkipAid = false;
+        Assert.assertTrue(mCardEmulationManager.isSkipAid(PROPER_SKIP_DATA_NDF2_HEADER));
+    }
+
+    @Test
+    public void testOnHostCardEmulationData_technologyApdu_skipData() {
+        mCardEmulationManager.onHostCardEmulationData(CardEmulationManager.NFC_HCE_APDU,
+                PROPER_SKIP_DATA_NDF1_HEADER);
+
+        verify(mHostEmulationManager).onHostEmulationData(mDataCaptor.capture());
+        Assert.assertEquals(PROPER_SKIP_DATA_NDF1_HEADER, mDataCaptor.getValue());
+        verifyZeroInteractions(mHostNfcFEmulationManager);
+        verifyZeroInteractions(mPowerManager);
+    }
+
+    @Test
+    public void testOnHostCardEmulationData_technologyNfcf_DontSkipData() {
+        mCardEmulationManager.onHostCardEmulationData(CardEmulationManager.NFC_HCE_NFCF,
+                PROPER_SKIP_DATA_NDF1_HEADER);
+
+        verify(mHostNfcFEmulationManager).onHostEmulationData(mDataCaptor.capture());
+        Assert.assertEquals(PROPER_SKIP_DATA_NDF1_HEADER, mDataCaptor.getValue());
+        verifyZeroInteractions(mHostEmulationManager);
+        verify(mPowerManager).userActivity(anyLong(), eq(PowerManager.USER_ACTIVITY_EVENT_TOUCH),
+                eq(0));
+    }
+
+    @Test
+    public void testOnHostCardEmulationDeactivated_technologyApdu() {
+        mCardEmulationManager.onHostCardEmulationDeactivated(CardEmulationManager.NFC_HCE_APDU);
+
+        assertConstructorMethodCalls();
+        verify(mHostEmulationManager).onHostEmulationDeactivated();
+        verify(mPreferredServices).onHostEmulationDeactivated();
+        verifyZeroInteractions(mHostNfcFEmulationManager);
+        verifyZeroInteractions(mRegisteredNfcFServicesCache);
+        verifyZeroInteractions(mEnabledNfcFServices);
+    }
+
+    @Test
+    public void testOnHostCardEmulationDeactivated_technologyNfcf() {
+        mCardEmulationManager.onHostCardEmulationDeactivated(CardEmulationManager.NFC_HCE_NFCF);
+
+        assertConstructorMethodCalls();
+        verify(mHostNfcFEmulationManager).onHostEmulationDeactivated();
+        verify(mRegisteredNfcFServicesCache).onHostEmulationDeactivated();
+        verify(mEnabledNfcFServices).onHostEmulationDeactivated();
+        verifyZeroInteractions(mHostEmulationManager);
+        verifyZeroInteractions(mPreferredServices);
+    }
+
+    @Test
+    public void testOnOffHostAidSelected() {
+        mCardEmulationManager.onOffHostAidSelected();
+
+        assertConstructorMethodCalls();
+        verify(mHostEmulationManager).onOffHostAidSelected();
+    }
+
+    @Test
+    public void testOnUserSwitched() {
+        mCardEmulationManager.onUserSwitched(USER_ID);
+
+        assertConstructorMethodCalls();
+        verify(mWalletRoleObserver).onUserSwitched(eq(USER_ID));
+        verify(mRegisteredServicesCache).onUserSwitched();
+        verify(mPreferredServices).onUserSwitched(eq(USER_ID));
+        verify(mHostNfcFEmulationManager).onUserSwitched();
+        verify(mRegisteredT3tIdentifiersCache).onUserSwitched();
+        verify(mEnabledNfcFServices).onUserSwitched(eq(USER_ID));
+        verify(mRegisteredNfcFServicesCache).onUserSwitched();
+    }
+
+    @Test
+    public void testOnManagedProfileChanged() {
+        mCardEmulationManager.onManagedProfileChanged();
+
+        assertConstructorMethodCalls();
+        verify(mRegisteredServicesCache).onManagedProfileChanged();
+        verify(mRegisteredNfcFServicesCache).onManagedProfileChanged();
+    }
+
+    @Test
+    public void testOnNfcEnabled() {
+        mCardEmulationManager.onNfcEnabled();
+
+        assertConstructorMethodCalls();
+        verify(mRegisteredAidCache).onNfcEnabled();
+        verify(mRegisteredT3tIdentifiersCache).onNfcEnabled();
+    }
+
+    @Test
+    public void testOnNfcDisabled() {
+        mCardEmulationManager.onNfcDisabled();
+
+        assertConstructorMethodCalls();
+        verify(mRegisteredAidCache).onNfcDisabled();
+        verify(mHostNfcFEmulationManager).onNfcDisabled();
+        verify(mRegisteredNfcFServicesCache).onNfcDisabled();
+        verify(mEnabledNfcFServices).onNfcDisabled();
+        verify(mRegisteredT3tIdentifiersCache).onNfcDisabled();
+    }
+
+    @Test
+    public void testOnSecureNfcToggled() {
+        mCardEmulationManager.onSecureNfcToggled();
+
+        verify(mRegisteredAidCache).onSecureNfcToggled();
+        verify(mRegisteredT3tIdentifiersCache).onSecureNfcToggled();
+    }
+
+    @Test
+    public void testOnServicesUpdated_walletEnabledPollingLoopDisabled() {
+        when(mWalletRoleObserver.isWalletRoleFeatureEnabled()).thenReturn(true);
+        when(android.nfc.Flags.nfcReadPollingLoop()).thenReturn(false);
+
+        mCardEmulationManager.onServicesUpdated(USER_ID, UPDATED_SERVICES, false);
+
+        verify(mWalletRoleObserver, times(2)).isWalletRoleFeatureEnabled();
+        verify(mRegisteredAidCache).onServicesUpdated(eq(USER_ID), mServiceListCaptor.capture());
+        verify(mPreferredServices).onServicesUpdated();
+        Assert.assertEquals(UPDATED_SERVICES, mServiceListCaptor.getValue());
+        verifyZeroInteractions(mHostEmulationManager);
+        verify(mNfcService).onPreferredPaymentChanged(eq(NfcAdapter.PREFERRED_PAYMENT_UPDATED));
+    }
+
+    @Test
+    public void testOnServicesUpdated_walletEnabledPollingLoopEnabled() {
+        when(mWalletRoleObserver.isWalletRoleFeatureEnabled()).thenReturn(true);
+        when(android.nfc.Flags.nfcReadPollingLoop()).thenReturn(true);
+
+        mCardEmulationManager.onServicesUpdated(USER_ID, UPDATED_SERVICES, false);
+
+        verify(mWalletRoleObserver, times(2)).isWalletRoleFeatureEnabled();
+        verify(mRegisteredAidCache).onServicesUpdated(eq(USER_ID), mServiceListCaptor.capture());
+        verify(mPreferredServices).onServicesUpdated();
+        verify(mHostEmulationManager).updatePollingLoopFilters(eq(USER_ID),
+                mServiceListCaptor.capture());
+        verify(mNfcService).onPreferredPaymentChanged(eq(NfcAdapter.PREFERRED_PAYMENT_UPDATED));
+        Assert.assertEquals(UPDATED_SERVICES, mServiceListCaptor.getAllValues().getFirst());
+        Assert.assertEquals(UPDATED_SERVICES, mServiceListCaptor.getAllValues().getLast());
+    }
+
+    @Test
+    public void testOnNfcFServicesUpdated() {
+        mCardEmulationManager.onNfcFServicesUpdated(USER_ID, UPDATED_NFC_SERVICES);
+
+        verify(mRegisteredT3tIdentifiersCache).onServicesUpdated(eq(USER_ID),
+                mNfcServiceListCaptor.capture());
+        Assert.assertEquals(UPDATED_NFC_SERVICES, mNfcServiceListCaptor.getValue());
+    }
+
+    @Test
+    public void testIsServiceRegistered_serviceExists() {
+        when(mRegisteredServicesCache.hasService(eq(USER_ID), any())).thenReturn(true);
+
+        Assert.assertTrue(mCardEmulationManager
+                .isServiceRegistered(USER_ID, WALLET_PAYMENT_SERVICE));
+
+        verify(mRegisteredServicesCache, times(2))
+                .hasService(eq(USER_ID),eq(WALLET_PAYMENT_SERVICE));
+    }
+
+    @Test
+    public void testIsServiceRegistered_serviceDoesNotExists() {
+        when(mRegisteredServicesCache.hasService(eq(USER_ID), any())).thenReturn(false);
+
+        Assert.assertFalse(mCardEmulationManager
+                .isServiceRegistered(USER_ID, WALLET_PAYMENT_SERVICE));
+
+        verify(mRegisteredServicesCache).invalidateCache(eq(USER_ID), eq(true));
+        verify(mRegisteredServicesCache, times(2))
+                .hasService(eq(USER_ID),eq(WALLET_PAYMENT_SERVICE));
+    }
+
+    @Test
+    public void testIsNfcServiceInstalled_serviceExists() {
+        when(mRegisteredNfcFServicesCache.hasService(eq(USER_ID), any())).thenReturn(true);
+
+        Assert.assertTrue(mCardEmulationManager
+                .isNfcFServiceInstalled(USER_ID, WALLET_PAYMENT_SERVICE));
+
+        verify(mRegisteredNfcFServicesCache, times(2))
+                .hasService(eq(USER_ID),eq(WALLET_PAYMENT_SERVICE));
+    }
+
+    @Test
+    public void testIsNfcServiceInstalled_serviceDoesNotExists() {
+        when(mRegisteredNfcFServicesCache.hasService(eq(USER_ID), any())).thenReturn(false);
+
+        Assert.assertFalse(mCardEmulationManager
+                .isNfcFServiceInstalled(USER_ID, WALLET_PAYMENT_SERVICE));
+
+        verify(mRegisteredNfcFServicesCache).invalidateCache(eq(USER_ID));
+        verify(mRegisteredNfcFServicesCache, times(2))
+                .hasService(eq(USER_ID),eq(WALLET_PAYMENT_SERVICE));
+    }
+
+    @Test
+    public void testPackageHasPreferredService() {
+        when(mPreferredServices.packageHasPreferredService(eq(WALLET_HOLDER_PACKAGE_NAME)))
+                .thenReturn(true);
+
+        Assert.assertTrue(mCardEmulationManager
+                .packageHasPreferredService(WALLET_HOLDER_PACKAGE_NAME));
+
+        verify(mPreferredServices).packageHasPreferredService(eq(WALLET_HOLDER_PACKAGE_NAME));
+    }
+
+    @Test
+    public void testCardEmulationIsDefaultServiceForCategory_serviceExistsWalletEnabled()
+            throws RemoteException {
+        when(mRegisteredServicesCache.hasService(eq(USER_ID), any())).thenReturn(true);
+        when(mWalletRoleObserver.isWalletRoleFeatureEnabled()).thenReturn(true);
+        when(mWalletRoleObserver.getDefaultWalletRoleHolder(eq(USER_ID)))
+                .thenReturn(WALLET_HOLDER_PACKAGE_NAME);
+
+        Assert.assertTrue(mCardEmulationManager.getNfcCardEmulationInterface()
+                .isDefaultServiceForCategory(USER_ID, WALLET_PAYMENT_SERVICE,
+                        CardEmulation.CATEGORY_PAYMENT));
+
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.validateUserId(USER_ID);
+        });
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.enforceUserPermissions(mContext);
+        });
+        verify(mRegisteredServicesCache, times(2))
+                .hasService(eq(USER_ID),eq(WALLET_PAYMENT_SERVICE));
+    }
+
+    @Test
+    public void testCardEmulationIsDefaultServiceForCategory_serviceDoesNotExists()
+            throws RemoteException {
+        when(mRegisteredServicesCache.hasService(eq(USER_ID), any())).thenReturn(false);
+
+        assertConstructorMethodCalls();
+        Assert.assertFalse(mCardEmulationManager.getNfcCardEmulationInterface()
+                .isDefaultServiceForCategory(USER_ID, WALLET_PAYMENT_SERVICE,
+                        CardEmulation.CATEGORY_PAYMENT));
+
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.validateUserId(USER_ID);
+        });
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.enforceUserPermissions(mContext);
+        });
+        verifyZeroInteractions(mWalletRoleObserver);
+        verify(mRegisteredServicesCache).invalidateCache(eq(USER_ID), eq(true));
+        verify(mRegisteredServicesCache, times(2))
+                .hasService(eq(USER_ID),eq(WALLET_PAYMENT_SERVICE));
+    }
+
+    @Test
+    public void testCardEmulationIsDefaultServiceForAid_serviceExists()
+            throws RemoteException {
+        when(mRegisteredServicesCache.hasService(eq(USER_ID), any())).thenReturn(true);
+        when(mRegisteredAidCache.isDefaultServiceForAid(eq(USER_ID), any(), eq(PAYMENT_AID_1)))
+                .thenReturn(true);
+
+        Assert.assertTrue(mCardEmulationManager.getNfcCardEmulationInterface()
+                .isDefaultServiceForAid(USER_ID, WALLET_PAYMENT_SERVICE,
+                        PAYMENT_AID_1));
+
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.validateUserId(USER_ID);
+        });
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.validateUserId(USER_ID);
+        });
+        verify(mRegisteredAidCache).isDefaultServiceForAid(eq(USER_ID), eq(WALLET_PAYMENT_SERVICE),
+                eq(PAYMENT_AID_1));
+        verify(mRegisteredServicesCache, times(2))
+                .hasService(eq(USER_ID),eq(WALLET_PAYMENT_SERVICE));
+    }
+
+    @Test
+    public void testCardEmulationIsDefaultServiceForAid_serviceDoesNotExists()
+            throws RemoteException {
+        when(mRegisteredServicesCache.hasService(eq(USER_ID), any())).thenReturn(false);
+
+        Assert.assertFalse(mCardEmulationManager.getNfcCardEmulationInterface()
+                .isDefaultServiceForAid(USER_ID, WALLET_PAYMENT_SERVICE,
+                        PAYMENT_AID_1));
+
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.validateUserId(USER_ID);
+        });
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.enforceUserPermissions(mContext);
+        });
+        verify(mRegisteredAidCache).onWalletRoleHolderChanged(eq(WALLET_HOLDER_PACKAGE_NAME),
+                eq(USER_ID));
+        verify(mRegisteredServicesCache).invalidateCache(eq(USER_ID), eq(true));
+        verify(mRegisteredServicesCache, times(2))
+                .hasService(eq(USER_ID),eq(WALLET_PAYMENT_SERVICE));
+        verifyZeroInteractions(mRegisteredAidCache);
+    }
+
+    @Test
+    public void testCardEmulationSetDefaultForNextTap_serviceExists()
+            throws RemoteException {
+        when(mRegisteredServicesCache.hasService(eq(USER_ID), any())).thenReturn(true);
+        when(mPreferredServices.setDefaultForNextTap(anyInt(), any())).thenReturn(true);
+
+        Assert.assertTrue(mCardEmulationManager.getNfcCardEmulationInterface()
+                .setDefaultForNextTap(USER_ID, WALLET_PAYMENT_SERVICE));
+
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.validateProfileId(mContext, USER_ID);
+        });
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.enforceAdminPermissions(mContext);
+        });
+        verify(mPreferredServices).setDefaultForNextTap(eq(USER_ID), eq(WALLET_PAYMENT_SERVICE));
+        verify(mRegisteredServicesCache, times(2))
+                .hasService(eq(USER_ID),eq(WALLET_PAYMENT_SERVICE));
+    }
+
+    @Test
+    public void testCardEmulationSetDefaultForNextTap_serviceDoesNotExists()
+            throws RemoteException {
+        when(mRegisteredServicesCache.hasService(eq(USER_ID), any())).thenReturn(false);
+
+        Assert.assertFalse(mCardEmulationManager.getNfcCardEmulationInterface()
+                .setDefaultForNextTap(USER_ID, WALLET_PAYMENT_SERVICE));
+
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.validateProfileId(mContext, USER_ID);
+        });
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.enforceAdminPermissions(mContext);
+        });
+        verify(mRegisteredServicesCache).invalidateCache(eq(USER_ID), eq(true));
+        verify(mRegisteredServicesCache, times(2))
+                .hasService(eq(USER_ID),eq(WALLET_PAYMENT_SERVICE));
+        verify(mPreferredServices).onWalletRoleHolderChanged(eq(WALLET_HOLDER_PACKAGE_NAME),
+                eq(USER_ID));
+        verifyZeroInteractions(mPreferredServices);
+    }
+
+    @Test
+    public void testCardEmulationSetShouldDefaultToObserveModeForService_serviceExists()
+            throws RemoteException {
+        when(mRegisteredServicesCache.hasService(eq(USER_ID), any())).thenReturn(true);
+        when(mRegisteredServicesCache.setShouldDefaultToObserveModeForService(anyInt(), anyInt(),
+                any(), anyBoolean())).thenReturn(true);
+
+        Assert.assertTrue(mCardEmulationManager.getNfcCardEmulationInterface()
+                .setShouldDefaultToObserveModeForService(USER_ID, WALLET_PAYMENT_SERVICE,
+                        true));
+
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.validateUserId(USER_ID);
+        });
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.enforceUserPermissions(mContext);
+        });
+        verify(mRegisteredServicesCache).initialize();
+        verify(mRegisteredServicesCache).setShouldDefaultToObserveModeForService(eq(USER_ID),
+                anyInt(), eq(WALLET_PAYMENT_SERVICE), eq(true));
+        verify(mRegisteredServicesCache, times(2))
+                .hasService(eq(USER_ID),eq(WALLET_PAYMENT_SERVICE));
+        verifyNoMoreInteractions(mRegisteredServicesCache);
+    }
+
+    @Test
+    public void testCardEmulationSetShouldDefaultToObserveModeForService_serviceDoesNotExists()
+            throws RemoteException {
+        when(mRegisteredServicesCache.hasService(eq(USER_ID), any())).thenReturn(false);
+
+        Assert.assertFalse(mCardEmulationManager.getNfcCardEmulationInterface()
+                .setShouldDefaultToObserveModeForService(USER_ID, WALLET_PAYMENT_SERVICE,
+                        false));
+
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.validateUserId(USER_ID);
+        });
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.enforceUserPermissions(mContext);
+        });
+        verify(mRegisteredServicesCache).initialize();
+        verify(mRegisteredServicesCache).invalidateCache(eq(USER_ID), eq(true));
+        verify(mRegisteredServicesCache, times(2))
+                .hasService(eq(USER_ID),eq(WALLET_PAYMENT_SERVICE));
+        verifyNoMoreInteractions(mRegisteredServicesCache);
+    }
+
+    @Test
+    public void testCardEmulationRegisterAidGroupForService_serviceExists()
+            throws RemoteException {
+        when(mRegisteredServicesCache.hasService(eq(USER_ID), any())).thenReturn(true);
+        when(mRegisteredServicesCache.registerAidGroupForService(eq(USER_ID), anyInt(), any(),
+                any())).thenReturn(true);
+        AidGroup aidGroup = Mockito.mock(AidGroup.class);
+
+        Assert.assertTrue(mCardEmulationManager.getNfcCardEmulationInterface()
+                .registerAidGroupForService(USER_ID, WALLET_PAYMENT_SERVICE, aidGroup));
+
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.validateUserId(USER_ID);
+        });
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.enforceUserPermissions(mContext);
+        });
+        verify(mRegisteredServicesCache).initialize();
+        verify(mRegisteredServicesCache, times(2))
+                .hasService(eq(USER_ID),eq(WALLET_PAYMENT_SERVICE));
+        verify(mRegisteredServicesCache).registerAidGroupForService(eq(USER_ID), anyInt(),
+                eq(WALLET_PAYMENT_SERVICE), eq(aidGroup));
+        verifyNoMoreInteractions(mRegisteredServicesCache);
+    }
+
+    @Test
+    public void testCardEmulationRegisterAidGroupForService_serviceDoesNotExists()
+            throws RemoteException {
+        when(mRegisteredServicesCache.hasService(eq(USER_ID), any())).thenReturn(false);
+        when(mRegisteredServicesCache.registerAidGroupForService(eq(USER_ID), anyInt(), any(),
+                any())).thenReturn(true);
+        AidGroup aidGroup = Mockito.mock(AidGroup.class);
+
+        Assert.assertFalse(mCardEmulationManager.getNfcCardEmulationInterface()
+                .registerAidGroupForService(USER_ID, WALLET_PAYMENT_SERVICE, aidGroup));
+
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.validateUserId(USER_ID);
+        });
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.enforceUserPermissions(mContext);
+        });
+        verify(mRegisteredAidCache).onWalletRoleHolderChanged(eq(WALLET_HOLDER_PACKAGE_NAME),
+                        eq(USER_ID));
+        verify(mRegisteredServicesCache).invalidateCache(eq(USER_ID), eq(true));
+        verify(mRegisteredServicesCache, times(2))
+                .hasService(eq(USER_ID),eq(WALLET_PAYMENT_SERVICE));
+        verifyNoMoreInteractions(mRegisteredAidCache);
+    }
+
+    @Test
+    public void testCardEmulationRegisterPollingLoopFilterForService_serviceExists()
+            throws RemoteException {
+        when(mRegisteredServicesCache.hasService(eq(USER_ID), any())).thenReturn(true);
+        when(mRegisteredServicesCache.registerPollingLoopFilterForService(eq(USER_ID), anyInt(),
+                any(), any(),anyBoolean())).thenReturn(true);
+        String pollingLoopFilter = "filter";
+
+        Assert.assertTrue(mCardEmulationManager.getNfcCardEmulationInterface()
+                .registerPollingLoopFilterForService(USER_ID, WALLET_PAYMENT_SERVICE,
+                        pollingLoopFilter, true));
+
+        verify(mRegisteredServicesCache).initialize();
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.enforceUserPermissions(mContext);
+        });
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.validateUserId(USER_ID);
+        });
+        verify(mRegisteredServicesCache, times(2))
+                .hasService(eq(USER_ID),eq(WALLET_PAYMENT_SERVICE));
+        verify(mRegisteredServicesCache).registerPollingLoopFilterForService(eq(USER_ID),
+                anyInt(), eq(WALLET_PAYMENT_SERVICE), eq(pollingLoopFilter), eq(true));
+        verifyNoMoreInteractions(mRegisteredServicesCache);
+    }
+
+    @Test
+    public void testCardEmulationRegisterPollingLoopFilterForService_serviceDoesNotExists()
+            throws RemoteException {
+        when(mRegisteredServicesCache.hasService(eq(USER_ID), any())).thenReturn(false);
+        when(mRegisteredServicesCache.registerPollingLoopFilterForService(eq(USER_ID), anyInt(),
+                any(), any(),anyBoolean())).thenReturn(true);
+        String pollingLoopFilter = "filter";
+
+        Assert.assertFalse(mCardEmulationManager.getNfcCardEmulationInterface()
+                .registerPollingLoopFilterForService(USER_ID, WALLET_PAYMENT_SERVICE,
+                        pollingLoopFilter, true));
+
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.validateUserId(USER_ID);
+        });
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.enforceUserPermissions(mContext);
+        });
+        verify(mRegisteredServicesCache).initialize();
+        verify(mRegisteredServicesCache).invalidateCache(eq(USER_ID), eq(true));
+        verify(mRegisteredServicesCache, times(2))
+                .hasService(eq(USER_ID),eq(WALLET_PAYMENT_SERVICE));
+        verifyNoMoreInteractions(mRegisteredServicesCache);
+    }
+
+    @Test
+    public void testCardEmulationRemovePollingLoopFilterForService_serviceExists()
+            throws RemoteException {
+        when(mRegisteredServicesCache.hasService(eq(USER_ID), any())).thenReturn(true);
+        when(mRegisteredServicesCache.removePollingLoopFilterForService(eq(USER_ID), anyInt(),
+                any(), any())).thenReturn(true);
+        String pollingLoopFilter = "filter";
+
+        Assert.assertTrue(mCardEmulationManager.getNfcCardEmulationInterface()
+                .removePollingLoopFilterForService(USER_ID, WALLET_PAYMENT_SERVICE,
+                        pollingLoopFilter));
+
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.enforceUserPermissions(mContext);
+        });
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.validateUserId(USER_ID);
+        });
+        verify(mRegisteredServicesCache).initialize();
+        verify(mRegisteredServicesCache, times(2))
+                .hasService(eq(USER_ID),eq(WALLET_PAYMENT_SERVICE));
+        verify(mRegisteredServicesCache).removePollingLoopFilterForService(eq(USER_ID),
+                anyInt(), eq(WALLET_PAYMENT_SERVICE), eq(pollingLoopFilter));
+        verifyNoMoreInteractions(mRegisteredServicesCache);
+    }
+
+    @Test
+    public void testCardEmulationRemovePollingLoopFilterForService_serviceDoesNotExists()
+            throws RemoteException {
+        when(mRegisteredServicesCache.hasService(eq(USER_ID), any())).thenReturn(false);
+        when(mRegisteredServicesCache.removePollingLoopFilterForService(eq(USER_ID), anyInt(),
+                any(), any())).thenReturn(true);
+        String pollingLoopFilter = "filter";
+
+        Assert.assertFalse(mCardEmulationManager.getNfcCardEmulationInterface()
+                .removePollingLoopFilterForService(USER_ID, WALLET_PAYMENT_SERVICE,
+                        pollingLoopFilter));
+
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.enforceUserPermissions(mContext);
+        });
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.validateUserId(USER_ID);
+        });
+        verify(mRegisteredServicesCache).initialize();
+        verify(mRegisteredServicesCache).invalidateCache(eq(USER_ID), eq(true));
+        verify(mRegisteredServicesCache, times(2))
+                .hasService(eq(USER_ID),eq(WALLET_PAYMENT_SERVICE));
+        verifyNoMoreInteractions(mRegisteredServicesCache);
+    }
+
+    @Test
+    public void testCardEmulationRegisterPollingLoopPatternFilterForService_serviceExists()
+            throws RemoteException {
+        when(mRegisteredServicesCache.hasService(eq(USER_ID), any())).thenReturn(true);
+        when(mRegisteredServicesCache.registerPollingLoopPatternFilterForService(eq(USER_ID),
+                anyInt(), any(), any(), anyBoolean())).thenReturn(true);
+        String pollingLoopFilter = "filter";
+
+        Assert.assertTrue(mCardEmulationManager.getNfcCardEmulationInterface()
+                .registerPollingLoopPatternFilterForService(USER_ID, WALLET_PAYMENT_SERVICE,
+                        pollingLoopFilter, true));
+
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.enforceUserPermissions(mContext);
+        });
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.validateUserId(USER_ID);
+        });
+        verify(mRegisteredServicesCache).initialize();
+        verify(mRegisteredServicesCache, times(2))
+                .hasService(eq(USER_ID),eq(WALLET_PAYMENT_SERVICE));
+        verify(mRegisteredServicesCache).registerPollingLoopPatternFilterForService(eq(USER_ID),
+                anyInt(), eq(WALLET_PAYMENT_SERVICE), eq(pollingLoopFilter), eq(true));
+        verifyNoMoreInteractions(mRegisteredServicesCache);
+    }
+
+    @Test
+    public void testCardEmulationRegisterPollingLoopPatternFilterForService_serviceDoesNotExists()
+            throws RemoteException {
+        when(mRegisteredServicesCache.hasService(eq(USER_ID), any())).thenReturn(false);
+        when(mRegisteredServicesCache.registerPollingLoopPatternFilterForService(eq(USER_ID),
+                anyInt(), any(), any(), anyBoolean())).thenReturn(true);
+        String pollingLoopFilter = "filter";
+
+        Assert.assertFalse(mCardEmulationManager.getNfcCardEmulationInterface()
+                .registerPollingLoopPatternFilterForService(USER_ID, WALLET_PAYMENT_SERVICE,
+                        pollingLoopFilter, true));
+
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.enforceUserPermissions(mContext);
+        });
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.validateUserId(USER_ID);
+        });
+        verify(mRegisteredServicesCache).initialize();
+        verify(mRegisteredServicesCache).invalidateCache(eq(USER_ID), eq(true));
+        verify(mRegisteredServicesCache, times(2))
+                .hasService(eq(USER_ID),eq(WALLET_PAYMENT_SERVICE));
+        verifyNoMoreInteractions(mRegisteredServicesCache);
+    }
+
+    @Test
+    public void testCardEmulationRemovePollingLoopPatternFilterForService_serviceExists()
+            throws RemoteException {
+        when(mRegisteredServicesCache.hasService(eq(USER_ID), any())).thenReturn(true);
+        when(mRegisteredServicesCache.removePollingLoopPatternFilterForService(eq(USER_ID),
+                anyInt(), any(), any())).thenReturn(true);
+        String pollingLoopFilter = "filter";
+
+        Assert.assertTrue(mCardEmulationManager.getNfcCardEmulationInterface()
+                .removePollingLoopPatternFilterForService(USER_ID, WALLET_PAYMENT_SERVICE,
+                        pollingLoopFilter));
+
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.enforceUserPermissions(mContext);
+        });
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.validateUserId(USER_ID);
+        });
+        verify(mRegisteredServicesCache).initialize();
+        verify(mRegisteredServicesCache, times(2))
+                .hasService(eq(USER_ID),eq(WALLET_PAYMENT_SERVICE));
+        verify(mRegisteredServicesCache).removePollingLoopPatternFilterForService(eq(USER_ID),
+                anyInt(), eq(WALLET_PAYMENT_SERVICE), eq(pollingLoopFilter));
+        verifyNoMoreInteractions(mRegisteredServicesCache);
+    }
+
+    @Test
+    public void testCardEmulationRemovePollingLoopPatternFilterForService_serviceDoesNotExists()
+            throws RemoteException {
+        when(mRegisteredServicesCache.hasService(eq(USER_ID), any())).thenReturn(false);
+        when(mRegisteredServicesCache.removePollingLoopPatternFilterForService(eq(USER_ID),
+                anyInt(), any(), any())).thenReturn(true);
+        String pollingLoopFilter = "filter";
+
+        Assert.assertFalse(mCardEmulationManager.getNfcCardEmulationInterface()
+                .removePollingLoopPatternFilterForService(USER_ID, WALLET_PAYMENT_SERVICE,
+                        pollingLoopFilter));
+
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.enforceUserPermissions(mContext);
+        });
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.validateUserId(USER_ID);
+        });
+        verify(mRegisteredServicesCache).initialize();
+        verify(mRegisteredServicesCache).invalidateCache(eq(USER_ID), eq(true));
+        verify(mRegisteredServicesCache, times(2))
+                .hasService(eq(USER_ID),eq(WALLET_PAYMENT_SERVICE));
+        verifyNoMoreInteractions(mRegisteredServicesCache);
+    }
+
+    @Test
+    public void testCardEmulationSetOffHostForService_serviceExists()
+            throws RemoteException {
+        when(mRegisteredServicesCache.hasService(eq(USER_ID), any())).thenReturn(true);
+        when(mRegisteredServicesCache.setOffHostSecureElement(eq(USER_ID),
+                anyInt(), any(), any())).thenReturn(true);
+        String offhostse = "offhostse";
+
+        Assert.assertTrue(mCardEmulationManager.getNfcCardEmulationInterface()
+                .setOffHostForService(USER_ID, WALLET_PAYMENT_SERVICE, offhostse));
+
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.enforceUserPermissions(mContext);
+        });
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.validateUserId(USER_ID);
+        });
+        verify(mRegisteredServicesCache).initialize();
+        verify(mRegisteredServicesCache, times(2))
+                .hasService(eq(USER_ID),eq(WALLET_PAYMENT_SERVICE));
+        verify(mRegisteredServicesCache).setOffHostSecureElement(eq(USER_ID), anyInt(),
+                eq(WALLET_PAYMENT_SERVICE) , eq(offhostse));
+        verifyNoMoreInteractions(mRegisteredServicesCache);
+    }
+
+    @Test
+    public void testCardEmulationSetOffHostForService_serviceDoesNotExists()
+            throws RemoteException {
+        when(mRegisteredServicesCache.hasService(eq(USER_ID), any())).thenReturn(false);
+        when(mRegisteredServicesCache.setOffHostSecureElement(eq(USER_ID),
+                anyInt(), any(), any())).thenReturn(true);
+        String offhostse = "offhostse";
+
+        Assert.assertFalse(mCardEmulationManager.getNfcCardEmulationInterface()
+                .setOffHostForService(USER_ID, WALLET_PAYMENT_SERVICE, offhostse));
+
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.enforceUserPermissions(mContext);
+        });
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.validateUserId(USER_ID);
+        });
+        verify(mRegisteredServicesCache).initialize();
+        verify(mRegisteredServicesCache).invalidateCache(eq(USER_ID), eq(true));
+        verify(mRegisteredServicesCache, times(2))
+                .hasService(eq(USER_ID),eq(WALLET_PAYMENT_SERVICE));
+        verifyNoMoreInteractions(mRegisteredServicesCache);
+    }
+
+    @Test
+    public void testCardEmulationUnsetOffHostForService_serviceExists()
+            throws RemoteException {
+        when(mRegisteredServicesCache.hasService(eq(USER_ID), any())).thenReturn(true);
+        when(mRegisteredServicesCache.resetOffHostSecureElement(eq(USER_ID),
+                anyInt(), any())).thenReturn(true);
+
+        Assert.assertTrue(mCardEmulationManager.getNfcCardEmulationInterface()
+                .unsetOffHostForService(USER_ID, WALLET_PAYMENT_SERVICE));
+
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.enforceUserPermissions(mContext);
+        });
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.validateUserId(USER_ID);
+        });
+        verify(mRegisteredServicesCache).initialize();
+        verify(mRegisteredServicesCache, times(2))
+                .hasService(eq(USER_ID),eq(WALLET_PAYMENT_SERVICE));
+        verify(mRegisteredServicesCache).resetOffHostSecureElement(eq(USER_ID), anyInt(),
+                eq(WALLET_PAYMENT_SERVICE));
+        verifyNoMoreInteractions(mRegisteredServicesCache);
+        verify(mNfcService).onPreferredPaymentChanged(eq(NfcAdapter.PREFERRED_PAYMENT_UPDATED));
+    }
+
+    @Test
+    public void testCardEmulationUnsetOffHostForService_serviceDoesNotExists()
+            throws RemoteException {
+        when(mRegisteredServicesCache.hasService(eq(USER_ID), any())).thenReturn(false);
+        when(mRegisteredServicesCache.resetOffHostSecureElement(eq(USER_ID),
+                anyInt(), any())).thenReturn(true);
+
+        Assert.assertFalse(mCardEmulationManager.getNfcCardEmulationInterface()
+                .unsetOffHostForService(USER_ID, WALLET_PAYMENT_SERVICE));
+
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.enforceUserPermissions(mContext);
+        });
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.validateUserId(USER_ID);
+        });
+        verify(mRegisteredServicesCache).initialize();
+        verify(mRegisteredServicesCache).invalidateCache(eq(USER_ID), eq(true));
+        verify(mRegisteredServicesCache, times(2))
+                .hasService(eq(USER_ID),eq(WALLET_PAYMENT_SERVICE));
+        verifyNoMoreInteractions(mRegisteredServicesCache);
+    }
+
+    @Test
+    public void testCardEmulationGetAidGroupForService_serviceExists()
+            throws RemoteException {
+        AidGroup aidGroup = Mockito.mock(AidGroup.class);
+        when(mRegisteredServicesCache.hasService(eq(USER_ID), any())).thenReturn(true);
+        when(mRegisteredServicesCache.getAidGroupForService(eq(USER_ID),
+                anyInt(), any(), eq(CardEmulation.CATEGORY_PAYMENT))).thenReturn(aidGroup);
+
+        Assert.assertEquals(mCardEmulationManager.getNfcCardEmulationInterface()
+                .getAidGroupForService(USER_ID, WALLET_PAYMENT_SERVICE,
+                        CardEmulation.CATEGORY_PAYMENT), aidGroup);
+
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.enforceUserPermissions(mContext);
+        });
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.validateUserId(USER_ID);
+        });
+        verify(mRegisteredServicesCache).initialize();
+        verify(mRegisteredServicesCache, times(2))
+                .hasService(eq(USER_ID),eq(WALLET_PAYMENT_SERVICE));
+        verify(mRegisteredServicesCache).getAidGroupForService(eq(USER_ID), anyInt(),
+                eq(WALLET_PAYMENT_SERVICE), eq(CardEmulation.CATEGORY_PAYMENT));
+        verifyNoMoreInteractions(mRegisteredServicesCache);
+    }
+
+    @Test
+    public void testCardEmulationGetAidGroupForService_serviceDoesNotExists()
+            throws RemoteException {
+        AidGroup aidGroup = Mockito.mock(AidGroup.class);
+        when(mRegisteredServicesCache.hasService(eq(USER_ID), any())).thenReturn(false);
+        when(mRegisteredServicesCache.getAidGroupForService(eq(USER_ID),
+                anyInt(), any(), eq(CardEmulation.CATEGORY_PAYMENT))).thenReturn(aidGroup);
+
+        Assert.assertNull(mCardEmulationManager.getNfcCardEmulationInterface()
+                .getAidGroupForService(USER_ID, WALLET_PAYMENT_SERVICE,
+                        CardEmulation.CATEGORY_PAYMENT));
+
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.enforceUserPermissions(mContext);
+        });
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.validateUserId(USER_ID);
+        });
+        verify(mRegisteredServicesCache).initialize();
+        verify(mRegisteredServicesCache).invalidateCache(eq(USER_ID), eq(true));
+        verify(mRegisteredServicesCache, times(2))
+                .hasService(eq(USER_ID),eq(WALLET_PAYMENT_SERVICE));
+        verifyNoMoreInteractions(mRegisteredServicesCache);
+    }
+
+    @Test
+    public void testCardEmulationRemoveAidGroupForService_serviceExists()
+            throws RemoteException {
+        AidGroup aidGroup = Mockito.mock(AidGroup.class);
+        when(mRegisteredServicesCache.hasService(eq(USER_ID), any())).thenReturn(true);
+        when(mRegisteredServicesCache.removeAidGroupForService(eq(USER_ID),
+                anyInt(), any(), eq(CardEmulation.CATEGORY_PAYMENT))).thenReturn(true);
+
+        Assert.assertTrue(mCardEmulationManager.getNfcCardEmulationInterface()
+                .removeAidGroupForService(USER_ID, WALLET_PAYMENT_SERVICE,
+                        CardEmulation.CATEGORY_PAYMENT));
+
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.enforceUserPermissions(mContext);
+        });
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.validateUserId(USER_ID);
+        });
+        verify(mRegisteredServicesCache).initialize();
+        verify(mRegisteredServicesCache, times(2))
+                .hasService(eq(USER_ID),eq(WALLET_PAYMENT_SERVICE));
+        verify(mRegisteredServicesCache).removeAidGroupForService(eq(USER_ID), anyInt(),
+                eq(WALLET_PAYMENT_SERVICE), eq(CardEmulation.CATEGORY_PAYMENT));
+        verifyNoMoreInteractions(mRegisteredServicesCache);
+        verify(mNfcService).onPreferredPaymentChanged(eq(NfcAdapter.PREFERRED_PAYMENT_UPDATED));
+    }
+
+    @Test
+    public void testCardEmulationRemoveAidGroupForService_serviceDoesNotExists()
+            throws RemoteException {
+        when(mRegisteredServicesCache.hasService(eq(USER_ID), any())).thenReturn(false);
+        when(mRegisteredServicesCache.removeAidGroupForService(eq(USER_ID),
+                anyInt(), any(), eq(CardEmulation.CATEGORY_PAYMENT))).thenReturn(true);
+
+        Assert.assertFalse(mCardEmulationManager.getNfcCardEmulationInterface()
+                .removeAidGroupForService(USER_ID, WALLET_PAYMENT_SERVICE,
+                        CardEmulation.CATEGORY_PAYMENT));
+
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.enforceUserPermissions(mContext);
+        });
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.validateUserId(USER_ID);
+        });
+        verify(mRegisteredServicesCache).initialize();
+        verify(mRegisteredServicesCache).invalidateCache(eq(USER_ID), eq(true));
+        verify(mRegisteredServicesCache, times(2))
+                .hasService(eq(USER_ID),eq(WALLET_PAYMENT_SERVICE));
+        verifyNoMoreInteractions(mRegisteredServicesCache);
+    }
+
+    @Test
+    public void testCardEmulationGetServices()
+            throws RemoteException {
+        when(mRegisteredServicesCache.hasService(eq(USER_ID), any())).thenReturn(false);
+        when(mRegisteredServicesCache.getServicesForCategory(eq(USER_ID),
+                eq(CardEmulation.CATEGORY_PAYMENT))).thenReturn(UPDATED_SERVICES);
+
+        Assert.assertEquals(mCardEmulationManager.getNfcCardEmulationInterface()
+                .getServices(USER_ID, CardEmulation.CATEGORY_PAYMENT), UPDATED_SERVICES);
+
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.validateProfileId(mContext, USER_ID);
+        });
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.enforceAdminPermissions(mContext);
+        });
+        verify(mRegisteredServicesCache).initialize();
+        verify(mRegisteredServicesCache).getServicesForCategory(eq(USER_ID),
+                eq(CardEmulation.CATEGORY_PAYMENT));
+        verifyNoMoreInteractions(mRegisteredServicesCache);
+    }
+
+    @Test
+    public void testCardEmulationSetPreferredService_serviceExists()
+            throws RemoteException {
+        when(mRegisteredServicesCache.hasService(eq(USER_ID), any())).thenReturn(true);
+        when(mPreferredServices.registerPreferredForegroundService(eq(WALLET_PAYMENT_SERVICE),
+                anyInt())).thenReturn(true);
+
+        Assert.assertTrue(mCardEmulationManager.getNfcCardEmulationInterface()
+                .setPreferredService(WALLET_PAYMENT_SERVICE));
+
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.enforceUserPermissions(mContext);
+        });
+        verify(mRegisteredServicesCache).initialize();
+        verify(mRegisteredServicesCache, times(2))
+                .hasService(eq(USER_ID),eq(WALLET_PAYMENT_SERVICE));
+        verifyNoMoreInteractions(mRegisteredServicesCache);
+        verify(mPreferredServices).onWalletRoleHolderChanged(eq(WALLET_HOLDER_PACKAGE_NAME),
+                eq(USER_ID));
+        verify(mPreferredServices).registerPreferredForegroundService(eq(WALLET_PAYMENT_SERVICE),
+                anyInt());
+        verifyNoMoreInteractions(mPreferredServices);
+    }
+
+    @Test
+    public void testCardEmulationSetPreferredService_serviceDoesNotExists()
+            throws RemoteException {
+        when(mRegisteredServicesCache.hasService(eq(USER_ID), any())).thenReturn(false);
+        when(mPreferredServices.registerPreferredForegroundService(eq(WALLET_PAYMENT_SERVICE),
+                anyInt())).thenReturn(false);
+
+        Assert.assertFalse(mCardEmulationManager.getNfcCardEmulationInterface()
+                .setPreferredService(WALLET_PAYMENT_SERVICE));
+
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.enforceUserPermissions(mContext);
+        });
+        verify(mRegisteredServicesCache).initialize();
+        verify(mRegisteredServicesCache).invalidateCache(eq(USER_ID), eq(true));
+        verify(mRegisteredServicesCache, times(2))
+                .hasService(eq(USER_ID),eq(WALLET_PAYMENT_SERVICE));
+        verifyNoMoreInteractions(mRegisteredServicesCache);
+        verify(mPreferredServices).onWalletRoleHolderChanged(eq(WALLET_HOLDER_PACKAGE_NAME),
+                eq(USER_ID));
+        verifyNoMoreInteractions(mPreferredServices);
+    }
+
+    @Test
+    public void testCardEmulationUnsetPreferredService_serviceExists()
+            throws RemoteException {
+        when(mRegisteredServicesCache.hasService(eq(USER_ID), any())).thenReturn(true);
+        when(mPreferredServices.unregisteredPreferredForegroundService(anyInt()))
+                .thenReturn(true);
+
+        Assert.assertTrue(mCardEmulationManager.getNfcCardEmulationInterface()
+                .unsetPreferredService());
+
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.enforceUserPermissions(mContext);
+        });
+        verify(mPreferredServices).onWalletRoleHolderChanged(eq(WALLET_HOLDER_PACKAGE_NAME),
+                eq(USER_ID));
+        verify(mPreferredServices).unregisteredPreferredForegroundService(anyInt());
+        verifyNoMoreInteractions(mPreferredServices);
+    }
+
+    @Test
+    public void testCardEmulationUnsetPreferredService_serviceDoesNotExists()
+            throws RemoteException {
+        when(mPreferredServices.unregisteredPreferredForegroundService(anyInt()))
+                .thenReturn(false);
+
+        Assert.assertFalse(mCardEmulationManager.getNfcCardEmulationInterface()
+                .unsetPreferredService());
+
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.enforceUserPermissions(mContext);
+        });
+        verify(mPreferredServices).unregisteredPreferredForegroundService(anyInt());
+    }
+
+    @Test
+    public void testCardEmulationSupportsAidPrefixRegistration_doesSupport()
+            throws RemoteException {
+        when(mRegisteredAidCache.supportsAidPrefixRegistration()).thenReturn(true);
+
+        Assert.assertTrue(mCardEmulationManager.getNfcCardEmulationInterface()
+                .supportsAidPrefixRegistration());
+
+        verify(mRegisteredAidCache).supportsAidPrefixRegistration();
+    }
+
+    @Test
+    public void testCardEmulationSupportsAidPrefixRegistration_doesNotSupport()
+            throws RemoteException {
+        when(mRegisteredAidCache.supportsAidPrefixRegistration()).thenReturn(false);
+
+        Assert.assertFalse(mCardEmulationManager.getNfcCardEmulationInterface()
+                .supportsAidPrefixRegistration());
+
+        verify(mRegisteredAidCache).onWalletRoleHolderChanged(eq(WALLET_HOLDER_PACKAGE_NAME),
+                eq(USER_ID));
+        verify(mRegisteredAidCache).supportsAidPrefixRegistration();
+        verifyNoMoreInteractions(mRegisteredAidCache);
+    }
+
+    @Test
+    public void testCardEmulationGetPreferredPaymentService()
+            throws RemoteException {
+        ApduServiceInfo apduServiceInfo = Mockito.mock(ApduServiceInfo.class);
+        when(mRegisteredAidCache.getPreferredService()).thenReturn(WALLET_PAYMENT_SERVICE);
+        when(mRegisteredServicesCache.getService(eq(USER_ID), eq(WALLET_PAYMENT_SERVICE)))
+                .thenReturn(apduServiceInfo);
+
+        Assert.assertEquals(mCardEmulationManager.getNfcCardEmulationInterface()
+                .getPreferredPaymentService(USER_ID), apduServiceInfo);
+
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.validateUserId(USER_ID);
+        });
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.enforceUserPermissions(mContext);
+        });
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.enforcePreferredPaymentInfoPermissions(mContext);
+        });
+        verify(mRegisteredServicesCache).initialize();
+        verify(mRegisteredAidCache).onWalletRoleHolderChanged(eq(WALLET_HOLDER_PACKAGE_NAME),
+                eq(USER_ID));
+        verify(mRegisteredAidCache).getPreferredService();
+        verify(mRegisteredServicesCache).getService(eq(USER_ID), eq(WALLET_PAYMENT_SERVICE));
+        verifyNoMoreInteractions(mRegisteredAidCache);
+        verifyNoMoreInteractions(mRegisteredServicesCache);
+    }
+
+    @Test
+    public void testCardEmulationSetServiceEnabledForCategoryOther_resourceTrue()
+            throws RemoteException {
+        when(mResources.getBoolean(R.bool.enable_service_for_category_other)).thenReturn(true);
+        when(mRegisteredServicesCache.registerOtherForService(anyInt(), any(), anyBoolean()))
+                .thenReturn(true);
+
+        Assert.assertTrue(mCardEmulationManager.getNfcCardEmulationInterface()
+                .setServiceEnabledForCategoryOther(USER_ID, WALLET_PAYMENT_SERVICE, true));
+
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.enforceUserPermissions(mContext);
+        });
+        verify(mRegisteredServicesCache).initialize();
+        verify(mRegisteredServicesCache).registerOtherForService(eq(USER_ID),
+                eq(WALLET_PAYMENT_SERVICE), eq(true));
+        verifyNoMoreInteractions(mRegisteredServicesCache);
+    }
+
+    @Test
+    public void testCardEmulationSetServiceEnabledForCategoryOther_resourceFalse()
+            throws RemoteException {
+        when(mResources.getBoolean(R.bool.enable_service_for_category_other)).thenReturn(false);
+        when(mRegisteredServicesCache.registerOtherForService(anyInt(), any(), anyBoolean()))
+                .thenReturn(true);
+
+        Assert.assertFalse(mCardEmulationManager.getNfcCardEmulationInterface()
+                .setServiceEnabledForCategoryOther(USER_ID, WALLET_PAYMENT_SERVICE, true));
+
+        verify(mRegisteredServicesCache).initialize();
+        verifyNoMoreInteractions(mRegisteredServicesCache);
+    }
+
+    @Test
+    public void testCardEmulationIsDefaultPaymentRegistered_walletRoleEnabledWalletSet()
+            throws RemoteException {
+        when(mWalletRoleObserver.isWalletRoleFeatureEnabled()).thenReturn(true);
+        when(mWalletRoleObserver.getDefaultWalletRoleHolder(anyInt()))
+                .thenReturn(WALLET_HOLDER_PACKAGE_NAME);
+        when(Binder.getCallingUserHandle()).thenReturn(USER_HANDLE);
+
+        Assert.assertTrue(mCardEmulationManager.getNfcCardEmulationInterface()
+                .isDefaultPaymentRegistered());
+
+        verify(mWalletRoleObserver, times(2)).isWalletRoleFeatureEnabled();
+        verify(mWalletRoleObserver, times(2))
+                .getDefaultWalletRoleHolder(eq(USER_ID));
+    }
+
+    @Test
+    public void testCardEmulationIsDefaultPaymentRegistered_walletRoleEnabledWalletNone()
+            throws RemoteException {
+        when(mWalletRoleObserver.isWalletRoleFeatureEnabled()).thenReturn(true);
+        when(mWalletRoleObserver.getDefaultWalletRoleHolder(anyInt()))
+                .thenReturn(null);
+        when(Binder.getCallingUserHandle()).thenReturn(USER_HANDLE);
+
+        Assert.assertFalse(mCardEmulationManager.getNfcCardEmulationInterface()
+                .isDefaultPaymentRegistered());
+
+        verify(mWalletRoleObserver, times(2)).isWalletRoleFeatureEnabled();
+        verify(mWalletRoleObserver, times(2))
+                .getDefaultWalletRoleHolder(eq(USER_ID));
+    }
+
+    @Test
+    public void testCardEmulationOverrideRoutingTable_callerNotForeground()
+            throws RemoteException {
+        when(mForegroundUtils.registerUidToBackgroundCallback(any(), anyInt()))
+                .thenReturn(false);
+        String protocol = "DH";
+        String technology = "DH";
+
+        Assert.assertFalse(mCardEmulationManager.getNfcCardEmulationInterface()
+                .overrideRoutingTable(USER_ID, protocol, technology));
+
+        verify(mRegisteredAidCache).onWalletRoleHolderChanged(eq(WALLET_HOLDER_PACKAGE_NAME),
+                eq(USER_ID));
+        verify(mRoutingOptionManager).getOffHostRouteEse();
+        verify(mRoutingOptionManager).getOffHostRouteUicc();
+        verifyNoMoreInteractions(mRoutingOptionManager);
+        verifyNoMoreInteractions(mRegisteredAidCache);
+    }
+
+    @Test
+    public void testCardEmulationOverrideRoutingTable_callerForegroundRouteNull()
+            throws RemoteException {
+        when(mForegroundUtils.registerUidToBackgroundCallback(any(), anyInt()))
+                .thenReturn(true);
+
+        Assert.assertTrue(mCardEmulationManager.getNfcCardEmulationInterface()
+                .overrideRoutingTable(USER_ID, null, null));
+
+        verify(mRegisteredAidCache).onWalletRoleHolderChanged(eq(WALLET_HOLDER_PACKAGE_NAME),
+                eq(USER_ID));
+        verify(mRoutingOptionManager).overrideDefaultIsoDepRoute(eq(-1));
+        verify(mRoutingOptionManager).overrideDefaultOffHostRoute(eq(-1));
+        verify(mRoutingOptionManager).getOffHostRouteEse();
+        verify(mRoutingOptionManager).getOffHostRouteUicc();
+        verify(mRegisteredAidCache).onRoutingOverridedOrRecovered();
+        verifyNoMoreInteractions(mRoutingOptionManager);
+        verifyNoMoreInteractions(mRegisteredAidCache);
+    }
+
+    @Test
+    public void testCardEmulationOverrideRoutingTable_callerForegroundRouteDH()
+            throws RemoteException {
+        when(mForegroundUtils.registerUidToBackgroundCallback(any(), anyInt()))
+                .thenReturn(true);
+        String protocol = "DH";
+        String technology = "DH";
+
+        Assert.assertTrue(mCardEmulationManager.getNfcCardEmulationInterface()
+                .overrideRoutingTable(USER_ID, protocol, technology));
+
+        verify(mRegisteredAidCache).onWalletRoleHolderChanged(eq(WALLET_HOLDER_PACKAGE_NAME),
+                eq(USER_ID));
+        verify(mRoutingOptionManager).overrideDefaultIsoDepRoute(eq(0));
+        verify(mRoutingOptionManager).overrideDefaultOffHostRoute(eq(0));
+        verify(mRoutingOptionManager).getOffHostRouteEse();
+        verify(mRoutingOptionManager).getOffHostRouteUicc();
+        verify(mRegisteredAidCache).onRoutingOverridedOrRecovered();
+        verifyNoMoreInteractions(mRoutingOptionManager);
+        verifyNoMoreInteractions(mRegisteredAidCache);
+    }
+
+    @Test
+    public void testCardEmulationOverrideRoutingTable_callerForegroundRouteeSE()
+            throws RemoteException {
+        when(mForegroundUtils.registerUidToBackgroundCallback(any(), anyInt()))
+                .thenReturn(true);
+        String protocol = "eSE1";
+        String technology = "eSE1";
+
+        Assert.assertTrue(mCardEmulationManager.getNfcCardEmulationInterface()
+                .overrideRoutingTable(USER_ID, protocol, technology));
+
+        verify(mRegisteredAidCache).onWalletRoleHolderChanged(eq(WALLET_HOLDER_PACKAGE_NAME),
+                eq(USER_ID));
+        verify(mRoutingOptionManager).overrideDefaultIsoDepRoute(eq(TEST_DATA_1[0] & 0xFF));
+        verify(mRoutingOptionManager).overrideDefaultOffHostRoute(eq(TEST_DATA_1[0] & 0xFF));
+        verify(mRoutingOptionManager).getOffHostRouteEse();
+        verify(mRoutingOptionManager).getOffHostRouteUicc();
+        verify(mRegisteredAidCache).onRoutingOverridedOrRecovered();
+        verifyNoMoreInteractions(mRoutingOptionManager);
+        verifyNoMoreInteractions(mRegisteredAidCache);
+    }
+
+    @Test
+    public void testCardEmulationOverrideRoutingTable_callerForegroundRouteSIM()
+            throws RemoteException {
+        when(mForegroundUtils.registerUidToBackgroundCallback(any(), anyInt()))
+                .thenReturn(true);
+        String protocol = "SIM1";
+        String technology = "SIM1";
+
+        Assert.assertTrue(mCardEmulationManager.getNfcCardEmulationInterface()
+                .overrideRoutingTable(USER_ID, protocol, technology));
+
+        verify(mRegisteredAidCache).onWalletRoleHolderChanged(eq(WALLET_HOLDER_PACKAGE_NAME),
+                eq(USER_ID));
+        verify(mRoutingOptionManager).overrideDefaultIsoDepRoute(eq(TEST_DATA_2[0] & 0xFF));
+        verify(mRoutingOptionManager).overrideDefaultOffHostRoute(eq(TEST_DATA_2[0] & 0xFF));
+        verify(mRoutingOptionManager).getOffHostRouteEse();
+        verify(mRoutingOptionManager).getOffHostRouteUicc();
+        verify(mRegisteredAidCache).onRoutingOverridedOrRecovered();
+        verifyNoMoreInteractions(mRoutingOptionManager);
+        verifyNoMoreInteractions(mRegisteredAidCache);
+    }
+
+    @Test
+    public void testCardEmulationRecoverRoutingTable_callerForeground()
+            throws RemoteException {
+        when(mForegroundUtils.isInForeground(anyInt()))
+                .thenReturn(true);
+
+        Assert.assertTrue(mCardEmulationManager.getNfcCardEmulationInterface()
+                .recoverRoutingTable(USER_ID));
+
+        verify(mRegisteredAidCache).onWalletRoleHolderChanged(eq(WALLET_HOLDER_PACKAGE_NAME),
+                eq(USER_ID));
+        verify(mRoutingOptionManager).recoverOverridedRoutingTable();
+        verify(mRoutingOptionManager).getOffHostRouteEse();
+        verify(mRoutingOptionManager).getOffHostRouteUicc();
+        verify(mRegisteredAidCache).onRoutingOverridedOrRecovered();
+        verifyNoMoreInteractions(mRoutingOptionManager);
+        verifyNoMoreInteractions(mRegisteredAidCache);
+    }
+
+    @Test
+    public void testCardEmulationRecoverRoutingTable_callerNotForeground()
+            throws RemoteException {
+        when(mForegroundUtils.isInForeground(anyInt()))
+                .thenReturn(false);
+
+        Assert.assertFalse(mCardEmulationManager.getNfcCardEmulationInterface()
+                .recoverRoutingTable(USER_ID));
+
+        verify(mRegisteredAidCache).onWalletRoleHolderChanged(eq(WALLET_HOLDER_PACKAGE_NAME),
+                eq(USER_ID));
+        verify(mRoutingOptionManager).getOffHostRouteEse();
+        verify(mRoutingOptionManager).getOffHostRouteUicc();
+        verifyNoMoreInteractions(mRoutingOptionManager);
+        verifyNoMoreInteractions(mRegisteredAidCache);
+    }
+
+    @Test
+    public void testNfcFCardEmulationGetSystemCodeForService_serviceExists()
+            throws RemoteException {
+        String systemCode = "systemCode";
+        when(mRegisteredNfcFServicesCache.hasService(eq(USER_ID), any()))
+                .thenReturn(true);
+        when(mRegisteredNfcFServicesCache.getSystemCodeForService(anyInt(),
+                anyInt(), any())).thenReturn(systemCode);
+
+        Assert.assertEquals(mCardEmulationManager.getNfcFCardEmulationInterface()
+                .getSystemCodeForService(USER_ID, WALLET_PAYMENT_SERVICE), systemCode);
+
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.validateUserId(USER_ID);
+        });
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.enforceUserPermissions(mContext);
+        });
+        verify(mRegisteredNfcFServicesCache).initialize();
+        verify(mRegisteredNfcFServicesCache, times(2))
+                .hasService(eq(USER_ID),eq(WALLET_PAYMENT_SERVICE));
+        verify(mRegisteredNfcFServicesCache).getSystemCodeForService(eq(USER_ID), anyInt(),
+                eq(WALLET_PAYMENT_SERVICE));
+        verifyNoMoreInteractions(mRegisteredNfcFServicesCache);
+    }
+
+    @Test
+    public void testNfcFCardEmulationGetSystemCodeForService_serviceDoesNotExists()
+            throws RemoteException {
+        String systemCode = "systemCode";
+        when(mRegisteredNfcFServicesCache.hasService(eq(USER_ID), any()))
+                .thenReturn(false);
+        when(mRegisteredNfcFServicesCache.getSystemCodeForService(anyInt(),
+                anyInt(), any())).thenReturn(systemCode);
+
+        Assert.assertNull(mCardEmulationManager.getNfcFCardEmulationInterface()
+                .getSystemCodeForService(USER_ID, WALLET_PAYMENT_SERVICE));
+
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.validateUserId(USER_ID);
+        });
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.enforceUserPermissions(mContext);
+        });
+        verify(mRegisteredNfcFServicesCache).initialize();
+        verify(mRegisteredNfcFServicesCache).invalidateCache(eq(USER_ID));
+        verify(mRegisteredNfcFServicesCache, times(2))
+                .hasService(eq(USER_ID),eq(WALLET_PAYMENT_SERVICE));
+        verifyNoMoreInteractions(mRegisteredNfcFServicesCache);
+    }
+
+    @Test
+    public void testNfcFCardEmulationRegisterSystemCodeForService_serviceExists()
+            throws RemoteException {
+        String systemCode = "systemCode";
+        when(mRegisteredNfcFServicesCache.hasService(eq(USER_ID), any()))
+                .thenReturn(true);
+        when(mRegisteredNfcFServicesCache.registerSystemCodeForService(anyInt(),
+                anyInt(), any(), anyString())).thenReturn(true);
+
+        Assert.assertTrue(mCardEmulationManager.getNfcFCardEmulationInterface()
+                .registerSystemCodeForService(USER_ID, WALLET_PAYMENT_SERVICE, systemCode));
+
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.validateUserId(USER_ID);
+        });
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.enforceUserPermissions(mContext);
+        });
+        verify(mRegisteredNfcFServicesCache).initialize();
+        verify(mRegisteredNfcFServicesCache, times(2))
+                .hasService(eq(USER_ID),eq(WALLET_PAYMENT_SERVICE));
+        verify(mRegisteredNfcFServicesCache).registerSystemCodeForService(eq(USER_ID), anyInt(),
+                eq(WALLET_PAYMENT_SERVICE), eq(systemCode));
+        verifyNoMoreInteractions(mRegisteredNfcFServicesCache);
+    }
+
+    @Test
+    public void testNfcFCardEmulationRegisterSystemCodeForService_serviceDoesNotExists()
+            throws RemoteException {
+        String systemCode = "systemCode";
+        when(mRegisteredNfcFServicesCache.hasService(eq(USER_ID), any()))
+                .thenReturn(false);
+        when(mRegisteredNfcFServicesCache.registerSystemCodeForService(anyInt(),
+                anyInt(), any(), anyString())).thenReturn(true);
+
+        Assert.assertFalse(mCardEmulationManager.getNfcFCardEmulationInterface()
+                .registerSystemCodeForService(USER_ID, WALLET_PAYMENT_SERVICE, systemCode));
+
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.validateUserId(USER_ID);
+        });
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.enforceUserPermissions(mContext);
+        });
+        verify(mRegisteredNfcFServicesCache).initialize();
+        verify(mRegisteredNfcFServicesCache).invalidateCache(eq(USER_ID));
+        verify(mRegisteredNfcFServicesCache, times(2))
+                .hasService(eq(USER_ID),eq(WALLET_PAYMENT_SERVICE));
+        verifyNoMoreInteractions(mRegisteredNfcFServicesCache);
+    }
+
+    @Test
+    public void testNfcFCardEmulationRemoveSystemCodeForService_serviceExists()
+            throws RemoteException {
+        when(mRegisteredNfcFServicesCache.hasService(eq(USER_ID), any()))
+                .thenReturn(true);
+        when(mRegisteredNfcFServicesCache.removeSystemCodeForService(anyInt(),
+                anyInt(), any())).thenReturn(true);
+
+        Assert.assertTrue(mCardEmulationManager.getNfcFCardEmulationInterface()
+                .removeSystemCodeForService(USER_ID, WALLET_PAYMENT_SERVICE));
+
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.validateUserId(USER_ID);
+        });
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.enforceUserPermissions(mContext);
+        });
+        verify(mRegisteredNfcFServicesCache).initialize();
+        verify(mRegisteredNfcFServicesCache, times(2))
+                .hasService(eq(USER_ID),eq(WALLET_PAYMENT_SERVICE));
+        verify(mRegisteredNfcFServicesCache).removeSystemCodeForService(eq(USER_ID), anyInt(),
+                eq(WALLET_PAYMENT_SERVICE));
+        verifyNoMoreInteractions(mRegisteredNfcFServicesCache);
+    }
+
+    @Test
+    public void testNfcFCardEmulationRemoveSystemCodeForService_serviceDoesNotExists()
+            throws RemoteException {
+        when(mRegisteredNfcFServicesCache.hasService(eq(USER_ID), any()))
+                .thenReturn(false);
+        when(mRegisteredNfcFServicesCache.removeSystemCodeForService(anyInt(),
+                anyInt(), any())).thenReturn(true);
+
+        Assert.assertFalse(mCardEmulationManager.getNfcFCardEmulationInterface()
+                .removeSystemCodeForService(USER_ID, WALLET_PAYMENT_SERVICE));
+
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.validateUserId(USER_ID);
+        });
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.enforceUserPermissions(mContext);
+        });
+        verify(mRegisteredNfcFServicesCache).initialize();
+        verify(mRegisteredNfcFServicesCache).invalidateCache(eq(USER_ID));
+        verify(mRegisteredNfcFServicesCache, times(2))
+                .hasService(eq(USER_ID),eq(WALLET_PAYMENT_SERVICE));
+        verifyNoMoreInteractions(mRegisteredNfcFServicesCache);
+    }
+
+    @Test
+    public void testNfcFCardEmulationGetNfcid2ForService_serviceExists()
+            throws RemoteException {
+        String nfcid2 = "nfcid2";
+        when(mRegisteredNfcFServicesCache.hasService(eq(USER_ID), any()))
+                .thenReturn(true);
+        when(mRegisteredNfcFServicesCache.getNfcid2ForService(anyInt(),
+                anyInt(), any())).thenReturn(nfcid2);
+
+        Assert.assertEquals(mCardEmulationManager.getNfcFCardEmulationInterface()
+                .getNfcid2ForService(USER_ID, WALLET_PAYMENT_SERVICE), nfcid2);
+
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.validateUserId(USER_ID);
+        });
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.enforceUserPermissions(mContext);
+        });
+        verify(mRegisteredNfcFServicesCache).initialize();
+        verify(mRegisteredNfcFServicesCache, times(2))
+                .hasService(eq(USER_ID),eq(WALLET_PAYMENT_SERVICE));
+        verify(mRegisteredNfcFServicesCache).getNfcid2ForService(eq(USER_ID), anyInt(),
+                eq(WALLET_PAYMENT_SERVICE));
+        verifyNoMoreInteractions(mRegisteredNfcFServicesCache);
+    }
+
+    @Test
+    public void testNfcFCardEmulationGetNfcid2ForService_serviceDoesNotExists()
+            throws RemoteException {
+        String nfcid2 = "nfcid2";
+        when(mRegisteredNfcFServicesCache.hasService(eq(USER_ID), any()))
+                .thenReturn(false);
+        when(mRegisteredNfcFServicesCache.getNfcid2ForService(anyInt(),
+                anyInt(), any())).thenReturn(nfcid2);
+
+        Assert.assertNull(mCardEmulationManager.getNfcFCardEmulationInterface()
+                .getNfcid2ForService(USER_ID, WALLET_PAYMENT_SERVICE));
+
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.validateUserId(USER_ID);
+        });
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.enforceUserPermissions(mContext);
+        });
+        verify(mRegisteredNfcFServicesCache).initialize();
+        verify(mRegisteredNfcFServicesCache).invalidateCache(eq(USER_ID));
+        verify(mRegisteredNfcFServicesCache, times(2))
+                .hasService(eq(USER_ID),eq(WALLET_PAYMENT_SERVICE));
+        verifyNoMoreInteractions(mRegisteredNfcFServicesCache);
+    }
+
+    @Test
+    public void testNfcFCardEmulationSetNfcid2ForService_serviceExists()
+            throws RemoteException {
+        String nfcid2 = "nfcid2";
+        when(mRegisteredNfcFServicesCache.hasService(eq(USER_ID), any()))
+                .thenReturn(true);
+        when(mRegisteredNfcFServicesCache.setNfcid2ForService(anyInt(),
+                anyInt(), any(), anyString())).thenReturn(true);
+
+        Assert.assertTrue(mCardEmulationManager.getNfcFCardEmulationInterface()
+                .setNfcid2ForService(USER_ID, WALLET_PAYMENT_SERVICE, nfcid2));
+
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.validateUserId(USER_ID);
+        });
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.enforceUserPermissions(mContext);
+        });
+        verify(mRegisteredNfcFServicesCache).initialize();
+        verify(mRegisteredNfcFServicesCache, times(2))
+                .hasService(eq(USER_ID),eq(WALLET_PAYMENT_SERVICE));
+        verify(mRegisteredNfcFServicesCache).setNfcid2ForService(eq(USER_ID), anyInt(),
+                eq(WALLET_PAYMENT_SERVICE), eq(nfcid2));
+        verifyNoMoreInteractions(mRegisteredNfcFServicesCache);
+    }
+
+    @Test
+    public void testNfcFCardEmulationSetNfcid2ForService_serviceDoesNotExists()
+            throws RemoteException {
+        String nfcid2 = "nfcid2";
+        when(mRegisteredNfcFServicesCache.hasService(eq(USER_ID), any()))
+                .thenReturn(false);
+        when(mRegisteredNfcFServicesCache.setNfcid2ForService(anyInt(),
+                anyInt(), any(), anyString())).thenReturn(true);
+
+        Assert.assertFalse(mCardEmulationManager.getNfcFCardEmulationInterface()
+                .setNfcid2ForService(USER_ID, WALLET_PAYMENT_SERVICE, nfcid2));
+
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.validateUserId(USER_ID);
+        });
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.enforceUserPermissions(mContext);
+        });
+        verify(mRegisteredNfcFServicesCache).initialize();
+        verify(mRegisteredNfcFServicesCache).invalidateCache(eq(USER_ID));
+        verify(mRegisteredNfcFServicesCache, times(2))
+                .hasService(eq(USER_ID),eq(WALLET_PAYMENT_SERVICE));
+        verifyNoMoreInteractions(mRegisteredNfcFServicesCache);
+    }
+
+    @Test
+    public void testNfcFCardEmulationEnableNfcFForegroundService_serviceExists()
+            throws RemoteException {
+        when(mRegisteredNfcFServicesCache.hasService(eq(USER_ID), any()))
+                .thenReturn(true);
+        when(mEnabledNfcFServices.registerEnabledForegroundService(any(),
+                anyInt())).thenReturn(true);
+        when(Binder.getCallingUserHandle()).thenReturn(USER_HANDLE);
+
+        Assert.assertTrue(mCardEmulationManager.getNfcFCardEmulationInterface()
+                .enableNfcFForegroundService(WALLET_PAYMENT_SERVICE));
+
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.enforceUserPermissions(mContext);
+        });
+        verify(mRegisteredNfcFServicesCache).initialize();
+        verify(mRegisteredNfcFServicesCache, times(2))
+                .hasService(eq(USER_ID),eq(WALLET_PAYMENT_SERVICE));
+        verify(mEnabledNfcFServices).registerEnabledForegroundService(eq(WALLET_PAYMENT_SERVICE),
+                anyInt());
+        verifyNoMoreInteractions(mRegisteredNfcFServicesCache);
+    }
+
+    @Test
+    public void testNfcFCardEmulationEnableNfcFForegroundService_serviceDoesNotExists()
+            throws RemoteException {
+        when(mRegisteredNfcFServicesCache.hasService(eq(USER_ID), any()))
+                .thenReturn(false);
+        when(mEnabledNfcFServices.registerEnabledForegroundService(any(),
+                anyInt())).thenReturn(true);
+
+        Assert.assertFalse(mCardEmulationManager.getNfcFCardEmulationInterface()
+                .enableNfcFForegroundService(WALLET_PAYMENT_SERVICE));
+
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.enforceUserPermissions(mContext);
+        });
+        verify(mRegisteredNfcFServicesCache).initialize();
+        verify(mRegisteredNfcFServicesCache).invalidateCache(eq(USER_ID));
+        verify(mRegisteredNfcFServicesCache, times(2))
+                .hasService(eq(USER_ID),eq(WALLET_PAYMENT_SERVICE));
+        verifyNoMoreInteractions(mRegisteredNfcFServicesCache);
+        verifyNoMoreInteractions(mEnabledNfcFServices);
+    }
+
+    @Test
+    public void testNfcFCardEmulationDisableNfcFForegroundService_serviceDoesNotExists()
+            throws RemoteException {
+        when(mEnabledNfcFServices.unregisteredEnabledForegroundService(anyInt()))
+                .thenReturn(true);
+
+        Assert.assertTrue(mCardEmulationManager.getNfcFCardEmulationInterface()
+                .disableNfcFForegroundService());
+
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.enforceUserPermissions(mContext);
+        });
+        verify(mEnabledNfcFServices).unregisteredEnabledForegroundService(anyInt());
+        verifyNoMoreInteractions(mEnabledNfcFServices);
+    }
+
+    @Test
+    public void testNfcFCardEmulationGetServices()
+            throws RemoteException {
+        when(mRegisteredNfcFServicesCache.getServices(anyInt()))
+                .thenReturn(UPDATED_NFC_SERVICES);
+
+        Assert.assertEquals(mCardEmulationManager.getNfcFCardEmulationInterface()
+                .getNfcFServices(USER_ID), UPDATED_NFC_SERVICES);
+
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.validateProfileId(mContext, USER_ID);
+        });
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.enforceUserPermissions(mContext);
+        });
+        verify(mRegisteredNfcFServicesCache).initialize();
+        verify(mRegisteredNfcFServicesCache).getServices(eq(USER_ID));
+        verifyNoMoreInteractions(mRegisteredNfcFServicesCache);
+    }
+
+    @Test
+    public void testNfcFCardEmulationGetMaxNumOfRegisterableSystemCodes()
+            throws RemoteException {
+        when(mNfcService.getLfT3tMax()).thenReturn(3);
+
+        Assert.assertEquals(mCardEmulationManager.getNfcFCardEmulationInterface()
+                .getMaxNumOfRegisterableSystemCodes(), 3);
+
+        ExtendedMockito.verify(() -> {
+            NfcPermissions.enforceUserPermissions(mContext);
+        });
+        verify(mNfcService).getLfT3tMax();
+        verifyNoMoreInteractions(mNfcService);
+    }
+
+    @Test
+    public void testOnPreferredPaymentServiceChanged_observeModeEnabled() {
+        when(mRegisteredServicesCache.doesServiceShouldDefaultToObserveMode(anyInt(), any()))
+                .thenReturn(true);
+        when(mRegisteredAidCache.getPreferredService()).thenReturn(WALLET_PAYMENT_SERVICE);
+        when(android.nfc.Flags.nfcObserveMode()).thenReturn(true);
+
+        mCardEmulationManager.onPreferredPaymentServiceChanged(USER_ID, WALLET_PAYMENT_SERVICE);
+
+        verify(mHostEmulationManager).onPreferredPaymentServiceChanged(eq(USER_ID),
+                eq(WALLET_PAYMENT_SERVICE));
+        verify(mRegisteredAidCache).onWalletRoleHolderChanged(
+                eq(WALLET_HOLDER_PACKAGE_NAME), eq(USER_ID));
+        verify(mRegisteredAidCache).onPreferredPaymentServiceChanged(eq(USER_ID),
+                eq(WALLET_PAYMENT_SERVICE));
+        verify(mRegisteredServicesCache).initialize();
+        verify(mNfcService).onPreferredPaymentChanged(eq(NfcAdapter.PREFERRED_PAYMENT_CHANGED));
+        assertUpdateForShouldDefaultToObserveMode(true);
+    }
+
+    @Test
+    public void testOnPreferredPaymentServiceChanged_observeModeDisabled() {
+        when(mRegisteredServicesCache.doesServiceShouldDefaultToObserveMode(anyInt(), any()))
+                .thenReturn(true);
+        when(mRegisteredAidCache.getPreferredService()).thenReturn(WALLET_PAYMENT_SERVICE);
+        when(android.nfc.Flags.nfcObserveMode()).thenReturn(false);
+
+        mCardEmulationManager.onPreferredPaymentServiceChanged(USER_ID, WALLET_PAYMENT_SERVICE);
+
+        verify(mHostEmulationManager).onPreferredPaymentServiceChanged(eq(USER_ID),
+                eq(WALLET_PAYMENT_SERVICE));
+        verify(mRegisteredAidCache).onWalletRoleHolderChanged(
+                eq(WALLET_HOLDER_PACKAGE_NAME), eq(USER_ID));
+        verify(mRegisteredAidCache).onPreferredPaymentServiceChanged(eq(USER_ID),
+                eq(WALLET_PAYMENT_SERVICE));
+        verify(mRegisteredServicesCache).initialize();
+        verify(mNfcService).onPreferredPaymentChanged(eq(NfcAdapter.PREFERRED_PAYMENT_CHANGED));
+        assertUpdateForShouldDefaultToObserveMode(false);
+    }
+
+    @Test
+    public void testOnPreferredForegroundServiceChanged_observeModeEnabled() {
+        when(mRegisteredServicesCache.doesServiceShouldDefaultToObserveMode(anyInt(), any()))
+                .thenReturn(true);
+        when(mRegisteredAidCache.getPreferredService()).thenReturn(WALLET_PAYMENT_SERVICE);
+        when(android.nfc.Flags.nfcObserveMode()).thenReturn(true);
+
+        mCardEmulationManager.onPreferredForegroundServiceChanged(USER_ID, WALLET_PAYMENT_SERVICE);
+
+        verify(mRegisteredAidCache).onWalletRoleHolderChanged(
+                eq(WALLET_HOLDER_PACKAGE_NAME), eq(USER_ID));
+        verify(mHostEmulationManager).onPreferredForegroundServiceChanged(eq(USER_ID),
+                eq(WALLET_PAYMENT_SERVICE));
+        verify(mRegisteredAidCache).onPreferredForegroundServiceChanged(eq(USER_ID),
+                eq(WALLET_PAYMENT_SERVICE));
+        verify(mRegisteredServicesCache).initialize();
+        verify(mNfcService).onPreferredPaymentChanged(eq(NfcAdapter.PREFERRED_PAYMENT_CHANGED));
+        assertUpdateForShouldDefaultToObserveMode(true);
+    }
+
+    @Test
+    public void testOnPreferredForegroundServiceChanged_observeModeDisabled() {
+        when(mRegisteredServicesCache.doesServiceShouldDefaultToObserveMode(anyInt(), any()))
+                .thenReturn(true);
+        when(mRegisteredAidCache.getPreferredService()).thenReturn(WALLET_PAYMENT_SERVICE);
+        when(android.nfc.Flags.nfcObserveMode()).thenReturn(false);
+
+        mCardEmulationManager.onPreferredForegroundServiceChanged(USER_ID, WALLET_PAYMENT_SERVICE);
+
+        verify(mHostEmulationManager).onPreferredForegroundServiceChanged(eq(USER_ID),
+                eq(WALLET_PAYMENT_SERVICE));
+        verify(mRegisteredAidCache).onWalletRoleHolderChanged(
+                eq(WALLET_HOLDER_PACKAGE_NAME), eq(USER_ID));
+        verify(mRegisteredAidCache).onPreferredForegroundServiceChanged(eq(USER_ID),
+                eq(WALLET_PAYMENT_SERVICE));
+        verify(mRegisteredServicesCache).initialize();
+        verify(mNfcService).onPreferredPaymentChanged(eq(NfcAdapter.PREFERRED_PAYMENT_CHANGED));
+        assertUpdateForShouldDefaultToObserveMode(false);
+    }
+
+    @Test
+    public void testOnWalletRoleHolderChanged() {
+        mCardEmulationManager.onWalletRoleHolderChanged(WALLET_HOLDER_PACKAGE_NAME, USER_ID);
+
+        verify(mPreferredServices, times(2))
+                .onWalletRoleHolderChanged(eq(WALLET_HOLDER_PACKAGE_NAME), eq(USER_ID));
+        verify(mRegisteredAidCache, times(2))
+                .onWalletRoleHolderChanged(eq(WALLET_HOLDER_PACKAGE_NAME), eq(USER_ID));
+        verifyNoMoreInteractions(mPreferredServices);
+        verifyNoMoreInteractions(mRegisteredAidCache);
+    }
+
+    @Test
+    public void testOnEnabledForegroundNfcFServiceChanged() {
+        mCardEmulationManager.onEnabledForegroundNfcFServiceChanged(USER_ID,
+                WALLET_PAYMENT_SERVICE);
+
+        verify(mRegisteredT3tIdentifiersCache).onEnabledForegroundNfcFServiceChanged(eq(USER_ID),
+                eq(WALLET_PAYMENT_SERVICE));
+        verify(mHostNfcFEmulationManager)
+                .onEnabledForegroundNfcFServiceChanged(eq(USER_ID),
+                        eq(WALLET_PAYMENT_SERVICE));
+        verifyNoMoreInteractions(mRegisteredT3tIdentifiersCache);
+        verifyNoMoreInteractions(mHostNfcFEmulationManager);
+    }
+
+    @Test
+    public void testGetRegisteredAidCategory() {
+        RegisteredAidCache.AidResolveInfo aidResolveInfo = Mockito.mock(
+                RegisteredAidCache.AidResolveInfo.class);
+        when(aidResolveInfo.getCategory()).thenReturn(CardEmulation.CATEGORY_PAYMENT);
+
+        when(mRegisteredAidCache.resolveAid(anyString())).thenReturn(aidResolveInfo);
+
+        Assert.assertEquals(mCardEmulationManager.getRegisteredAidCategory(PAYMENT_AID_1),
+                CardEmulation.CATEGORY_PAYMENT);
+
+        verify(mRegisteredAidCache).resolveAid(eq(PAYMENT_AID_1));
+        verify(aidResolveInfo).getCategory();
+    }
+
+    @Test
+    public void testIsRequiresScreenOnServiceExist() {
+        when(mRegisteredAidCache.isRequiresScreenOnServiceExist()).thenReturn(true);
+
+        Assert.assertTrue(mCardEmulationManager.isRequiresScreenOnServiceExist());
+    }
+
+
+    private void assertUpdateForShouldDefaultToObserveMode(boolean flagEnabled) {
+        if (flagEnabled) {
+            ExtendedMockito.verify(() -> {
+                NfcAdapter.getDefaultAdapter(mContext);
+            });
+            verify(mRegisteredAidCache).getPreferredService();
+            verify(mRegisteredServicesCache).doesServiceShouldDefaultToObserveMode(eq(USER_ID),
+                    eq(WALLET_PAYMENT_SERVICE));
+            verify(mNfcAdapter).setObserveModeEnabled(eq(true));
+        }
+        verifyNoMoreInteractions(mNfcAdapter);
+        verifyNoMoreInteractions(mRegisteredServicesCache);
+        verifyNoMoreInteractions(mRegisteredAidCache);
+    }
+
+    private CardEmulationManager createInstanceWithMockParams() {
+        when(mRoutingOptionManager.getOffHostRouteEse()).thenReturn(TEST_DATA_1);
+        when(mRoutingOptionManager.getOffHostRouteUicc()).thenReturn(TEST_DATA_2);
+        when(mWalletRoleObserver.isWalletRoleFeatureEnabled()).thenReturn(true);
+        when(mWalletRoleObserver.getDefaultWalletRoleHolder(eq(USER_ID)))
+                .thenReturn(WALLET_HOLDER_PACKAGE_NAME);
+
+        return new CardEmulationManager(mContext, mForegroundUtils, mWalletRoleObserver,
+                mRegisteredAidCache, mRegisteredT3tIdentifiersCache, mHostEmulationManager,
+                mHostNfcFEmulationManager, mRegisteredServicesCache, mRegisteredNfcFServicesCache,
+                mPreferredServices, mEnabledNfcFServices, mRoutingOptionManager, mPowerManager);
+    }
+}
diff --git a/tests/unit/src/com/android/nfc/cardemulation/HostEmulationManagerTest.java b/tests/unit/src/com/android/nfc/cardemulation/HostEmulationManagerTest.java
new file mode 100644
index 0000000..efda7e5
--- /dev/null
+++ b/tests/unit/src/com/android/nfc/cardemulation/HostEmulationManagerTest.java
@@ -0,0 +1,1196 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.nfc.cardemulation;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyList;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+import static org.mockito.ArgumentMatchers.eq;
+
+import android.app.KeyguardManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.nfc.NfcAdapter;
+import android.nfc.cardemulation.ApduServiceInfo;
+import android.nfc.cardemulation.CardEmulation;
+import android.nfc.cardemulation.HostApduService;
+import android.nfc.cardemulation.PollingFrame;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.nfc.NfcEventLog;
+import com.android.nfc.NfcInjector;
+import com.android.nfc.NfcService;
+import com.android.nfc.NfcStatsLog;
+import com.android.nfc.cardemulation.util.StatsdUtils;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+import java.util.ArrayList;
+import java.util.HexFormat;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class HostEmulationManagerTest {
+
+    private static final String WALLET_HOLDER_PACKAGE_NAME = "com.android.test.walletroleholder";
+    private static final ComponentName WALLET_PAYMENT_SERVICE
+            = new ComponentName(WALLET_HOLDER_PACKAGE_NAME,
+            "com.android.test.walletroleholder.WalletRoleHolderApduService");
+    private static final ComponentName ANOTHER_WALLET_SERVICE
+            = new ComponentName(WALLET_HOLDER_PACKAGE_NAME,
+            "com.android.test.walletroleholder.XRoleHolderApduService");
+    private static final int USER_ID = 0;
+    private static final UserHandle USER_HANDLE = UserHandle.of(USER_ID);
+    private static final String PL_FILTER = "66696C746572";
+    private static final Pattern PL_PATTERN = Pattern.compile("66696C*46572");
+    private static final List<String> POLLING_LOOP_FILTER = List.of(PL_FILTER);
+    private static final List<Pattern> POLLING_LOOP_PATTEN_FILTER
+            = List.of(PL_PATTERN);
+    private static final String MOCK_AID = "A000000476416E64726F6964484340";
+
+    @Mock
+    private Context mContext;
+    @Mock
+    private RegisteredAidCache mRegisteredAidCache;
+    @Mock
+    private PowerManager mPowerManager;
+    @Mock
+    private KeyguardManager mKeyguardManager;
+    @Mock
+    private PackageManager mPackageManager;
+    @Mock
+    private NfcAdapter mNfcAdapter;
+    @Mock
+    private Messenger mMessanger;
+    @Mock
+    private NfcService mNfcService;
+    @Mock
+    private NfcInjector mNfcInjector;
+    @Mock
+    private NfcEventLog mNfcEventLog;
+    @Mock
+    private StatsdUtils mStatsUtils;
+    @Captor
+    private ArgumentCaptor<Intent> mIntentArgumentCaptor;
+    @Captor
+    private ArgumentCaptor<ServiceConnection> mServiceConnectionArgumentCaptor;
+    @Captor
+    private ArgumentCaptor<List<ApduServiceInfo>> mServiceListArgumentCaptor;
+    @Captor
+    private ArgumentCaptor<Message> mMessageArgumentCaptor;
+
+    private MockitoSession mStaticMockSession;
+    private TestableLooper mTestableLooper;
+    private HostEmulationManager mHostEmulationManager;
+
+    @Before
+    public void setUp() {
+        mStaticMockSession = ExtendedMockito.mockitoSession()
+                .mockStatic(com.android.nfc.flags.Flags.class)
+                .mockStatic(NfcStatsLog.class)
+                .mockStatic(UserHandle.class)
+                .mockStatic(NfcAdapter.class)
+                .mockStatic(NfcService.class)
+                .mockStatic(NfcInjector.class)
+                .strictness(Strictness.LENIENT)
+                .startMocking();
+        MockitoAnnotations.initMocks(this);
+        mTestableLooper = TestableLooper.get(this);
+        when(NfcAdapter.getDefaultAdapter(mContext)).thenReturn(mNfcAdapter);
+        when(UserHandle.getUserHandleForUid(eq(USER_ID))).thenReturn(USER_HANDLE);
+        when(UserHandle.of(eq(USER_ID))).thenReturn(USER_HANDLE);
+        when(NfcService.getInstance()).thenReturn(mNfcService);
+        when(NfcInjector.getInstance()).thenReturn(mNfcInjector);
+        when(mNfcInjector.getNfcEventLog()).thenReturn(mNfcEventLog);
+        when(com.android.nfc.flags.Flags.statsdCeEventsFlag()).thenReturn(true);
+        when(mContext.getSystemService(eq(PowerManager.class))).thenReturn(mPowerManager);
+        when(mContext.getSystemService(eq(KeyguardManager.class))).thenReturn(mKeyguardManager);
+        mHostEmulationManager = new HostEmulationManager(mContext, mTestableLooper.getLooper(),
+                mRegisteredAidCache, mStatsUtils);
+    }
+
+    @After
+    public void tearDown() {
+        mStaticMockSession.finishMocking();
+    }
+
+    @Test
+    public void testConstructor() {
+        verify(mContext).getSystemService(eq(PowerManager.class));
+        verify(mContext).getSystemService(eq(KeyguardManager.class));
+        verifyNoMoreInteractions(mContext);
+    }
+
+    @Test
+    public void testOnPreferredPaymentServiceChanged_nullService() {
+        mHostEmulationManager.mPaymentServiceBound = true;
+
+        mHostEmulationManager.onPreferredPaymentServiceChanged(USER_ID, null);
+        mTestableLooper.processAllMessages();
+
+        verify(mContext).getSystemService(eq(PowerManager.class));
+        verify(mContext).getSystemService(eq(KeyguardManager.class));
+        verify(mContext).unbindService(eq(mHostEmulationManager.getPaymentConnection()));
+        verifyNoMoreInteractions(mContext);
+    }
+
+    @Test
+    public void testOnPreferredPaymentServiceChanged_noPreviouslyBoundService() {
+        when(mContext.bindServiceAsUser(any(), any(), anyInt(), any())).thenReturn(true);
+        UserHandle userHandle = UserHandle.of(USER_ID);
+
+        mHostEmulationManager.onPreferredPaymentServiceChanged(USER_ID, WALLET_PAYMENT_SERVICE);
+        mTestableLooper.processAllMessages();
+
+        verify(mContext).getSystemService(eq(PowerManager.class));
+        verify(mContext).getSystemService(eq(KeyguardManager.class));
+        verify(mContext).bindServiceAsUser(mIntentArgumentCaptor.capture(),
+                mServiceConnectionArgumentCaptor.capture(),
+                eq(Context.BIND_AUTO_CREATE | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS),
+                eq(userHandle));
+        verifyNoMoreInteractions(mContext);
+        Intent intent = mIntentArgumentCaptor.getValue();
+        Assert.assertEquals(intent.getAction(), HostApduService.SERVICE_INTERFACE);
+        Assert.assertEquals(intent.getComponent(), WALLET_PAYMENT_SERVICE);
+        Assert.assertTrue(mHostEmulationManager.mPaymentServiceBound);
+        Assert.assertEquals(mHostEmulationManager.mLastBoundPaymentServiceName,
+                WALLET_PAYMENT_SERVICE);
+        Assert.assertEquals(mHostEmulationManager.mPaymentServiceUserId,
+                USER_ID);
+    }
+
+    @Test
+    public void testOnPreferredPaymentServiceChanged_previouslyBoundService() {
+        when(mContext.bindServiceAsUser(any(), any(), anyInt(), any())).thenReturn(true);
+        UserHandle userHandle = UserHandle.of(USER_ID);
+        mHostEmulationManager.mPaymentServiceBound = true;
+
+        mHostEmulationManager.onPreferredPaymentServiceChanged(USER_ID, WALLET_PAYMENT_SERVICE);
+        mTestableLooper.processAllMessages();
+
+        verify(mContext).getSystemService(eq(PowerManager.class));
+        verify(mContext).getSystemService(eq(KeyguardManager.class));
+        verify(mContext).unbindService(eq(mHostEmulationManager.getPaymentConnection()));
+        verify(mContext).bindServiceAsUser(mIntentArgumentCaptor.capture(),
+                mServiceConnectionArgumentCaptor.capture(),
+                eq(Context.BIND_AUTO_CREATE | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS),
+                eq(userHandle));
+        verifyNoMoreInteractions(mContext);
+        Intent intent = mIntentArgumentCaptor.getValue();
+        Assert.assertEquals(intent.getAction(), HostApduService.SERVICE_INTERFACE);
+        Assert.assertEquals(intent.getComponent(), WALLET_PAYMENT_SERVICE);
+        Assert.assertTrue(mHostEmulationManager.mPaymentServiceBound);
+        Assert.assertEquals(mHostEmulationManager.mLastBoundPaymentServiceName,
+                WALLET_PAYMENT_SERVICE);
+        Assert.assertEquals(mHostEmulationManager.mPaymentServiceUserId,
+                USER_ID);
+        Assert.assertNotNull(mServiceConnectionArgumentCaptor.getValue());
+    }
+
+    @Test
+    public void testUpdatePollingLoopFilters() {
+        ApduServiceInfo serviceWithFilter = mock(ApduServiceInfo.class);
+        when(serviceWithFilter.getPollingLoopFilters()).thenReturn(POLLING_LOOP_FILTER);
+        when(serviceWithFilter.getPollingLoopPatternFilters()).thenReturn(List.of());
+        ApduServiceInfo serviceWithPatternFilter = mock(ApduServiceInfo.class);
+        when(serviceWithPatternFilter.getPollingLoopFilters()).thenReturn(List.of());
+        when(serviceWithPatternFilter.getPollingLoopPatternFilters())
+                .thenReturn(POLLING_LOOP_PATTEN_FILTER);
+
+        mHostEmulationManager.updatePollingLoopFilters(USER_ID, List.of(serviceWithFilter,
+                serviceWithPatternFilter));
+
+        Map<Integer, Map<String, List<ApduServiceInfo>>> pollingLoopFilters
+                = mHostEmulationManager.getPollingLoopFilters();
+        Map<Integer, Map<Pattern, List<ApduServiceInfo>>> pollingLoopPatternFilters
+                = mHostEmulationManager.getPollingLoopPatternFilters();
+        Assert.assertTrue(pollingLoopFilters.containsKey(USER_ID));
+        Assert.assertTrue(pollingLoopPatternFilters.containsKey(USER_ID));
+        Map<String, List<ApduServiceInfo>> filtersForUser = pollingLoopFilters.get(USER_ID);
+        Map<Pattern, List<ApduServiceInfo>> patternFiltersForUser = pollingLoopPatternFilters
+                .get(USER_ID);
+        Assert.assertTrue(filtersForUser.containsKey(PL_FILTER));
+        Assert.assertTrue(patternFiltersForUser.containsKey(PL_PATTERN));
+        Assert.assertTrue(filtersForUser.get(PL_FILTER).contains(serviceWithFilter));
+        Assert.assertTrue(patternFiltersForUser.get(PL_PATTERN).contains(serviceWithPatternFilter));
+    }
+
+    @Test
+    public void testOnPollingLoopDetected_activeServiceAlreadyBound_overlappingServices()
+            throws PackageManager.NameNotFoundException, RemoteException {
+        ApduServiceInfo serviceWithFilter = mock(ApduServiceInfo.class);
+        when(serviceWithFilter.getPollingLoopFilters()).thenReturn(POLLING_LOOP_FILTER);
+        when(serviceWithFilter.getPollingLoopPatternFilters()).thenReturn(List.of());
+        when(serviceWithFilter.getShouldAutoTransact(anyString())).thenReturn(true);
+        when(serviceWithFilter.getComponent()).thenReturn(WALLET_PAYMENT_SERVICE);
+        when(serviceWithFilter.getUid()).thenReturn(USER_ID);
+        ApduServiceInfo overlappingServiceWithFilter = mock(ApduServiceInfo.class);
+        when(overlappingServiceWithFilter.getPollingLoopFilters()).thenReturn(POLLING_LOOP_FILTER);
+        when(overlappingServiceWithFilter.getPollingLoopPatternFilters()).thenReturn(List.of());
+        ApduServiceInfo serviceWithPatternFilter = mock(ApduServiceInfo.class);
+        when(serviceWithPatternFilter.getPollingLoopFilters()).thenReturn(List.of());
+        when(serviceWithPatternFilter.getPollingLoopPatternFilters())
+                .thenReturn(POLLING_LOOP_PATTEN_FILTER);
+        when(mRegisteredAidCache.resolvePollingLoopFilterConflict(anyList()))
+                .thenReturn(serviceWithFilter);
+        mHostEmulationManager.updatePollingLoopFilters(USER_ID, List.of(serviceWithFilter,
+                serviceWithPatternFilter, overlappingServiceWithFilter));
+        when(mContext.getPackageManager()).thenReturn(mPackageManager);
+        when(mRegisteredAidCache.getPreferredService()).thenReturn(WALLET_PAYMENT_SERVICE);
+        ApplicationInfo applicationInfo = new ApplicationInfo();
+        applicationInfo.uid = USER_ID;
+        mHostEmulationManager.mPaymentServiceName = WALLET_PAYMENT_SERVICE;
+        when(mPackageManager.getApplicationInfo(eq(WALLET_HOLDER_PACKAGE_NAME), eq(0)))
+                .thenReturn(applicationInfo);
+        String data = "filter";
+        PollingFrame frame1 = new PollingFrame(PollingFrame.POLLING_LOOP_TYPE_UNKNOWN,
+                data.getBytes(), 0, 0, false);
+        PollingFrame frame2 = new PollingFrame(PollingFrame.POLLING_LOOP_TYPE_OFF,
+                null, 0, 0, false);
+
+        mHostEmulationManager.mActiveService = mMessanger;
+
+        mHostEmulationManager.onPollingLoopDetected(List.of(frame1, frame2));
+
+        verify(mContext).getSystemService(eq(PowerManager.class));
+        verify(mContext).getSystemService(eq(KeyguardManager.class));
+        verify(mRegisteredAidCache)
+                .resolvePollingLoopFilterConflict(mServiceListArgumentCaptor.capture());
+        Assert.assertTrue(mServiceListArgumentCaptor.getValue().contains(serviceWithFilter));
+        Assert.assertTrue(mServiceListArgumentCaptor.getValue()
+                .contains(overlappingServiceWithFilter));
+        verify(mNfcAdapter).setObserveModeEnabled(eq(false));
+        Assert.assertTrue(mHostEmulationManager.mEnableObserveModeAfterTransaction);
+        Assert.assertTrue(frame1.getTriggeredAutoTransact());
+        Assert.assertEquals(mHostEmulationManager.mState, HostEmulationManager.STATE_POLLING_LOOP);
+        verify(mMessanger).send(mMessageArgumentCaptor.capture());
+        Message message = mMessageArgumentCaptor.getValue();
+        Bundle bundle = message.getData();
+        Assert.assertEquals(message.what, HostApduService.MSG_POLLING_LOOP);
+        Assert.assertTrue(bundle.containsKey(HostApduService.KEY_POLLING_LOOP_FRAMES_BUNDLE));
+        ArrayList<PollingFrame> sentFrames = bundle
+                .getParcelableArrayList(HostApduService.KEY_POLLING_LOOP_FRAMES_BUNDLE);
+        Assert.assertTrue(sentFrames.contains(frame1));
+        Assert.assertTrue(sentFrames.contains(frame2));
+        Assert.assertNull(mHostEmulationManager.mPendingPollingLoopFrames);
+    }
+
+    @Test
+    public void testOnPollingLoopDetected_paymentServiceAlreadyBound_4Frames()
+            throws PackageManager.NameNotFoundException, RemoteException {
+        ApduServiceInfo serviceWithFilter = mock(ApduServiceInfo.class);
+        when(serviceWithFilter.getPollingLoopFilters()).thenReturn(POLLING_LOOP_FILTER);
+        when(serviceWithFilter.getPollingLoopPatternFilters()).thenReturn(List.of());
+        when(serviceWithFilter.getShouldAutoTransact(anyString())).thenReturn(true);
+        when(serviceWithFilter.getComponent()).thenReturn(WALLET_PAYMENT_SERVICE);
+        when(serviceWithFilter.getUid()).thenReturn(USER_ID);
+        mHostEmulationManager.updatePollingLoopFilters(USER_ID, List.of(serviceWithFilter));
+        when(mContext.getPackageManager()).thenReturn(mPackageManager);
+        when(mRegisteredAidCache.getPreferredService()).thenReturn(WALLET_PAYMENT_SERVICE);
+        ApplicationInfo applicationInfo = new ApplicationInfo();
+        applicationInfo.uid = USER_ID;
+        when(mPackageManager.getApplicationInfo(eq(WALLET_HOLDER_PACKAGE_NAME), eq(0)))
+                .thenReturn(applicationInfo);
+        PollingFrame frame1 = new PollingFrame(PollingFrame.POLLING_LOOP_TYPE_ON,
+                null, 0, 0, false);
+        PollingFrame frame2 = new PollingFrame(PollingFrame.POLLING_LOOP_TYPE_ON,
+                null, 0, 0, false);
+        PollingFrame frame3 = new PollingFrame(PollingFrame.POLLING_LOOP_TYPE_OFF,
+                null, 0, 0, false);
+        PollingFrame frame4 = new PollingFrame(PollingFrame.POLLING_LOOP_TYPE_OFF,
+                null, 0, 0, false);
+        mHostEmulationManager.mPaymentService = mMessanger;
+        mHostEmulationManager.mPaymentServiceName = WALLET_PAYMENT_SERVICE;
+
+        mHostEmulationManager.onPollingLoopDetected(List.of(frame1, frame2, frame3, frame4));
+
+        verify(mContext).getSystemService(eq(PowerManager.class));
+        verify(mContext).getSystemService(eq(KeyguardManager.class));
+        verify(mMessanger).send(mMessageArgumentCaptor.capture());
+        Message message = mMessageArgumentCaptor.getValue();
+        Bundle bundle = message.getData();
+        Assert.assertEquals(message.what, HostApduService.MSG_POLLING_LOOP);
+        Assert.assertTrue(bundle.containsKey(HostApduService.KEY_POLLING_LOOP_FRAMES_BUNDLE));
+        ArrayList<PollingFrame> sentFrames = bundle
+                .getParcelableArrayList(HostApduService.KEY_POLLING_LOOP_FRAMES_BUNDLE);
+        Assert.assertTrue(sentFrames.contains(frame1));
+        Assert.assertTrue(sentFrames.contains(frame2));
+        Assert.assertTrue(sentFrames.contains(frame3));
+        Assert.assertTrue(sentFrames.contains(frame4));
+        Assert.assertNull(mHostEmulationManager.mPendingPollingLoopFrames);
+    }
+
+    @Test
+    public void testOnPreferredForegroundServiceChanged_noPreviouslyBoundService() {
+        when(mContext.bindServiceAsUser(any(), any(), anyInt(), any())).thenReturn(true);
+        UserHandle userHandle = UserHandle.of(USER_ID);
+
+        mHostEmulationManager.onPreferredForegroundServiceChanged(USER_ID, WALLET_PAYMENT_SERVICE);
+
+        verify(mContext).getSystemService(eq(PowerManager.class));
+        verify(mContext).getSystemService(eq(KeyguardManager.class));
+        verify(mContext).bindServiceAsUser(mIntentArgumentCaptor.capture(),
+                mServiceConnectionArgumentCaptor.capture(),
+                eq(Context.BIND_AUTO_CREATE | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS),
+                eq(userHandle));
+        verifyNoMoreInteractions(mContext);
+        Intent intent = mIntentArgumentCaptor.getValue();
+        Assert.assertEquals(intent.getAction(), HostApduService.SERVICE_INTERFACE);
+        Assert.assertEquals(intent.getComponent(), WALLET_PAYMENT_SERVICE);
+        Assert.assertTrue(mHostEmulationManager.mServiceBound);
+        Assert.assertEquals(mHostEmulationManager.mServiceUserId, USER_ID);
+    }
+
+    @Test
+    public void testOnPreferredForegroundServiceChanged_nullService() {
+        mHostEmulationManager.mServiceBound = true;
+
+        mHostEmulationManager.onPreferredForegroundServiceChanged(USER_ID, null);
+
+        verify(mContext).getSystemService(eq(PowerManager.class));
+        verify(mContext).getSystemService(eq(KeyguardManager.class));
+        verify(mContext).unbindService(eq(mHostEmulationManager.getServiceConnection()));
+        verifyNoMoreInteractions(mContext);
+    }
+
+    @Test
+    public void testOnPreferredForegroundServiceChanged_nullService_previouslyBoundService() {
+        when(mContext.bindServiceAsUser(any(), any(), anyInt(), any())).thenReturn(true);
+        UserHandle userHandle = UserHandle.of(USER_ID);
+        mHostEmulationManager.mServiceBound = true;
+
+        mHostEmulationManager.onPreferredForegroundServiceChanged(USER_ID, WALLET_PAYMENT_SERVICE);
+
+        verify(mContext).getSystemService(eq(PowerManager.class));
+        verify(mContext).getSystemService(eq(KeyguardManager.class));
+        verify(mContext).unbindService(eq(mHostEmulationManager.getServiceConnection()));
+        verify(mContext).bindServiceAsUser(mIntentArgumentCaptor.capture(),
+                mServiceConnectionArgumentCaptor.capture(),
+                eq(Context.BIND_AUTO_CREATE | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS),
+                eq(userHandle));
+        verifyNoMoreInteractions(mContext);
+        Intent intent = mIntentArgumentCaptor.getValue();
+        Assert.assertEquals(intent.getAction(), HostApduService.SERVICE_INTERFACE);
+        Assert.assertEquals(intent.getComponent(), WALLET_PAYMENT_SERVICE);
+        Assert.assertTrue(mHostEmulationManager.mServiceBound);
+        Assert.assertEquals(mHostEmulationManager.mServiceUserId, USER_ID);
+        Assert.assertNotNull(mServiceConnectionArgumentCaptor.getValue());
+    }
+
+    @Test
+    public void testOnHostEmulationActivated() {
+        mHostEmulationManager.onHostEmulationActivated();
+
+        verify(mContext).getSystemService(eq(PowerManager.class));
+        verify(mContext).getSystemService(eq(KeyguardManager.class));
+        verify(mContext).sendBroadcastAsUser(mIntentArgumentCaptor.capture(),
+                eq(UserHandle.ALL));
+        verifyNoMoreInteractions(mContext);
+        Intent intent = mIntentArgumentCaptor.getValue();
+        Assert.assertEquals(TapAgainDialog.ACTION_CLOSE, intent.getAction());
+        Assert.assertEquals(HostEmulationManager.NFC_PACKAGE, intent.getPackage());
+        Assert.assertEquals(HostEmulationManager.STATE_W4_SELECT, mHostEmulationManager.getState());
+    }
+
+    @Test
+    public void testOnHostEmulationData_stateIdle() {
+        byte[] emptyData = new byte[1];
+        mHostEmulationManager.mState = HostEmulationManager.STATE_IDLE;
+
+        mHostEmulationManager.onHostEmulationData(emptyData);
+
+        verifyZeroInteractions(mNfcService);
+        verify(mContext).getSystemService(eq(PowerManager.class));
+        verify(mContext).getSystemService(eq(KeyguardManager.class));
+        verifyNoMoreInteractions(mContext);
+    }
+
+    @Test
+    public void testOnHostEmulationData_stateW4Deactivate() {
+        byte[] emptyData = new byte[1];
+        mHostEmulationManager.mState = HostEmulationManager.STATE_W4_DEACTIVATE;
+
+        mHostEmulationManager.onHostEmulationData(emptyData);
+
+        verifyZeroInteractions(mNfcService);
+        verify(mContext).getSystemService(eq(PowerManager.class));
+        verify(mContext).getSystemService(eq(KeyguardManager.class));
+        verifyNoMoreInteractions(mContext);
+    }
+
+    @Test
+    public void testOnHostEmulationData_stateW4Select_hceAid() {
+        byte[] hceAidData = createSelectAidData(HostEmulationManager.ANDROID_HCE_AID);
+        mHostEmulationManager.mState = HostEmulationManager.STATE_W4_SELECT;
+
+        mHostEmulationManager.onHostEmulationData(hceAidData);
+
+        verify(mNfcService).sendData(eq(HostEmulationManager.ANDROID_HCE_RESPONSE));
+        verify(mContext).getSystemService(eq(PowerManager.class));
+        verify(mContext).getSystemService(eq(KeyguardManager.class));
+        verifyNoMoreInteractions(mContext);
+    }
+
+    @Test
+    public void testOnHostEmulationData_stateW4Select_nullResolveInfo() {
+        byte[] mockAidData = createSelectAidData(MOCK_AID);
+        mHostEmulationManager.mState = HostEmulationManager.STATE_W4_SELECT;
+        when(mRegisteredAidCache.resolveAid(eq(MOCK_AID))).thenReturn(null);
+
+        mHostEmulationManager.onHostEmulationData(mockAidData);
+
+        verify(mRegisteredAidCache).resolveAid(eq(MOCK_AID));
+        verify(mNfcService).sendData(eq(HostEmulationManager.AID_NOT_FOUND));
+        verify(mContext).getSystemService(eq(PowerManager.class));
+        verify(mContext).getSystemService(eq(KeyguardManager.class));
+        verifyNoMoreInteractions(mContext);
+    }
+
+    @Test
+    public void testOnHostEmulationData_stateW4Select_emptyResolveInfoServices() {
+        byte[] mockAidData = createSelectAidData(MOCK_AID);
+        mHostEmulationManager.mState = HostEmulationManager.STATE_W4_SELECT;
+        RegisteredAidCache.AidResolveInfo aidResolveInfo = mRegisteredAidCache.new AidResolveInfo();
+        aidResolveInfo.services = new ArrayList<>();
+        when(mRegisteredAidCache.resolveAid(eq(MOCK_AID))).thenReturn(aidResolveInfo);
+
+        mHostEmulationManager.onHostEmulationData(mockAidData);
+
+        verify(mRegisteredAidCache).resolveAid(eq(MOCK_AID));
+        verify(mNfcService).sendData(eq(HostEmulationManager.AID_NOT_FOUND));
+        verify(mContext).getSystemService(eq(PowerManager.class));
+        verify(mContext).getSystemService(eq(KeyguardManager.class));
+        verifyNoMoreInteractions(mContext);
+    }
+
+    @Test
+    public void testOnHostEmulationData_stateW4Select_defaultServiceExists_requiresUnlock() {
+        byte[] mockAidData = createSelectAidData(MOCK_AID);
+        mHostEmulationManager.mState = HostEmulationManager.STATE_W4_SELECT;
+        ApduServiceInfo apduServiceInfo = mock(ApduServiceInfo.class);
+        RegisteredAidCache.AidResolveInfo aidResolveInfo = mRegisteredAidCache.new AidResolveInfo();
+        aidResolveInfo.services = new ArrayList<>();
+        aidResolveInfo.services.add(apduServiceInfo);
+        aidResolveInfo.category = CardEmulation.CATEGORY_PAYMENT;
+        when(apduServiceInfo.requiresUnlock()).thenReturn(true);
+        when(apduServiceInfo.getUid()).thenReturn(USER_ID);
+        when(mNfcService.isSecureNfcEnabled()).thenReturn(false);
+        when(mKeyguardManager.isKeyguardLocked()).thenReturn(true);
+        aidResolveInfo.defaultService = apduServiceInfo;
+        when(mRegisteredAidCache.resolveAid(eq(MOCK_AID))).thenReturn(aidResolveInfo);
+
+        mHostEmulationManager.onHostEmulationData(mockAidData);
+
+        verify(apduServiceInfo, times(2)).getUid();
+        verify(mStatsUtils).setCardEmulationEventCategory(eq(CardEmulation.CATEGORY_PAYMENT));
+        verify(mStatsUtils).setCardEmulationEventUid(eq(USER_ID));
+        verify(mStatsUtils).logCardEmulationWrongSettingEvent();
+        verify(mRegisteredAidCache).resolveAid(eq(MOCK_AID));
+        verify(mNfcService).sendRequireUnlockIntent();
+        verify(mNfcService).sendData(eq(HostEmulationManager.AID_NOT_FOUND));
+        verify(mContext).getSystemService(eq(PowerManager.class));
+        verify(mContext).getSystemService(eq(KeyguardManager.class));
+        verifyTapAgainLaunched(apduServiceInfo, CardEmulation.CATEGORY_PAYMENT);
+        verifyNoMoreInteractions(mContext);
+    }
+
+    @Test
+    public void testOnHostEmulationData_stateW4Select_defaultServiceExists_secureNfcEnabled() {
+        byte[] mockAidData = createSelectAidData(MOCK_AID);
+        mHostEmulationManager.mState = HostEmulationManager.STATE_W4_SELECT;
+        ApduServiceInfo apduServiceInfo = mock(ApduServiceInfo.class);
+        RegisteredAidCache.AidResolveInfo aidResolveInfo = mRegisteredAidCache.new AidResolveInfo();
+        aidResolveInfo.services = new ArrayList<>();
+        aidResolveInfo.services.add(apduServiceInfo);
+        aidResolveInfo.category = CardEmulation.CATEGORY_PAYMENT;
+        when(apduServiceInfo.requiresUnlock()).thenReturn(false);
+        when(mNfcService.isSecureNfcEnabled()).thenReturn(true);
+        when(mKeyguardManager.isKeyguardLocked()).thenReturn(true);
+        aidResolveInfo.defaultService = apduServiceInfo;
+        when(mRegisteredAidCache.resolveAid(eq(MOCK_AID))).thenReturn(aidResolveInfo);
+
+        mHostEmulationManager.onHostEmulationData(mockAidData);
+
+        verify(apduServiceInfo, times(2)).getUid();
+        verify(mStatsUtils).setCardEmulationEventCategory(eq(CardEmulation.CATEGORY_PAYMENT));
+        verify(mStatsUtils).setCardEmulationEventUid(eq(USER_ID));
+        verify(mStatsUtils).logCardEmulationWrongSettingEvent();
+        verify(mRegisteredAidCache).resolveAid(eq(MOCK_AID));
+        verify(mNfcService).sendRequireUnlockIntent();
+        verify(mNfcService).sendData(eq(HostEmulationManager.AID_NOT_FOUND));
+        verify(mContext).getSystemService(eq(PowerManager.class));
+        verify(mContext).getSystemService(eq(KeyguardManager.class));
+        verifyTapAgainLaunched(apduServiceInfo, CardEmulation.CATEGORY_PAYMENT);
+        verifyNoMoreInteractions(mContext);
+    }
+
+    @Test
+    public void testOnHostEmulationData_stateW4Select_defaultServiceExists_requiresScreenOn() {
+        byte[] mockAidData = createSelectAidData(MOCK_AID);
+        mHostEmulationManager.mState = HostEmulationManager.STATE_W4_SELECT;
+        ApduServiceInfo apduServiceInfo = mock(ApduServiceInfo.class);
+        RegisteredAidCache.AidResolveInfo aidResolveInfo = mRegisteredAidCache.new AidResolveInfo();
+        aidResolveInfo.category = CardEmulation.CATEGORY_PAYMENT;
+        aidResolveInfo.services = new ArrayList<>();
+        aidResolveInfo.services.add(apduServiceInfo);
+        when(apduServiceInfo.requiresUnlock()).thenReturn(false);
+        when(apduServiceInfo.requiresScreenOn()).thenReturn(true);
+        when(mNfcService.isSecureNfcEnabled()).thenReturn(false);
+        when(mKeyguardManager.isKeyguardLocked()).thenReturn(true);
+        when(mPowerManager.isScreenOn()).thenReturn(false);
+        aidResolveInfo.defaultService = apduServiceInfo;
+        when(mRegisteredAidCache.resolveAid(eq(MOCK_AID))).thenReturn(aidResolveInfo);
+
+        mHostEmulationManager.onHostEmulationData(mockAidData);
+
+        verify(apduServiceInfo).getUid();
+        verify(mStatsUtils).setCardEmulationEventCategory(eq(CardEmulation.CATEGORY_PAYMENT));
+        verify(mStatsUtils).setCardEmulationEventUid(eq(USER_ID));
+        verify(mStatsUtils).logCardEmulationWrongSettingEvent();
+        verify(mRegisteredAidCache).resolveAid(eq(MOCK_AID));
+        verify(mNfcService).sendData(eq(HostEmulationManager.AID_NOT_FOUND));
+        verify(mContext).getSystemService(eq(PowerManager.class));
+        verify(mContext).getSystemService(eq(KeyguardManager.class));
+        verifyNoMoreInteractions(mContext);
+    }
+
+    @Test
+    public void testOnHostEmulationData_stateW4Select_defaultServiceExists_notOnHost() {
+        byte[] mockAidData = createSelectAidData(MOCK_AID);
+        mHostEmulationManager.mState = HostEmulationManager.STATE_W4_SELECT;
+        ApduServiceInfo apduServiceInfo = mock(ApduServiceInfo.class);
+        RegisteredAidCache.AidResolveInfo aidResolveInfo = mRegisteredAidCache.new AidResolveInfo();
+        aidResolveInfo.services = new ArrayList<>();
+        aidResolveInfo.services.add(apduServiceInfo);
+        aidResolveInfo.category = CardEmulation.CATEGORY_PAYMENT;
+        when(apduServiceInfo.requiresUnlock()).thenReturn(false);
+        when(apduServiceInfo.requiresScreenOn()).thenReturn(false);
+        when(apduServiceInfo.isOnHost()).thenReturn(false);
+        when(mNfcService.isSecureNfcEnabled()).thenReturn(false);
+        when(mKeyguardManager.isKeyguardLocked()).thenReturn(true);
+        when(mPowerManager.isScreenOn()).thenReturn(true);
+        aidResolveInfo.defaultService = apduServiceInfo;
+        when(mRegisteredAidCache.resolveAid(eq(MOCK_AID))).thenReturn(aidResolveInfo);
+
+        mHostEmulationManager.onHostEmulationData(mockAidData);
+
+        verify(apduServiceInfo).getUid();
+        verify(mStatsUtils).setCardEmulationEventCategory(eq(CardEmulation.CATEGORY_PAYMENT));
+        verify(mStatsUtils).setCardEmulationEventUid(eq(USER_ID));
+        verify(mStatsUtils).logCardEmulationNoRoutingEvent();
+        verify(mRegisteredAidCache).resolveAid(eq(MOCK_AID));
+        verify(mNfcService).sendData(eq(HostEmulationManager.AID_NOT_FOUND));
+        verify(mContext).getSystemService(eq(PowerManager.class));
+        verify(mContext).getSystemService(eq(KeyguardManager.class));
+        verifyNoMoreInteractions(mContext);
+    }
+
+    @Test
+    public void testOnHostEmulationData_stateW4Select_noDefaultService_noActiveService() {
+        byte[] mockAidData = createSelectAidData(MOCK_AID);
+        mHostEmulationManager.mState = HostEmulationManager.STATE_W4_SELECT;
+        ApduServiceInfo apduServiceInfo = mock(ApduServiceInfo.class);
+        RegisteredAidCache.AidResolveInfo aidResolveInfo = mRegisteredAidCache.new AidResolveInfo();
+        aidResolveInfo.category = CardEmulation.CATEGORY_PAYMENT;
+        aidResolveInfo.services = new ArrayList<>();
+        aidResolveInfo.services.add(apduServiceInfo);
+        when(mRegisteredAidCache.resolveAid(eq(MOCK_AID))).thenReturn(aidResolveInfo);
+        when(apduServiceInfo.getComponent()).thenReturn(WALLET_PAYMENT_SERVICE);
+
+        mHostEmulationManager.onHostEmulationData(mockAidData);
+
+        ExtendedMockito.verify(() -> {
+            NfcStatsLog.write(NfcStatsLog.NFC_AID_CONFLICT_OCCURRED, MOCK_AID);
+        });
+        verify(mStatsUtils).setCardEmulationEventCategory(eq(CardEmulation.CATEGORY_OTHER));
+        verify(mStatsUtils).logCardEmulationWrongSettingEvent();
+        Assert.assertEquals(HostEmulationManager.STATE_W4_DEACTIVATE,
+                mHostEmulationManager.getState());
+        verify(mRegisteredAidCache).resolveAid(eq(MOCK_AID));
+        verify(mContext).getSystemService(eq(PowerManager.class));
+        verify(mContext).getSystemService(eq(KeyguardManager.class));
+        verifyResolverLaunched((ArrayList)aidResolveInfo.services, null,
+                CardEmulation.CATEGORY_PAYMENT);
+        verifyNoMoreInteractions(mContext);
+    }
+
+    @Test
+    public void testOnHostEmulationData_stateW4Select_noDefaultService_matchingActiveService()
+            throws RemoteException {
+        byte[] mockAidData = createSelectAidData(MOCK_AID);
+        IBinder binder = mock(IBinder.class);
+        mHostEmulationManager.mState = HostEmulationManager.STATE_W4_SELECT;
+        ApduServiceInfo apduServiceInfo = mock(ApduServiceInfo.class);
+        RegisteredAidCache.AidResolveInfo aidResolveInfo = mRegisteredAidCache.new AidResolveInfo();
+        aidResolveInfo.services = new ArrayList<>();
+        aidResolveInfo.services.add(apduServiceInfo);
+        aidResolveInfo.category = CardEmulation.CATEGORY_PAYMENT;
+        when(apduServiceInfo.requiresUnlock()).thenReturn(false);
+        when(apduServiceInfo.requiresScreenOn()).thenReturn(false);
+        when(apduServiceInfo.isOnHost()).thenReturn(false);
+        when(apduServiceInfo.getComponent()).thenReturn(WALLET_PAYMENT_SERVICE);
+        when(mNfcService.isSecureNfcEnabled()).thenReturn(false);
+        when(mKeyguardManager.isKeyguardLocked()).thenReturn(false);
+        when(mPowerManager.isScreenOn()).thenReturn(true);
+        when(mRegisteredAidCache.resolveAid(eq(MOCK_AID))).thenReturn(aidResolveInfo);
+        mHostEmulationManager.mActiveServiceName = WALLET_PAYMENT_SERVICE;
+        mHostEmulationManager.mActiveService = mMessanger;
+        when(mMessanger.getBinder()).thenReturn(binder);
+        mHostEmulationManager.mPaymentServiceBound = true;
+        mHostEmulationManager.mPaymentServiceName = WALLET_PAYMENT_SERVICE;
+        mHostEmulationManager.mPaymentService = mMessanger;
+
+        mHostEmulationManager.onHostEmulationData(mockAidData);
+
+        Assert.assertEquals(HostEmulationManager.STATE_XFER, mHostEmulationManager.getState());
+        verify(apduServiceInfo).getUid();
+        verify(mStatsUtils).setCardEmulationEventCategory(eq(CardEmulation.CATEGORY_PAYMENT));
+        verify(mStatsUtils).setCardEmulationEventUid(eq(USER_ID));
+        verify(mStatsUtils).notifyCardEmulationEventWaitingForResponse();
+        verify(mRegisteredAidCache).resolveAid(eq(MOCK_AID));
+        verify(mContext).getSystemService(eq(PowerManager.class));
+        verify(mContext).getSystemService(eq(KeyguardManager.class));
+        verifyNoMoreInteractions(mContext);
+        verify(mMessanger).send(mMessageArgumentCaptor.capture());
+        Message message = mMessageArgumentCaptor.getValue();
+        Bundle bundle = message.getData();
+        Assert.assertEquals(message.what, HostApduService.MSG_COMMAND_APDU);
+        Assert.assertTrue(bundle.containsKey(HostEmulationManager.DATA_KEY));
+        Assert.assertEquals(mockAidData, bundle.getByteArray(HostEmulationManager.DATA_KEY));
+        Assert.assertEquals(mHostEmulationManager.getLocalMessenger(), message.replyTo);
+    }
+
+    @Test
+    public void testOnHostEmulationData_stateW4Select_noDefaultService_noBoundActiveService() {
+        when(mContext.bindServiceAsUser(any(), any(), anyInt(), any())).thenReturn(true);
+        byte[] mockAidData = createSelectAidData(MOCK_AID);
+        mHostEmulationManager.mState = HostEmulationManager.STATE_W4_SELECT;
+        ApduServiceInfo apduServiceInfo = mock(ApduServiceInfo.class);
+        RegisteredAidCache.AidResolveInfo aidResolveInfo = mRegisteredAidCache.new AidResolveInfo();
+        aidResolveInfo.services = new ArrayList<>();
+        aidResolveInfo.services.add(apduServiceInfo);
+        aidResolveInfo.category = CardEmulation.CATEGORY_PAYMENT;
+        when(apduServiceInfo.requiresUnlock()).thenReturn(false);
+        when(apduServiceInfo.requiresScreenOn()).thenReturn(false);
+        when(apduServiceInfo.isOnHost()).thenReturn(false);
+        when(apduServiceInfo.getComponent()).thenReturn(WALLET_PAYMENT_SERVICE);
+        when(apduServiceInfo.getUid()).thenReturn(USER_ID);
+        when(mNfcService.isSecureNfcEnabled()).thenReturn(false);
+        when(mKeyguardManager.isKeyguardLocked()).thenReturn(false);
+        when(mPowerManager.isScreenOn()).thenReturn(true);
+        when(mRegisteredAidCache.resolveAid(eq(MOCK_AID))).thenReturn(aidResolveInfo);
+        mHostEmulationManager.mActiveServiceName = WALLET_PAYMENT_SERVICE;
+        mHostEmulationManager.mPaymentServiceBound = false;
+        mHostEmulationManager.mServiceBound = false;
+
+        mHostEmulationManager.onHostEmulationData(mockAidData);
+
+        Assert.assertEquals(HostEmulationManager.STATE_W4_SERVICE,
+                mHostEmulationManager.getState());
+        Assert.assertEquals(mockAidData, mHostEmulationManager.mSelectApdu);
+        verify(apduServiceInfo).getUid();
+        verify(mStatsUtils).setCardEmulationEventCategory(eq(CardEmulation.CATEGORY_PAYMENT));
+        verify(mStatsUtils).setCardEmulationEventUid(eq(USER_ID));
+        verify(mStatsUtils).notifyCardEmulationEventWaitingForResponse();
+        verify(mRegisteredAidCache).resolveAid(eq(MOCK_AID));
+        verify(mContext).getSystemService(eq(PowerManager.class));
+        verify(mContext).getSystemService(eq(KeyguardManager.class));
+        verify(mContext).bindServiceAsUser(mIntentArgumentCaptor.capture(),
+                mServiceConnectionArgumentCaptor.capture(),
+                eq(Context.BIND_AUTO_CREATE | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS),
+                eq(USER_HANDLE));
+        Intent intent = mIntentArgumentCaptor.getValue();
+        Assert.assertEquals(intent.getAction(), HostApduService.SERVICE_INTERFACE);
+        Assert.assertEquals(intent.getComponent(), WALLET_PAYMENT_SERVICE);
+        Assert.assertEquals(mHostEmulationManager.getServiceConnection(),
+                mServiceConnectionArgumentCaptor.getValue());
+        Assert.assertTrue(mHostEmulationManager.mServiceBound);
+        Assert.assertEquals(USER_ID, mHostEmulationManager.mServiceUserId);
+        verifyNoMoreInteractions(mContext);
+    }
+
+    @Test
+    public void testOnHostEmulationData_stateW4Select_noSelectAid() {
+        mHostEmulationManager.mState = HostEmulationManager.STATE_W4_SELECT;
+        mHostEmulationManager.mPaymentServiceBound = true;
+        mHostEmulationManager.mPaymentServiceName = WALLET_PAYMENT_SERVICE;
+        mHostEmulationManager.mPaymentService = mMessanger;
+
+        mHostEmulationManager.onHostEmulationData(null);
+
+        verify(mContext).getSystemService(eq(PowerManager.class));
+        verify(mContext).getSystemService(eq(KeyguardManager.class));
+        verify(mNfcService).sendData(eq(HostEmulationManager.UNKNOWN_ERROR));
+        verifyNoMoreInteractions(mNfcService);
+        verifyNoMoreInteractions(mContext);
+    }
+
+    @Test
+    public void testOnHostEmulationData_stateW4Service() {
+        mHostEmulationManager.mState = HostEmulationManager.STATE_W4_SERVICE;
+
+        mHostEmulationManager.onHostEmulationData(null);
+
+        verify(mContext).getSystemService(eq(PowerManager.class));
+        verify(mContext).getSystemService(eq(KeyguardManager.class));
+        verifyNoMoreInteractions(mNfcService);
+        verifyNoMoreInteractions(mContext);
+    }
+
+    @Test
+    public void testOnHostEmulationData_stateXfer_nullAid_activeService() throws RemoteException {
+        byte[] data = new byte[3];
+        mHostEmulationManager.mState = HostEmulationManager.STATE_XFER;
+        mHostEmulationManager.mActiveServiceName = WALLET_PAYMENT_SERVICE;
+        mHostEmulationManager.mActiveService = mMessanger;
+
+        mHostEmulationManager.onHostEmulationData(data);
+
+        verify(mContext).getSystemService(eq(PowerManager.class));
+        verify(mContext).getSystemService(eq(KeyguardManager.class));
+        verify(mMessanger).send(mMessageArgumentCaptor.capture());
+        Message message = mMessageArgumentCaptor.getValue();
+        Bundle bundle = message.getData();
+        Assert.assertEquals(message.what, HostApduService.MSG_COMMAND_APDU);
+        Assert.assertTrue(bundle.containsKey(HostEmulationManager.DATA_KEY));
+        Assert.assertEquals(data, bundle.getByteArray(HostEmulationManager.DATA_KEY));
+        Assert.assertEquals(mHostEmulationManager.getLocalMessenger(), message.replyTo);
+        verifyNoMoreInteractions(mNfcService);
+        verifyNoMoreInteractions(mContext);
+    }
+
+    @Test
+    public void testOnHostEmulationData_stateXfer_selectAid_activeService() throws RemoteException {
+        byte[] mockAidData = createSelectAidData(MOCK_AID);
+        IBinder binder = mock(IBinder.class);
+        mHostEmulationManager.mState = HostEmulationManager.STATE_XFER;
+        ApduServiceInfo apduServiceInfo = mock(ApduServiceInfo.class);
+        RegisteredAidCache.AidResolveInfo aidResolveInfo = mRegisteredAidCache.new AidResolveInfo();
+        aidResolveInfo.services = new ArrayList<>();
+        aidResolveInfo.services.add(apduServiceInfo);
+        aidResolveInfo.category = CardEmulation.CATEGORY_PAYMENT;
+        when(apduServiceInfo.requiresUnlock()).thenReturn(false);
+        when(apduServiceInfo.requiresScreenOn()).thenReturn(false);
+        when(apduServiceInfo.isOnHost()).thenReturn(false);
+        when(apduServiceInfo.getComponent()).thenReturn(WALLET_PAYMENT_SERVICE);
+        when(mNfcService.isSecureNfcEnabled()).thenReturn(false);
+        when(mKeyguardManager.isKeyguardLocked()).thenReturn(false);
+        when(mPowerManager.isScreenOn()).thenReturn(true);
+        when(mRegisteredAidCache.resolveAid(eq(MOCK_AID))).thenReturn(aidResolveInfo);
+        mHostEmulationManager.mActiveServiceName = WALLET_PAYMENT_SERVICE;
+        mHostEmulationManager.mActiveService = mMessanger;
+        when(mMessanger.getBinder()).thenReturn(binder);
+        mHostEmulationManager.mPaymentServiceBound = true;
+        mHostEmulationManager.mPaymentServiceName = WALLET_PAYMENT_SERVICE;
+        mHostEmulationManager.mPaymentService = mMessanger;
+
+        mHostEmulationManager.onHostEmulationData(mockAidData);
+
+        Assert.assertEquals(HostEmulationManager.STATE_XFER, mHostEmulationManager.getState());
+        verify(mContext).getSystemService(eq(PowerManager.class));
+        verify(mContext).getSystemService(eq(KeyguardManager.class));
+        verify(mMessanger).send(mMessageArgumentCaptor.capture());
+        Message message = mMessageArgumentCaptor.getValue();
+        Bundle bundle = message.getData();
+        Assert.assertEquals(message.what, HostApduService.MSG_COMMAND_APDU);
+        Assert.assertTrue(bundle.containsKey(HostEmulationManager.DATA_KEY));
+        Assert.assertEquals(mockAidData, bundle.getByteArray(HostEmulationManager.DATA_KEY));
+        Assert.assertEquals(mHostEmulationManager.getLocalMessenger(), message.replyTo);
+        verifyNoMoreInteractions(mContext);
+    }
+
+    @Test
+    public void testOnHostEmulationData_stateXfer_selectAid_noActiveService() throws RemoteException {
+        when(mContext.bindServiceAsUser(any(), any(), anyInt(), any())).thenReturn(true);
+        byte[] mockAidData = createSelectAidData(MOCK_AID);
+        mHostEmulationManager.mState = HostEmulationManager.STATE_XFER;
+        ApduServiceInfo apduServiceInfo = mock(ApduServiceInfo.class);
+        RegisteredAidCache.AidResolveInfo aidResolveInfo = mRegisteredAidCache.new AidResolveInfo();
+        aidResolveInfo.services = new ArrayList<>();
+        aidResolveInfo.services.add(apduServiceInfo);
+        aidResolveInfo.category = CardEmulation.CATEGORY_PAYMENT;
+        when(apduServiceInfo.requiresUnlock()).thenReturn(false);
+        when(apduServiceInfo.requiresScreenOn()).thenReturn(false);
+        when(apduServiceInfo.isOnHost()).thenReturn(false);
+        when(apduServiceInfo.getComponent()).thenReturn(WALLET_PAYMENT_SERVICE);
+        when(apduServiceInfo.getUid()).thenReturn(USER_ID);
+        when(mNfcService.isSecureNfcEnabled()).thenReturn(false);
+        when(mKeyguardManager.isKeyguardLocked()).thenReturn(false);
+        when(mPowerManager.isScreenOn()).thenReturn(true);
+        when(mRegisteredAidCache.resolveAid(eq(MOCK_AID))).thenReturn(aidResolveInfo);
+        mHostEmulationManager.mActiveServiceName = WALLET_PAYMENT_SERVICE;
+        mHostEmulationManager.mPaymentServiceBound = false;
+        mHostEmulationManager.mServiceBound = false;
+
+        mHostEmulationManager.onHostEmulationData(mockAidData);
+
+        Assert.assertEquals(HostEmulationManager.STATE_W4_SERVICE,
+                mHostEmulationManager.getState());
+        verify(mContext).getSystemService(eq(PowerManager.class));
+        verify(mContext).getSystemService(eq(KeyguardManager.class));
+        verify(mContext).bindServiceAsUser(mIntentArgumentCaptor.capture(),
+                mServiceConnectionArgumentCaptor.capture(),
+                eq(Context.BIND_AUTO_CREATE | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS),
+                eq(USER_HANDLE));
+        Intent intent = mIntentArgumentCaptor.getValue();
+        Assert.assertEquals(intent.getAction(), HostApduService.SERVICE_INTERFACE);
+        Assert.assertEquals(intent.getComponent(), WALLET_PAYMENT_SERVICE);
+        Assert.assertEquals(mHostEmulationManager.getServiceConnection(),
+                mServiceConnectionArgumentCaptor.getValue());
+        Assert.assertTrue(mHostEmulationManager.mServiceBound);
+        Assert.assertEquals(USER_ID, mHostEmulationManager.mServiceUserId);
+        verifyNoMoreInteractions(mContext);
+    }
+
+    @Test
+    public void testOnHostEmulationDeactivated_activeService_enableObserveModeAfterTransaction()
+            throws RemoteException {
+        mHostEmulationManager.mActiveService = mMessanger;
+        mHostEmulationManager.mServiceBound = true;
+        mHostEmulationManager.mServiceUserId = USER_ID;
+        mHostEmulationManager.mServiceName = WALLET_PAYMENT_SERVICE;
+        mHostEmulationManager.mEnableObserveModeAfterTransaction = true;
+
+        mHostEmulationManager.onHostEmulationDeactivated();
+
+        Assert.assertNull(mHostEmulationManager.mActiveService);
+        Assert.assertNull(mHostEmulationManager.mActiveServiceName);
+        Assert.assertNull(mHostEmulationManager.mServiceName);
+        Assert.assertNull(mHostEmulationManager.mService);
+        Assert.assertNull(mHostEmulationManager.mPendingPollingLoopFrames);
+        Assert.assertEquals(-1, mHostEmulationManager.mActiveServiceUserId);
+        Assert.assertEquals(-1, mHostEmulationManager.mServiceUserId);
+        Assert.assertEquals(HostEmulationManager.STATE_IDLE, mHostEmulationManager.getState());
+        Assert.assertFalse(mHostEmulationManager.mEnableObserveModeAfterTransaction);
+        Assert.assertFalse(mHostEmulationManager.mServiceBound);
+        verify(mNfcAdapter).setObserveModeEnabled(eq(true));
+        verify(mMessanger).send(mMessageArgumentCaptor.capture());
+        Message message = mMessageArgumentCaptor.getValue();
+        Assert.assertEquals(message.what, HostApduService.MSG_DEACTIVATED);
+        Assert.assertEquals(message.arg1, HostApduService.DEACTIVATION_LINK_LOSS);
+        verify(mContext).getSystemService(eq(PowerManager.class));
+        verify(mContext).getSystemService(eq(KeyguardManager.class));
+        verify(mContext).unbindService(mServiceConnectionArgumentCaptor.capture());
+        verifyNoMoreInteractions(mMessanger);
+        Assert.assertEquals(mHostEmulationManager.getServiceConnection(),
+                mServiceConnectionArgumentCaptor.getValue());
+        verifyNoMoreInteractions(mContext);
+        verify(mStatsUtils).logCardEmulationDeactivatedEvent();
+    }
+
+    @Test
+    public void testOnHostEmulationDeactivated_noActiveService() throws RemoteException {
+        mHostEmulationManager.mActiveService = null;
+        mHostEmulationManager.mServiceBound = false;
+        mHostEmulationManager.mServiceUserId = USER_ID;
+        mHostEmulationManager.mEnableObserveModeAfterTransaction = false;
+
+        mHostEmulationManager.onHostEmulationDeactivated();
+
+        Assert.assertNull(mHostEmulationManager.mActiveService);
+        Assert.assertNull(mHostEmulationManager.mActiveServiceName);
+        Assert.assertNull(mHostEmulationManager.mServiceName);
+        Assert.assertNull(mHostEmulationManager.mService);
+        Assert.assertNull(mHostEmulationManager.mPendingPollingLoopFrames);
+        Assert.assertEquals(-1, mHostEmulationManager.mActiveServiceUserId);
+        Assert.assertEquals(-1, mHostEmulationManager.mServiceUserId);
+        Assert.assertEquals(HostEmulationManager.STATE_IDLE, mHostEmulationManager.getState());
+        Assert.assertFalse(mHostEmulationManager.mEnableObserveModeAfterTransaction);
+        Assert.assertFalse(mHostEmulationManager.mServiceBound);
+        verify(mContext).getSystemService(eq(PowerManager.class));
+        verify(mContext).getSystemService(eq(KeyguardManager.class));
+        verifyZeroInteractions(mMessanger);
+        verifyNoMoreInteractions(mMessanger);
+        verifyNoMoreInteractions(mContext);
+        verify(mStatsUtils).logCardEmulationDeactivatedEvent();
+    }
+
+    @Test
+    public void testOnOffHostAidSelected_noActiveService_stateXfer() {
+        mHostEmulationManager.mActiveService = null;
+        mHostEmulationManager.mServiceBound = false;
+        mHostEmulationManager.mState = HostEmulationManager.STATE_XFER;
+
+        mHostEmulationManager.onOffHostAidSelected();
+
+        Assert.assertNull(mHostEmulationManager.mActiveService);
+        Assert.assertNull(mHostEmulationManager.mActiveServiceName);
+        Assert.assertEquals(-1, mHostEmulationManager.mActiveServiceUserId);
+        Assert.assertFalse(mHostEmulationManager.mServiceBound);
+        verify(mContext).getSystemService(eq(PowerManager.class));
+        verify(mContext).getSystemService(eq(KeyguardManager.class));
+        verify(mContext).sendBroadcastAsUser(mIntentArgumentCaptor.capture(), eq(UserHandle.ALL));
+        Assert.assertEquals(HostEmulationManager.STATE_W4_SELECT,
+                mHostEmulationManager.getState());
+        Intent intent = mIntentArgumentCaptor.getValue();
+        Assert.assertEquals(TapAgainDialog.ACTION_CLOSE, intent.getAction());
+        Assert.assertEquals(HostEmulationManager.NFC_PACKAGE, intent.getPackage());
+        verifyNoMoreInteractions(mContext);
+    }
+
+    @Test
+    public void testOnOffHostAidSelected_activeServiceBound_stateXfer() throws RemoteException {
+        mHostEmulationManager.mActiveService = mMessanger;
+        mHostEmulationManager.mServiceBound = true;
+        mHostEmulationManager.mState = HostEmulationManager.STATE_XFER;
+
+        mHostEmulationManager.onOffHostAidSelected();
+
+        Assert.assertNull(mHostEmulationManager.mActiveService);
+        Assert.assertNull(mHostEmulationManager.mActiveServiceName);
+        Assert.assertEquals(-1, mHostEmulationManager.mActiveServiceUserId);
+        Assert.assertFalse(mHostEmulationManager.mServiceBound);
+        verify(mContext).unbindService(mServiceConnectionArgumentCaptor.capture());
+        verify(mContext).getSystemService(eq(PowerManager.class));
+        verify(mContext).getSystemService(eq(KeyguardManager.class));
+        verify(mContext).sendBroadcastAsUser(mIntentArgumentCaptor.capture(), eq(UserHandle.ALL));
+        Assert.assertEquals(HostEmulationManager.STATE_W4_SELECT,
+                mHostEmulationManager.getState());
+        Intent intent = mIntentArgumentCaptor.getValue();
+        Assert.assertEquals(TapAgainDialog.ACTION_CLOSE, intent.getAction());
+        Assert.assertEquals(HostEmulationManager.NFC_PACKAGE, intent.getPackage());
+        verify(mMessanger).send(mMessageArgumentCaptor.capture());
+        Message message = mMessageArgumentCaptor.getValue();
+        Assert.assertEquals(message.what, HostApduService.MSG_DEACTIVATED);
+        Assert.assertEquals(message.arg1, HostApduService.DEACTIVATION_DESELECTED);
+        verifyNoMoreInteractions(mContext);
+    }
+
+    @Test
+    public void testOnOffHostAidSelected_activeServiceBound_stateNonXfer() throws RemoteException {
+        mHostEmulationManager.mActiveService = mMessanger;
+        mHostEmulationManager.mServiceBound = true;
+        mHostEmulationManager.mState = HostEmulationManager.STATE_IDLE;
+
+        mHostEmulationManager.onOffHostAidSelected();
+
+        Assert.assertNull(mHostEmulationManager.mActiveService);
+        Assert.assertNull(mHostEmulationManager.mActiveServiceName);
+        Assert.assertEquals(-1, mHostEmulationManager.mActiveServiceUserId);
+        Assert.assertFalse(mHostEmulationManager.mServiceBound);
+        verify(mContext).unbindService(mServiceConnectionArgumentCaptor.capture());
+        verify(mContext).getSystemService(eq(PowerManager.class));
+        verify(mContext).getSystemService(eq(KeyguardManager.class));
+        verify(mContext).sendBroadcastAsUser(mIntentArgumentCaptor.capture(), eq(UserHandle.ALL));
+        Assert.assertEquals(HostEmulationManager.STATE_W4_SELECT,
+                mHostEmulationManager.getState());
+        Intent intent = mIntentArgumentCaptor.getValue();
+        Assert.assertEquals(TapAgainDialog.ACTION_CLOSE, intent.getAction());
+        Assert.assertEquals(HostEmulationManager.NFC_PACKAGE, intent.getPackage());
+        verifyZeroInteractions(mMessanger);
+        verifyNoMoreInteractions(mContext);
+    }
+
+    @Test
+    public void testServiceConnectionOnServiceConnected_stateSelectW4_selectApdu()
+            throws RemoteException {
+        IBinder service = mock(IBinder.class);
+        mHostEmulationManager.mState = HostEmulationManager.STATE_W4_SELECT;
+        mHostEmulationManager.mSelectApdu = new byte[3];
+
+        mHostEmulationManager.getServiceConnection().onServiceConnected(WALLET_PAYMENT_SERVICE,
+                service);
+
+        Assert.assertEquals(mHostEmulationManager.mServiceName, WALLET_PAYMENT_SERVICE);
+        Assert.assertNotNull(mHostEmulationManager.mService);
+        Assert.assertTrue(mHostEmulationManager.mServiceBound);
+        verify(mStatsUtils).notifyCardEmulationEventServiceBound();
+        Assert.assertEquals(HostEmulationManager.STATE_XFER, mHostEmulationManager.getState());
+        Assert.assertNull(mHostEmulationManager.mSelectApdu);
+        verify(service).transact(eq(1), any(), eq(null), eq(1));
+    }
+
+    @Test
+    public void testServiceConnectionOnServiceConnected_stateSelectW4_pollingLoopFrames()
+            throws RemoteException {
+        IBinder service = mock(IBinder.class);
+        mHostEmulationManager.mState = HostEmulationManager.STATE_W4_SELECT;
+        mHostEmulationManager.mSelectApdu = null;
+        mHostEmulationManager.mPendingPollingLoopFrames = new ArrayList<>(List.of());
+
+        mHostEmulationManager.getServiceConnection().onServiceConnected(WALLET_PAYMENT_SERVICE,
+                service);
+
+        Assert.assertEquals(mHostEmulationManager.mServiceName, WALLET_PAYMENT_SERVICE);
+        Assert.assertNotNull(mHostEmulationManager.mService);
+        Assert.assertTrue(mHostEmulationManager.mServiceBound);
+        Assert.assertEquals(HostEmulationManager.STATE_XFER, mHostEmulationManager.getState());
+        Assert.assertNull(mHostEmulationManager.mPendingPollingLoopFrames);
+        verify(service).transact(eq(1), any(), eq(null), eq(1));
+    }
+
+    @Test
+    public void testServiceConnectionOnServiceConnected_stateIdle() {
+        IBinder service = mock(IBinder.class);
+        mHostEmulationManager.mState = HostEmulationManager.STATE_IDLE;
+
+        mHostEmulationManager.getServiceConnection().onServiceConnected(WALLET_PAYMENT_SERVICE,
+                service);
+
+        verifyZeroInteractions(service);
+    }
+
+    @Test
+    public void testServiceConnectionOnServiceDisconnected() {
+        mHostEmulationManager.mService = mMessanger;
+        mHostEmulationManager.mServiceBound = true;
+        mHostEmulationManager.mServiceName = WALLET_PAYMENT_SERVICE;
+
+        mHostEmulationManager.getServiceConnection().onServiceDisconnected(WALLET_PAYMENT_SERVICE);
+
+        Assert.assertNull(mHostEmulationManager.mService);
+        Assert.assertFalse(mHostEmulationManager.mServiceBound);
+        Assert.assertNull(mHostEmulationManager.mServiceName);
+    }
+
+    @Test
+    public void testPaymentServiceConnectionOnServiceConnected() {
+        IBinder service = mock(IBinder.class);
+        mHostEmulationManager.mLastBoundPaymentServiceName = WALLET_PAYMENT_SERVICE;
+
+        mHostEmulationManager.getPaymentConnection().onServiceConnected(WALLET_PAYMENT_SERVICE,
+                service);
+
+        Assert.assertNotNull(mHostEmulationManager.mPaymentServiceName);
+        Assert.assertEquals(WALLET_PAYMENT_SERVICE, mHostEmulationManager.mPaymentServiceName);
+    }
+
+    @Test
+    public void testPaymentServiceConnectionOnServiceDisconnected() {
+        mHostEmulationManager.mPaymentService = mMessanger;
+        mHostEmulationManager.mPaymentServiceBound = true;
+        mHostEmulationManager.mPaymentServiceName = WALLET_PAYMENT_SERVICE;
+
+        mHostEmulationManager.getPaymentConnection().onServiceDisconnected(WALLET_PAYMENT_SERVICE);
+
+        Assert.assertNull(mHostEmulationManager.mPaymentService);
+        Assert.assertNull(mHostEmulationManager.mPaymentServiceName);
+    }
+
+    @Test
+    public void testFindSelectAid_properAid() {
+        final byte[] aidData = createSelectAidData(MOCK_AID);
+
+        String aidString = mHostEmulationManager.findSelectAid(aidData);
+
+        Assert.assertEquals(MOCK_AID, aidString);
+    }
+
+    @Test
+    public void testFindSelectAid_nullData() {
+        String aidString = mHostEmulationManager.findSelectAid(null);
+
+        Assert.assertNull(aidString);
+    }
+
+    @Test
+    public void testFindSelectAid_shortLength() {
+        final byte[] aidData = new byte[1];
+
+        String aidString = mHostEmulationManager.findSelectAid(aidData);
+
+        Assert.assertNull(aidString);
+    }
+
+    @Test
+    public void testFindSelectAid_longAidLength() {
+        final byte[] aidData = createSelectAidData(MOCK_AID);
+        aidData[4] = (byte)(HostEmulationManager.SELECT_APDU_HDR_LENGTH
+                + HexFormat.of().parseHex(MOCK_AID).length + 1);
+
+        String aidString = mHostEmulationManager.findSelectAid(aidData);
+
+        Assert.assertNull(aidString);
+    }
+
+    private void verifyTapAgainLaunched(ApduServiceInfo service, String category) {
+        verify(mContext).getPackageName();
+        verify(mContext).startActivityAsUser(mIntentArgumentCaptor.capture(), eq(USER_HANDLE));
+        Intent intent = mIntentArgumentCaptor.getValue();
+        Assert.assertEquals(category, intent.getStringExtra(TapAgainDialog.EXTRA_CATEGORY));
+        Assert.assertEquals(service, intent.getParcelableExtra(TapAgainDialog.EXTRA_APDU_SERVICE));
+        int flags = Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK;
+        Assert.assertEquals(flags, intent.getFlags());
+        Assert.assertEquals(TapAgainDialog.class.getCanonicalName(),
+                intent.getComponent().getClassName());
+    }
+
+    private void verifyResolverLaunched(ArrayList<ApduServiceInfo> services,
+                                        ComponentName failedComponent, String category) {
+        verify(mContext).getPackageName();
+        verify(mContext).startActivityAsUser(mIntentArgumentCaptor.capture(), eq(UserHandle.CURRENT));
+        Intent intent = mIntentArgumentCaptor.getValue();
+        Assert.assertEquals(category, intent.getStringExtra(AppChooserActivity.EXTRA_CATEGORY));
+        Assert.assertEquals(services,
+                intent.getParcelableArrayListExtra(AppChooserActivity.EXTRA_APDU_SERVICES));
+        if (failedComponent != null) {
+            Assert.assertEquals(failedComponent,
+                    intent.getParcelableExtra(AppChooserActivity.EXTRA_FAILED_COMPONENT));
+        }
+        int flags = Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK;
+        Assert.assertEquals(flags, intent.getFlags());
+        Assert.assertEquals(AppChooserActivity.class.getCanonicalName(),
+                intent.getComponent().getClassName());
+    }
+
+    private static byte[] createSelectAidData(String aid) {
+        byte[] aidStringData = HexFormat.of().parseHex(aid);
+        byte[] aidData = new byte[HostEmulationManager.SELECT_APDU_HDR_LENGTH
+                + aidStringData.length];
+        aidData[0] = 0x00;
+        aidData[1] = HostEmulationManager.INSTR_SELECT;
+        aidData[2] = 0x04;
+        aidData[3] = 0x00;
+        aidData[4] = (byte)aidStringData.length;
+        System.arraycopy(aidStringData, 0, aidData, HostEmulationManager.SELECT_APDU_HDR_LENGTH,
+                aidData.length - HostEmulationManager.SELECT_APDU_HDR_LENGTH);
+        return aidData;
+    }
+
+}
diff --git a/tests/unit/src/com/android/nfc/cardemulation/HostNfcFEmulationManagerTest.java b/tests/unit/src/com/android/nfc/cardemulation/HostNfcFEmulationManagerTest.java
new file mode 100644
index 0000000..7d29c74
--- /dev/null
+++ b/tests/unit/src/com/android/nfc/cardemulation/HostNfcFEmulationManagerTest.java
@@ -0,0 +1,541 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.nfc.cardemulation;
+
+import static com.android.nfc.cardemulation.HostNfcFEmulationManager.STATE_IDLE;
+import static com.android.nfc.cardemulation.HostNfcFEmulationManager.STATE_W4_SERVICE;
+import static com.android.nfc.cardemulation.HostNfcFEmulationManager.STATE_XFER;
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.nfc.cardemulation.HostNfcFService;
+import android.nfc.cardemulation.NfcFServiceInfo;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.UserHandle;
+import android.util.proto.ProtoOutputStream;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.nfc.NfcService;
+
+import java.io.PrintWriter;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+
+@RunWith(AndroidJUnit4.class)
+public class HostNfcFEmulationManagerTest {
+
+  private HostNfcFEmulationManager manager;
+
+  @Mock
+  private RegisteredT3tIdentifiersCache mockIdentifiersCache;
+  @Mock
+  private Messenger mockActiveService;
+  @Mock
+  private Context mockContext;
+  @Mock
+  private IBinder mockIBinder;
+  @Mock
+  private PrintWriter mockPrintWriter;
+  @Mock
+  private ProtoOutputStream mockProto;
+  @Mock
+  private NfcFServiceInfo mockResolvedService;
+
+  @Captor
+  ArgumentCaptor<Message> msgCaptor;
+  @Captor
+  ArgumentCaptor<Intent> intentCaptor;
+  @Captor
+  ArgumentCaptor<UserHandle> userHandleCaptor;
+  @Captor
+  ArgumentCaptor<ServiceConnection> serviceConnectionCaptor;
+  @Captor
+  ArgumentCaptor<Integer> flagsCaptor;
+
+  private static final ComponentName testService = new ComponentName(
+      "com.android.test.walletroleholder",
+      "com.android.test.walletroleholder.WalletRoleHolderApduService");
+  private static final byte[] DATA = new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
+  private static final int USER_ID = 2;
+
+  @Before
+  public void setUp() {
+    MockitoAnnotations.initMocks(this);
+    when(mockIdentifiersCache.resolveNfcid2(anyString())).thenReturn(null);
+    when(mockActiveService.getBinder()).thenReturn(mockIBinder);
+    when(mockIdentifiersCache.resolveNfcid2(anyString())).thenReturn(mockResolvedService);
+    when(mockResolvedService.getUid()).thenReturn(USER_ID);
+    doNothing().when(mockPrintWriter).println(anyString());
+    doNothing().when(mockProto).write(anyLong(), anyString());
+    doNothing().when(mockProto).end(anyLong());
+  }
+
+  @Test
+  public void testConstructor() {
+    Looper.prepare();
+
+    manager = new HostNfcFEmulationManager(mockContext, mockIdentifiersCache);
+
+    assertThat(manager.mContext).isEqualTo(mockContext);
+    assertThat(manager.mLock).isNotNull();
+    assertThat(manager.mEnabledFgServiceName).isNull();
+    assertThat(manager.mT3tIdentifiersCache).isEqualTo(mockIdentifiersCache);
+    assertThat(manager.mState).isEqualTo(STATE_IDLE);
+  }
+
+  @Test
+  public void testOnEnabledForegroundNfcFServiceChangedWithNonNullService() throws Exception {
+    Looper.prepare();
+    manager = new HostNfcFEmulationManager(mockContext, mockIdentifiersCache);
+
+    manager.onEnabledForegroundNfcFServiceChanged(USER_ID, testService);
+
+    assertThat(manager.mEnabledFgServiceUserId).isEqualTo(USER_ID);
+    assertThat(manager.mEnabledFgServiceName).isEqualTo(testService);
+  }
+
+  @Test
+  public void testOnEnabledForegroundNfcFServiceChangedWithNullService() throws Exception {
+    Looper.prepare();
+    manager = new HostNfcFEmulationManager(mockContext, mockIdentifiersCache);
+    manager.mActiveService = mockActiveService;
+    manager.mServiceBound = true;
+
+    manager.onEnabledForegroundNfcFServiceChanged(USER_ID, /* service = */ null);
+
+    verify(mockActiveService).send(any(Message.class));
+    verify(mockContext).unbindService(any(ServiceConnection.class));
+    assertThat(manager.mEnabledFgServiceUserId).isEqualTo(USER_ID);
+    assertThat(manager.mEnabledFgServiceName).isNull();
+    assertThat(manager.mServiceBound).isFalse();
+    assertThat(manager.mService).isNull();
+    assertThat(manager.mServiceName).isNull();
+    assertThat(manager.mServiceUserId).isEqualTo(-1);
+  }
+
+  @Test
+  public void testOnHostEmulationDataWithNullDataAndNoActiveService_ReturnsEarly()
+      throws Exception {
+    Looper.prepare();
+    manager = new HostNfcFEmulationManager(mockContext, mockIdentifiersCache);
+    manager.mActiveServiceName = null;
+
+    manager.onHostEmulationData(/* data = */ null);
+
+    assertThat(manager.mState).isEqualTo(STATE_IDLE);
+  }
+
+  @Test
+  public void testOnHostEmulationDataWithDisabledResolvedServiceFromActiveService_ReturnsEarly()
+      throws Exception {
+    Looper.prepare();
+    manager = new HostNfcFEmulationManager(mockContext, mockIdentifiersCache);
+    manager.mActiveServiceName = testService;
+    manager.mEnabledFgServiceName = null;
+
+    manager.onHostEmulationData(/* data = */ null);
+
+    assertThat(manager.mState).isEqualTo(STATE_IDLE);
+  }
+
+  @Test
+  public void testOnHostEmulationDataWithEnabledResolvedServiceFromCache_BindToService()
+      throws Exception {
+    Looper.prepare();
+    manager = new HostNfcFEmulationManager(mockContext, mockIdentifiersCache);
+    manager.mActiveServiceName = testService;
+    manager.mEnabledFgServiceName = testService;
+    manager.mServiceBound = true;
+    manager.mServiceName = testService;
+    manager.mServiceUserId = 0;
+    manager.mService = mockActiveService;
+
+    manager.onHostEmulationData(DATA);
+
+    verify(mockActiveService).send(any(Message.class));
+    assertThat(manager.mState).isEqualTo(STATE_XFER);
+  }
+
+  @Test
+  public void testOnHostEmulationDataWithValidExistingService_BindToService() throws Exception {
+    Looper.prepare();
+    manager = new HostNfcFEmulationManager(mockContext, mockIdentifiersCache);
+    manager.mActiveServiceName = testService;
+    manager.mEnabledFgServiceName = testService;
+    manager.mEnabledFgServiceUserId = USER_ID;
+    manager.mServiceBound = true;
+    manager.mServiceName = testService;
+    manager.mServiceUserId = USER_ID;
+    manager.mService = mockActiveService;
+
+    manager.onHostEmulationData(/* data = */ null);
+
+    verify(mockActiveService).send(any(Message.class));
+    assertThat(manager.mState).isEqualTo(STATE_XFER);
+  }
+
+  @Test
+  public void testOnHostEmulationDataWithInvalidExistingService_WaitingForNewService()
+      throws Exception {
+    Looper.prepare();
+    manager = new HostNfcFEmulationManager(mockContext, mockIdentifiersCache);
+    manager.mActiveServiceName = testService;
+    manager.mEnabledFgServiceName = testService;
+
+    manager.onHostEmulationData(DATA);
+
+    assertThat(manager.mState).isEqualTo(STATE_W4_SERVICE);
+    assertThat(manager.mPendingPacket).isEqualTo(DATA);
+  }
+
+  @Test
+  public void testOnHostEmulationDataWithXFERState_SendRegularPacketData() throws Exception {
+    Looper.prepare();
+    manager = new HostNfcFEmulationManager(mockContext, mockIdentifiersCache);
+    manager.mActiveServiceName = testService;
+    manager.mEnabledFgServiceName = testService;
+    manager.mState = STATE_XFER;
+    manager.mActiveService = mockActiveService;
+
+    manager.onHostEmulationData(DATA);
+
+    verify(mockActiveService).send(any(Message.class));
+  }
+
+  @Test
+  public void testOnHostEmulationDataWithW4ServiceState_DoNothing() throws Exception {
+    Looper.prepare();
+    manager = new HostNfcFEmulationManager(mockContext, mockIdentifiersCache);
+    manager.mActiveServiceName = testService;
+    manager.mEnabledFgServiceName = testService;
+    manager.mState = STATE_W4_SERVICE;
+
+    manager.onHostEmulationData(DATA);
+
+    assertThat(manager.mState).isEqualTo(STATE_W4_SERVICE);
+  }
+
+  @Test
+  public void testOnHostEmulationDeactivated() throws Exception {
+    Looper.prepare();
+    manager = new HostNfcFEmulationManager(mockContext, mockIdentifiersCache);
+    manager.mActiveService = mockActiveService;
+
+    manager.onHostEmulationDeactivated();
+
+    verify(mockActiveService).send(any(Message.class));
+    assertThat(manager.mActiveService).isNull();
+    assertThat(manager.mActiveServiceName).isNull();
+    assertThat(manager.mState).isEqualTo(STATE_IDLE);
+  }
+
+  @Test
+  public void testOnNfcDisabled() throws Exception {
+    Looper.prepare();
+    manager = new HostNfcFEmulationManager(mockContext, mockIdentifiersCache);
+    manager.mActiveService = mockActiveService;
+
+    manager.onNfcDisabled();
+
+    verify(mockActiveService).send(any(Message.class));
+    assertThat(manager.mEnabledFgServiceName).isNull();
+    assertThat(manager.mActiveService).isNull();
+    assertThat(manager.mActiveServiceName).isNull();
+    assertThat(manager.mState).isEqualTo(STATE_IDLE);
+  }
+
+  @Test
+  public void testOnUserSwitched() throws Exception {
+    Looper.prepare();
+    manager = new HostNfcFEmulationManager(mockContext, mockIdentifiersCache);
+    manager.mActiveService = mockActiveService;
+
+    manager.onUserSwitched();
+
+    verify(mockActiveService).send(any(Message.class));
+    assertThat(manager.mEnabledFgServiceName).isNull();
+    assertThat(manager.mActiveService).isNull();
+    assertThat(manager.mActiveServiceName).isNull();
+    assertThat(manager.mState).isEqualTo(STATE_IDLE);
+  }
+
+  @Test
+  public void testSendDataToServiceLockedWithNewService() throws Exception {
+    Looper.prepare();
+    manager = new HostNfcFEmulationManager(mockContext, mockIdentifiersCache);
+
+    manager.sendDataToServiceLocked(mockActiveService, DATA);
+
+    verify(mockActiveService).send(msgCaptor.capture());
+    Message msg = msgCaptor.getValue();
+    assertThat(msg.getTarget()).isNull();
+    assertThat(msg.what).isEqualTo(HostNfcFService.MSG_COMMAND_PACKET);
+    assertThat(msg.getData().getByteArray("data")).isEqualTo(DATA);
+  }
+
+  @Test
+  public void testSendDataToServiceLockedWithExistingService() throws Exception {
+    Looper.prepare();
+    manager = new HostNfcFEmulationManager(mockContext, mockIdentifiersCache);
+    manager.mActiveService = mockActiveService;
+
+    manager.sendDataToServiceLocked(mockActiveService, DATA);
+
+    verify(mockActiveService).send(msgCaptor.capture());
+    Message msg = msgCaptor.getValue();
+    assertThat(msg.getTarget()).isNull();
+    assertThat(msg.what).isEqualTo(HostNfcFService.MSG_COMMAND_PACKET);
+    assertThat(msg.getData().getByteArray("data")).isEqualTo(DATA);
+  }
+
+  @Test
+  public void testDeactivateToActiveServiceLockedWithNullActiveService() throws Exception {
+    Looper.prepare();
+    manager = new HostNfcFEmulationManager(mockContext, mockIdentifiersCache);
+
+    manager.sendDeactivateToActiveServiceLocked(HostNfcFService.DEACTIVATION_LINK_LOSS);
+
+    verify(mockActiveService, times(0)).send(any(Message.class));
+  }
+
+  @Test
+  public void testDeactivateToActiveServiceLockedWithNonNullActiveService() throws Exception {
+    Looper.prepare();
+    manager = new HostNfcFEmulationManager(mockContext, mockIdentifiersCache);
+    manager.mActiveService = mockActiveService;
+
+    manager.sendDeactivateToActiveServiceLocked(HostNfcFService.DEACTIVATION_LINK_LOSS);
+
+    verify(mockActiveService).send(msgCaptor.capture());
+    Message msg = msgCaptor.getValue();
+    assertThat(msg.getTarget()).isNull();
+    assertThat(msg.what).isEqualTo(HostNfcFService.MSG_DEACTIVATED);
+    assertThat(msg.arg1).isEqualTo(HostNfcFService.DEACTIVATION_LINK_LOSS);
+  }
+
+  @Test
+  public void testBindServiceWithNewService_SuccessfullyBindsToService() throws Exception {
+    Looper.prepare();
+    manager = new HostNfcFEmulationManager(mockContext, mockIdentifiersCache);
+    when(mockContext.bindServiceAsUser(any(Intent.class), any(ServiceConnection.class), anyInt(),
+        any(UserHandle.class))).thenReturn(true);
+
+    Messenger result = manager.bindServiceIfNeededLocked(USER_ID, testService);
+
+    verify(mockContext).bindServiceAsUser(intentCaptor.capture(), serviceConnectionCaptor.capture(),
+        flagsCaptor.capture(), userHandleCaptor.capture());
+    Intent bindIntent = intentCaptor.getValue();
+    assertThat(bindIntent.getAction()).isEqualTo(HostNfcFService.SERVICE_INTERFACE);
+    assertThat(bindIntent.getComponent()).isEqualTo(testService);
+    assertThat(serviceConnectionCaptor.getValue()).isNotNull();
+    assertThat(flagsCaptor.getValue()).isEqualTo(Context.BIND_AUTO_CREATE);
+    assertThat(userHandleCaptor.getValue()).isEqualTo(UserHandle.of(USER_ID));
+    assertThat(manager.mServiceUserId).isEqualTo(USER_ID);
+    assertThat(result).isNull();
+  }
+
+  @Test
+  public void testBindServiceWithNewService_FailsToBindToService() throws Exception {
+    Looper.prepare();
+    manager = new HostNfcFEmulationManager(mockContext, mockIdentifiersCache);
+    when(mockContext.bindServiceAsUser(any(Intent.class), any(ServiceConnection.class), anyInt(),
+        any(UserHandle.class))).thenReturn(false);
+
+    Messenger result = manager.bindServiceIfNeededLocked(USER_ID, testService);
+
+    assertThat(manager.mServiceUserId).isEqualTo(0);
+    assertThat(result).isNull();
+  }
+
+  @Test
+  public void testBindServiceWithExistingService_ServiceAlreadyBound() throws Exception {
+    Looper.prepare();
+    manager = new HostNfcFEmulationManager(mockContext, mockIdentifiersCache);
+    manager.mService = mockActiveService;
+    manager.mServiceBound = true;
+    manager.mServiceName = testService;
+    manager.mServiceUserId = USER_ID;
+
+    Messenger result = manager.bindServiceIfNeededLocked(USER_ID, testService);
+
+    assertThat(result).isEqualTo(mockActiveService);
+  }
+
+  @Test
+  public void testUnbindService() throws Exception {
+    Looper.prepare();
+    manager = new HostNfcFEmulationManager(mockContext, mockIdentifiersCache);
+    manager.mServiceBound = true;
+
+    manager.unbindServiceIfNeededLocked();
+
+    verify(mockContext).unbindService(any(ServiceConnection.class));
+    assertThat(manager.mServiceBound).isFalse();
+    assertThat(manager.mService).isNull();
+    assertThat(manager.mServiceName).isNull();
+    assertThat(manager.mServiceUserId).isEqualTo(-1);
+  }
+
+  @Test
+  public void testFindNfcid2WithNullData() throws Exception {
+    Looper.prepare();
+    manager = new HostNfcFEmulationManager(mockContext, mockIdentifiersCache);
+
+    String result = manager.findNfcid2(/* data = */ null);
+
+    assertThat(result).isNull();
+  }
+
+  @Test
+  public void testFindNfcid2WithNonNullData() throws Exception {
+    Looper.prepare();
+    manager = new HostNfcFEmulationManager(mockContext, mockIdentifiersCache);
+
+    String result = manager.findNfcid2(DATA);
+
+    assertThat(result).isEqualTo("0203040506070809");
+  }
+
+  @Test
+  public void testServiceConnectionOnConnected() throws Exception {
+    Looper.prepare();
+    manager = new HostNfcFEmulationManager(mockContext, mockIdentifiersCache);
+    manager.mPendingPacket = DATA;
+    manager.bindServiceIfNeededLocked(USER_ID, testService);
+    verify(mockContext).bindServiceAsUser(any(Intent.class), serviceConnectionCaptor.capture(),
+        anyInt(), any(UserHandle.class));
+    ServiceConnection connection = serviceConnectionCaptor.getValue();
+
+    connection.onServiceConnected(testService, mockIBinder);
+
+    assertThat(manager.mService).isNotNull();
+    assertThat(manager.mServiceBound).isTrue();
+    assertThat(manager.mServiceName).isEqualTo(testService);
+    assertThat(manager.mState).isEqualTo(STATE_XFER);
+    assertThat(manager.mPendingPacket).isNull();
+  }
+
+  @Test
+  public void testServiceConnectionOnDisconnected() throws Exception {
+    Looper.prepare();
+    manager = new HostNfcFEmulationManager(mockContext, mockIdentifiersCache);
+    manager.bindServiceIfNeededLocked(USER_ID, testService);
+    verify(mockContext).bindServiceAsUser(any(Intent.class), serviceConnectionCaptor.capture(),
+        anyInt(), any(UserHandle.class));
+    ServiceConnection connection = serviceConnectionCaptor.getValue();
+
+    connection.onServiceDisconnected(testService);
+
+    assertThat(manager.mService).isNull();
+    assertThat(manager.mServiceBound).isFalse();
+    assertThat(manager.mServiceName).isNull();
+  }
+
+  @Test
+  public void testMessageHandler() throws Exception {
+    Looper.prepare();
+    manager = new HostNfcFEmulationManager(mockContext, mockIdentifiersCache);
+    manager.mActiveService = mockActiveService;
+    Message msg = new Message();
+    msg.replyTo = mockActiveService;
+    msg.what = HostNfcFService.MSG_RESPONSE_PACKET;
+    Bundle dataBundle = new Bundle();
+    dataBundle.putByteArray("data", new byte[]{});
+    msg.setData(dataBundle);
+    HostNfcFEmulationManager.MessageHandler messageHandler = manager.new MessageHandler();
+
+    messageHandler.handleMessage(msg);
+  }
+
+  @Test
+  public void testBytesToString() throws Exception {
+    Looper.prepare();
+    manager = new HostNfcFEmulationManager(mockContext, mockIdentifiersCache);
+
+    String result = manager.bytesToString(DATA, /* offset = */ 0, DATA.length);
+
+    assertThat(result).isEqualTo("000102030405060708090A0B");
+  }
+
+  @Test
+  public void testDumpWithBoundService() throws Exception {
+    Looper.prepare();
+    manager = new HostNfcFEmulationManager(mockContext, mockIdentifiersCache);
+    manager.mServiceBound = true;
+
+    manager.dump(/* fd = */ null, mockPrintWriter, /* args = */ null);
+
+    verify(mockPrintWriter, times(2)).println(anyString());
+  }
+
+  @Test
+  public void testDumpWithoutBoundService() throws Exception {
+    Looper.prepare();
+    manager = new HostNfcFEmulationManager(mockContext, mockIdentifiersCache);
+
+    manager.dump(/* fd = */ null, mockPrintWriter, /* args = */ null);
+
+    verify(mockPrintWriter).println(anyString());
+  }
+
+  @Test
+  public void testDumpDebugWithBoundService() throws Exception {
+    Looper.prepare();
+    manager = new HostNfcFEmulationManager(mockContext, mockIdentifiersCache);
+    manager.mServiceBound = true;
+    manager.mServiceName = testService;
+
+    manager.dumpDebug(mockProto);
+
+    verify(mockProto, times(2)).write(anyLong(), anyString());
+    verify(mockProto, times(1)).end(anyLong());
+  }
+
+  @Test
+  public void testDumpDebugWithoutBoundService() throws Exception {
+    Looper.prepare();
+    manager = new HostNfcFEmulationManager(mockContext, mockIdentifiersCache);
+
+    manager.dumpDebug(mockProto);
+
+    verify(mockProto, times(0)).write(anyLong(), anyString());
+    verify(mockProto, times(0)).end(anyLong());
+  }
+}
\ No newline at end of file
diff --git a/tests/unit/src/com/android/nfc/cardemulation/ListAdapterTest.java b/tests/unit/src/com/android/nfc/cardemulation/ListAdapterTest.java
new file mode 100644
index 0000000..075c0dc
--- /dev/null
+++ b/tests/unit/src/com/android/nfc/cardemulation/ListAdapterTest.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.nfc.cardemulation;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.PackageManager;
+import android.nfc.cardemulation.ApduServiceInfo;
+import android.view.LayoutInflater;
+import android.view.View;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.nfc.tests.unit.R;
+
+import java.util.ArrayList;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class ListAdapterTest {
+
+  private Context context;
+  private Context mockContext;
+  private View viewInput;
+  @Mock
+  private ApduServiceInfo mockService;
+  @Mock
+  private LayoutInflater mockInflater;
+
+  @Rule
+  public final ActivityTestRule<AppChooserActivity> rule
+      = new ActivityTestRule<>(AppChooserActivity.class);
+
+  @Before
+  public void setUp() {
+    MockitoAnnotations.initMocks(this);
+    context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+
+    // Set up mocks for onView()
+    viewInput
+        = spy(LayoutInflater.from(context).inflate(R.layout.activity_layout_test, null, false));
+    AppChooserActivity.ViewHolder viewHolder = new AppChooserActivity.ViewHolder(viewInput);
+    viewHolder.text = viewInput.findViewById(R.id.applabel);
+    viewHolder.icon = viewInput.findViewById(R.id.appicon);
+    viewHolder.banner = viewInput.findViewById(R.id.banner);
+    doReturn(viewHolder).when(viewInput).getTag();
+    when(mockInflater.inflate(anyInt(), any(), anyBoolean())).thenReturn(viewInput);
+
+    // Set up mockService
+    when(mockService.getDescription()).thenReturn("");
+    when(mockService.loadIcon(any(PackageManager.class))).thenReturn(null);
+    when(mockService.loadBanner(any(PackageManager.class))).thenReturn(null);
+    when(mockService.getUid()).thenReturn(0);
+
+    mockContext = new ContextWrapper(context) {
+      @Override
+      public Object getSystemService(String name) {
+        return mockInflater;
+      }
+    };
+  }
+
+  @Test
+  public void testListAdapterConstructor() throws Throwable {
+    rule.runOnUiThread(() -> {
+      AppChooserActivity.ListAdapter adapter
+          = rule.getActivity().new ListAdapter(mockContext, getMockServiceList());
+
+      assertThat(adapter.getCount()).isEqualTo(1);
+      assertThat(adapter.getItem(0)).isNotNull();
+    });
+  }
+
+  @Test
+  public void testListAdapterGetViewWithNullConvertView() throws Throwable {
+    rule.runOnUiThread(() -> {
+      AppChooserActivity.ListAdapter adapter
+          = rule.getActivity().new ListAdapter(mockContext, getMockServiceList());
+      View viewResult = adapter.getView(0, null, null);
+
+      assertThat(viewResult).isEqualTo(viewInput);
+      AppChooserActivity.ViewHolder viewHolderResult = (AppChooserActivity.ViewHolder) viewResult.getTag();
+      assertThat(viewHolderResult).isNotNull();
+      assertThat(viewHolderResult.icon).isNotNull();
+      assertThat(viewHolderResult.text).isNotNull();
+    });
+  }
+
+  @Test
+  public void testListAdapterGetViewWithNonNullConvertView() throws Throwable {
+    rule.runOnUiThread(() -> {
+      AppChooserActivity.ListAdapter adapter
+          = rule.getActivity().new ListAdapter(mockContext, getMockServiceList());
+      View viewResult = adapter.getView(0, viewInput, null);
+
+      assertThat(viewResult).isEqualTo(viewInput);
+      AppChooserActivity.ViewHolder viewHolderResult
+          = (AppChooserActivity.ViewHolder) viewResult.getTag();
+      assertThat(viewHolderResult).isNotNull();
+      assertThat(viewHolderResult.icon).isNotNull();
+      assertThat(viewHolderResult.text).isNotNull();
+    });
+  }
+
+  private ArrayList<ApduServiceInfo> getMockServiceList() {
+    ArrayList<ApduServiceInfo> serviceList = new ArrayList<>();
+    serviceList.add(mockService);
+    return serviceList;
+  }
+}
diff --git a/tests/unit/src/com/android/nfc/cardemulation/NfcCardEmulationOccurredTest.java b/tests/unit/src/com/android/nfc/cardemulation/NfcCardEmulationOccurredTest.java
index 44d6675..110b127 100644
--- a/tests/unit/src/com/android/nfc/cardemulation/NfcCardEmulationOccurredTest.java
+++ b/tests/unit/src/com/android/nfc/cardemulation/NfcCardEmulationOccurredTest.java
@@ -37,9 +37,8 @@
 import android.content.pm.PackageManager;
 import android.nfc.cardemulation.ApduServiceInfo;
 import android.nfc.cardemulation.CardEmulation;
-import android.nfc.cardemulation.HostApduService;
+import android.nfc.cardemulation.PollingFrame;
 import android.os.Binder;
-import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -163,8 +162,7 @@
         mStaticMockSession.finishMocking();
     }
 
-    // TODO: Remove after aosp/2902507
-    // @RequiresFlagsDisabled(Flags.FLAG_STATSD_CE_EVENTS_FLAG)
+    @RequiresFlagsDisabled(Flags.FLAG_STATSD_CE_EVENTS_FLAG)
     @Test
     public void testHCEOther() {
         if (!mNfcSupported) return;
@@ -197,35 +195,41 @@
     public void testOnPollingLoopDetected() {
         if (!mNfcSupported) return;
 
-        Bundle pollingFrame = mock(Bundle.class);
+        PollingFrame pollingFrame = mock(PollingFrame.class);
+        ArrayList<PollingFrame> pollingFrames = new ArrayList<PollingFrame>();
+        pollingFrames.add(pollingFrame);
         ComponentName componentName = mock(ComponentName.class);
         when(componentName.getPackageName()).thenReturn("com.android.nfc");
         when(mockAidCache.getPreferredService()).thenReturn(componentName);
-        mHostEmulation.onPollingLoopDetected(pollingFrame);
-        Bundle resultBundle = mHostEmulation.mPendingPollingLoopFrames.get(0);
-        Assert.assertEquals(pollingFrame, resultBundle);
+        mHostEmulation.onPollingLoopDetected(pollingFrames);
+        PollingFrame resultPollingFrame = mHostEmulation.mPendingPollingLoopFrames.get(0);
+        Assert.assertEquals(pollingFrame, resultPollingFrame);
     }
 
     @Test
     public void testOnPollingLoopDetectedServiceBound() {
         if (!mNfcSupported) return;
 
-        Bundle pollingLoopTypeOnFrame = mock(Bundle.class);
-        Bundle pollingLoopTypeOffFrame = mock(Bundle.class);
-        when(pollingLoopTypeOnFrame.getChar(HostApduService.POLLING_LOOP_TYPE_KEY))
-                .thenReturn(HostApduService.POLLING_LOOP_TYPE_ON);
-        when(pollingLoopTypeOffFrame.getChar(HostApduService.POLLING_LOOP_TYPE_KEY))
-                .thenReturn(HostApduService.POLLING_LOOP_TYPE_OFF);
+        PollingFrame pollingLoopTypeOnFrame = mock(PollingFrame.class);
+        ArrayList<PollingFrame> pollingLoopTypeOnFrames = new ArrayList<PollingFrame>();
+        pollingLoopTypeOnFrames.add(pollingLoopTypeOnFrame);
+        PollingFrame pollingLoopTypeOffFrame = mock(PollingFrame.class);
+        ArrayList<PollingFrame> pollingLoopTypeOffFrames = new ArrayList<PollingFrame>();
+        pollingLoopTypeOffFrames.add(pollingLoopTypeOffFrame);
+        when(pollingLoopTypeOnFrame.getType())
+                .thenReturn(PollingFrame.POLLING_LOOP_TYPE_ON);
+        when(pollingLoopTypeOffFrame.getType())
+                .thenReturn(PollingFrame.POLLING_LOOP_TYPE_OFF);
         ComponentName componentName = mock(ComponentName.class);
         when(componentName.getPackageName()).thenReturn("com.android.nfc");
         when(mockAidCache.getPreferredService()).thenReturn(componentName);
         IBinder iBinder = new Binder();
         ServiceConnection serviceConnection = mHostEmulation.getServiceConnection();
         serviceConnection.onServiceConnected(componentName, iBinder);
-        mHostEmulation.onPollingLoopDetected(pollingLoopTypeOnFrame);
-        mHostEmulation.onPollingLoopDetected(pollingLoopTypeOnFrame);
-        mHostEmulation.onPollingLoopDetected(pollingLoopTypeOffFrame);
-        mHostEmulation.onPollingLoopDetected(pollingLoopTypeOffFrame);
+        mHostEmulation.onPollingLoopDetected(pollingLoopTypeOnFrames);
+        mHostEmulation.onPollingLoopDetected(pollingLoopTypeOnFrames);
+        mHostEmulation.onPollingLoopDetected(pollingLoopTypeOffFrames);
+        mHostEmulation.onPollingLoopDetected(pollingLoopTypeOffFrames);
         IBinder mActiveService = mHostEmulation.getMessenger();
         Assert.assertNotNull(mActiveService);
         Assert.assertEquals(iBinder, mActiveService);
diff --git a/tests/unit/src/com/android/nfc/cardemulation/PreferredServicesTest.java b/tests/unit/src/com/android/nfc/cardemulation/PreferredServicesTest.java
new file mode 100644
index 0000000..37d5471
--- /dev/null
+++ b/tests/unit/src/com/android/nfc/cardemulation/PreferredServicesTest.java
@@ -0,0 +1,608 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.nfc.cardemulation;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.ContentResolver;
+import android.content.ContextWrapper;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.net.Uri;
+import android.nfc.cardemulation.ApduServiceInfo;
+import android.nfc.cardemulation.CardEmulation;
+import android.provider.Settings;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.nfc.ForegroundUtils;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+@RunWith(AndroidJUnit4.class)
+public class PreferredServicesTest {
+
+  private PreferredServices services;
+  private MockitoSession mStaticMockSession;
+  private Context mContext;
+
+  @Mock
+  private RegisteredServicesCache mServicesCache;
+  @Mock
+  private PreferredServices.Callback mCallback;
+  @Mock
+  private RegisteredAidCache mAidCache;
+  @Mock
+  private WalletRoleObserver mObserver;
+  @Mock
+  private ForegroundUtils mForegroundUtils;
+  @Mock
+  private ContentResolver mContentResolver;
+  @Mock
+  private UserManager mUserManager;
+  @Mock
+  private ActivityManager mActivityManager;
+  @Mock
+  private ApduServiceInfo mServiceInfoPayment;
+  @Mock
+  private ApduServiceInfo mServiceInfoNonPayment;
+  @Mock
+  private UserHandle mUserHandle;
+  @Mock
+  private PrintWriter mPrintWriter;
+  @Mock
+  private RegisteredAidCache.AidResolveInfo mResolveInfo;
+
+  @Captor
+  private ArgumentCaptor<Integer> userIdCaptor;
+  @Captor
+  private ArgumentCaptor<ComponentName> candidateCaptor;
+
+  private static final String WALLET_HOLDER_PACKAGE_NAME = "com.android.test.walletroleholder";
+  private static final ComponentName TEST_COMPONENT
+      = new ComponentName(WALLET_HOLDER_PACKAGE_NAME,
+      "com.android.test.walletroleholder.WalletRoleHolderApduService");
+  private static final int USER_ID = 1;
+  private static final int FOREGROUND_UID = 7;
+
+  @Before
+  public void setUp() throws Exception {
+    mStaticMockSession = ExtendedMockito.mockitoSession()
+        .mockStatic(ForegroundUtils.class)
+        .mockStatic(ActivityManager.class)
+        .mockStatic(UserHandle.class)
+        .mockStatic(Settings.Secure.class)
+        .mockStatic(ComponentName.class)
+        .strictness(Strictness.LENIENT)
+        .startMocking();
+    MockitoAnnotations.initMocks(this);
+    mContext = new ContextWrapper(InstrumentationRegistry.getInstrumentation().getTargetContext()) {
+      @Override
+      public Object getSystemService(String name) {
+        if (Context.ACTIVITY_SERVICE.equals(name)) {
+          return (ActivityManager) mActivityManager;
+        } else if (Context.USER_SERVICE.equals(name)) {
+          return (UserManager) mUserManager;
+        } else {
+          return null;
+        }
+      }
+
+      @Override
+      public Context createContextAsUser(UserHandle user, int flags) {
+        return mContext;
+      }
+
+      @Override
+      public ContentResolver getContentResolver() {
+        return mContentResolver;
+      }
+    };
+
+    when(ForegroundUtils.getInstance(any(ActivityManager.class))).thenReturn(mForegroundUtils);
+    when(ActivityManager.getCurrentUser()).thenReturn(USER_ID);
+    doNothing().when(mContentResolver)
+        .registerContentObserverAsUser(any(Uri.class), anyBoolean(), any(), any(UserHandle.class));
+    doNothing().when(mCallback).onPreferredPaymentServiceChanged(anyInt(), any());
+    when(Settings.Secure.getString(any(ContentResolver.class), anyString())).thenReturn("");
+    when(Settings.Secure.getInt(any(ContentResolver.class), anyString())).thenReturn(USER_ID);
+    when(UserHandle.getUserHandleForUid(anyInt())).thenReturn(mUserHandle);
+    when(UserHandle.of(anyInt())).thenReturn(mUserHandle);
+    when(mUserHandle.getIdentifier()).thenReturn(FOREGROUND_UID);
+    when(mObserver.getDefaultWalletRoleHolder(anyInt())).thenReturn(null);
+    when(mServiceInfoPayment.getComponent()).thenReturn(TEST_COMPONENT);
+    when(mServiceInfoPayment.getAids()).thenReturn(getAids());
+    when(mServiceInfoPayment
+        .getCategoryForAid(anyString())).thenReturn(CardEmulation.CATEGORY_PAYMENT);
+    when(mServiceInfoPayment.hasCategory(eq(CardEmulation.CATEGORY_PAYMENT))).thenReturn(true);
+    when(mServiceInfoNonPayment.hasCategory(eq(CardEmulation.CATEGORY_PAYMENT))).thenReturn(false);
+    when(mServiceInfoNonPayment.getAids()).thenReturn(getAids());
+    when(mAidCache.resolveAid(anyString())).thenReturn(mResolveInfo);
+    when(mUserManager.getEnabledProfiles()).thenReturn(getUserHandles());
+    // Wallet role feature is enabled by default; several test cases set this value to false
+    when(mObserver.isWalletRoleFeatureEnabled()).thenReturn(true);
+  }
+
+  @After
+  public void tearDown() {
+    mStaticMockSession.finishMocking();
+  }
+
+  @Test
+  public void testConstructorWhenWalletRoleFeatureIsNotEnabled() {
+    when(mObserver.isWalletRoleFeatureEnabled()).thenReturn(false);
+
+    services = new PreferredServices(mContext, mServicesCache, mAidCache, mObserver, mCallback);
+
+    assertThat(services.mContext).isEqualTo(mContext);
+    assertThat(services.mWalletRoleObserver).isEqualTo(mObserver);
+    assertThat(services.mForegroundUtils).isEqualTo(mForegroundUtils);
+    assertThat(services.mServiceCache).isEqualTo(mServicesCache);
+    assertThat(services.mAidCache).isEqualTo(mAidCache);
+    assertThat(services.mCallback).isEqualTo(mCallback);
+    assertThat(services.mSettingsObserver).isNotNull();
+    verify(mContentResolver, times(2))
+        .registerContentObserverAsUser(any(), anyBoolean(), any(), any(UserHandle.class));
+    verify(mUserManager).getEnabledProfiles();
+    verify(mObserver, never()).getDefaultWalletRoleHolder(anyInt());
+  }
+
+  @Test
+  public void testConstructorWhenWalletRoleFeatureIsEnabled() {
+    services = new PreferredServices(mContext, mServicesCache, mAidCache, mObserver, mCallback);
+
+    assertThat(services.mContext).isEqualTo(mContext);
+    assertThat(services.mWalletRoleObserver).isEqualTo(mObserver);
+    assertThat(services.mForegroundUtils).isEqualTo(mForegroundUtils);
+    assertThat(services.mServiceCache).isEqualTo(mServicesCache);
+    assertThat(services.mAidCache).isEqualTo(mAidCache);
+    assertThat(services.mCallback).isEqualTo(mCallback);
+    assertThat(services.mSettingsObserver).isNotNull();
+    verify(mContentResolver, times(2))
+        .registerContentObserverAsUser(any(), anyBoolean(), any(), any(UserHandle.class));
+    verify(mUserManager).getEnabledProfiles();
+    verify(mObserver).getDefaultWalletRoleHolder(anyInt());
+    assertThat(services.mDefaultWalletHolderPaymentService).isNull();
+    verify(mCallback).onPreferredPaymentServiceChanged(anyInt(), any());
+  }
+
+  @Test
+  public void testOnWalletRoleHolderChangedWithNullPackageName() {
+    services = new PreferredServices(mContext, mServicesCache, mAidCache, mObserver, mCallback);
+
+    services.onWalletRoleHolderChanged(null, USER_ID);
+
+    verify(mCallback, times(2))
+        .onPreferredPaymentServiceChanged(userIdCaptor.capture(), candidateCaptor.capture());
+    List<Integer> userIds = userIdCaptor.getAllValues();
+    assertThat(userIds.get(0)).isEqualTo(USER_ID);
+    assertThat(userIds.get(1)).isEqualTo(USER_ID);
+    List<ComponentName> candidates = candidateCaptor.getAllValues();
+    assertThat(candidates.get(0)).isNull();
+    assertThat(candidates.get(1)).isNull();
+    assertThat(services.mDefaultWalletHolderPaymentService).isNull();
+  }
+
+  @Test
+  public void testOnWalletRoleHolderChangedWithExistingPackageNameAndExistingServiceInfos() {
+    when(mServicesCache.getInstalledServices(eq(USER_ID))).thenReturn(getPaymentServices());
+    services = new PreferredServices(mContext, mServicesCache, mAidCache, mObserver, mCallback);
+
+    services.onWalletRoleHolderChanged(WALLET_HOLDER_PACKAGE_NAME, USER_ID);
+
+    assertThat(services.mUserIdDefaultWalletHolder).isEqualTo(USER_ID);
+    verify(mCallback, times(2))
+        .onPreferredPaymentServiceChanged(userIdCaptor.capture(), candidateCaptor.capture());
+    List<Integer> userIds = userIdCaptor.getAllValues();
+    assertThat(userIds.get(0)).isEqualTo(USER_ID);
+    assertThat(userIds.get(1)).isEqualTo(USER_ID);
+    List<ComponentName> candidates = candidateCaptor.getAllValues();
+    assertThat(candidates.get(0)).isNull();
+    assertThat(candidates.get(1)).isEqualTo(TEST_COMPONENT);
+    assertThat(services.mDefaultWalletHolderPaymentService).isEqualTo(TEST_COMPONENT);
+  }
+
+  @Test
+  public void testOnWalletRoleHolderChangedWithExistingPackageNameAndNoServiceInfo() {
+    ArrayList<ApduServiceInfo> emptyList = new ArrayList<>();
+    when(mServicesCache.getInstalledServices(eq(USER_ID))).thenReturn(emptyList);
+    services = new PreferredServices(mContext, mServicesCache, mAidCache, mObserver, mCallback);
+
+    services.onWalletRoleHolderChanged(WALLET_HOLDER_PACKAGE_NAME, USER_ID);
+
+    assertThat(services.mUserIdDefaultWalletHolder).isEqualTo(USER_ID);
+    verify(mCallback).onPreferredPaymentServiceChanged(anyInt(), any());
+    assertThat(services.mDefaultWalletHolderPaymentService).isNull();
+  }
+
+  @Test
+  public void testOnWalletRoleHolderChangedWithIncorrectPackageName() {
+    when(mServicesCache.getInstalledServices(eq(USER_ID))).thenReturn(getPaymentServices());
+    services = new PreferredServices(mContext, mServicesCache, mAidCache, mObserver, mCallback);
+
+    services.onWalletRoleHolderChanged(/* defaultWalletHolderPackageName = */ "", USER_ID);
+
+    assertThat(services.mUserIdDefaultWalletHolder).isEqualTo(USER_ID);
+    verify(mCallback).onPreferredPaymentServiceChanged(anyInt(), any());
+    assertThat(services.mDefaultWalletHolderPaymentService).isNull();
+  }
+
+  @Test
+  public void testSetDefaultForNextTapWithNonNullService_NotifyChange() {
+    services = new PreferredServices(mContext, mServicesCache, mAidCache, mObserver, mCallback);
+    services.mForegroundUid = FOREGROUND_UID;
+
+    boolean result = services.setDefaultForNextTap(USER_ID, TEST_COMPONENT);
+
+    assertThat(result).isTrue();
+    assertThat(services.mNextTapDefault).isEqualTo(TEST_COMPONENT);
+    assertThat(services.mNextTapDefaultUserId).isEqualTo(USER_ID);
+    assertThat(services.mForegroundCurrent).isEqualTo(TEST_COMPONENT);
+    assertThat(services.mForegroundCurrentUid).isEqualTo(FOREGROUND_UID);
+    verify(mCallback)
+        .onPreferredForegroundServiceChanged(userIdCaptor.capture(), candidateCaptor.capture());
+    assertThat(userIdCaptor.getValue()).isEqualTo(USER_ID);
+    assertThat(candidateCaptor.getValue()).isEqualTo(TEST_COMPONENT);
+  }
+
+  @Test
+  public void testSetDefaultForNextTapWithNullService_NoChange() {
+    services = new PreferredServices(mContext, mServicesCache, mAidCache, mObserver, mCallback);
+    services.mForegroundUid = FOREGROUND_UID;
+    services.mForegroundRequested = null;
+    services.mForegroundCurrent = null;
+
+    boolean result = services.setDefaultForNextTap(USER_ID, /* service = */ null);
+
+    assertThat(result).isTrue();
+    assertThat(services.mNextTapDefault).isNull();
+    assertThat(services.mNextTapDefaultUserId).isEqualTo(USER_ID);
+    assertThat(services.mForegroundCurrent).isEqualTo(null);
+    assertThat(services.mForegroundCurrentUid).isEqualTo(0);
+    verify(mCallback, never()).onPreferredForegroundServiceChanged(anyInt(), any());
+  }
+
+  @Test
+  public void testSetDefaultForNextTapWithNonNullService_NoChange() {
+    services = new PreferredServices(mContext, mServicesCache, mAidCache, mObserver, mCallback);
+    services.mForegroundCurrent = TEST_COMPONENT;
+
+    boolean result = services.setDefaultForNextTap(FOREGROUND_UID, TEST_COMPONENT);
+
+    assertThat(result).isTrue();
+    assertThat(services.mNextTapDefault).isEqualTo(TEST_COMPONENT);
+    assertThat(services.mNextTapDefaultUserId).isEqualTo(FOREGROUND_UID);
+    assertThat(services.mForegroundCurrent).isEqualTo(TEST_COMPONENT);
+    assertThat(services.mForegroundCurrentUid).isEqualTo(0);
+    verify(mCallback, never()).onPreferredForegroundServiceChanged(anyInt(), any());
+  }
+
+  @Test
+  public void testSetDefaultForNextTapWithNullService_NotifyChange() {
+    services = new PreferredServices(mContext, mServicesCache, mAidCache, mObserver, mCallback);
+    services.mForegroundUid = FOREGROUND_UID;
+    services.mForegroundRequested = null;
+    services.mForegroundCurrent = TEST_COMPONENT;
+
+    boolean result = services.setDefaultForNextTap(USER_ID, /* service = */ null);
+
+    assertThat(result).isTrue();
+    assertThat(services.mNextTapDefault).isNull();
+    assertThat(services.mNextTapDefaultUserId).isEqualTo(USER_ID);
+    assertThat(services.mForegroundCurrent).isEqualTo(null);
+    assertThat(services.mForegroundCurrentUid).isEqualTo(FOREGROUND_UID);
+    verify(mCallback)
+        .onPreferredForegroundServiceChanged(userIdCaptor.capture(), candidateCaptor.capture());
+    assertThat(userIdCaptor.getValue()).isEqualTo(FOREGROUND_UID);
+    assertThat(candidateCaptor.getValue()).isNull();
+  }
+
+  @Test
+  public void testOnServicesUpdatedWithNullForeground_NoChange() {
+    services = new PreferredServices(mContext, mServicesCache, mAidCache, mObserver, mCallback);
+    services.mForegroundCurrent = null;
+
+    services.onServicesUpdated();
+
+    assertThat(services.mForegroundRequested).isNull();
+    assertThat(services.mForegroundUid).isEqualTo(0);
+    assertThat(services.mForegroundCurrentUid).isEqualTo(0);
+  }
+
+  @Test
+  public void testOnServicesUpdatedWithNonNullForegroundAndPaymentServiceInfo_CommitsChange() {
+    when(mServicesCache.getService(anyInt(), any())).thenReturn(mServiceInfoPayment);
+    services = new PreferredServices(mContext, mServicesCache, mAidCache, mObserver, mCallback);
+    services.mForegroundCurrent = TEST_COMPONENT;
+    services.mForegroundCurrentUid = FOREGROUND_UID;
+    services.mPaymentDefaults.currentPreferred = null;
+    services.mPaymentDefaults.preferForeground = false;
+
+    services.onServicesUpdated();
+
+    assertThat(services.mForegroundRequested).isNull();
+    assertThat(services.mForegroundUid).isEqualTo(-1);
+    assertThat(services.mForegroundCurrentUid).isEqualTo(-1);
+  }
+
+  @Test
+  public void testOnServicesUpdatedWithNonNullForegroundAndNonPaymentServiceInfo_CommitsChange() {
+    when(mServicesCache.getService(anyInt(), any())).thenReturn(mServiceInfoNonPayment);
+    mResolveInfo.category = CardEmulation.CATEGORY_PAYMENT;
+    mResolveInfo.defaultService = mServiceInfoNonPayment;
+    services = new PreferredServices(mContext, mServicesCache, mAidCache, mObserver, mCallback);
+    services.mForegroundCurrent = TEST_COMPONENT;
+    services.mForegroundCurrentUid = FOREGROUND_UID;
+    services.mPaymentDefaults.currentPreferred = null;
+    services.mPaymentDefaults.mUserHandle = mUserHandle;
+    services.mPaymentDefaults.preferForeground = false;
+
+    services.onServicesUpdated();
+
+    assertThat(services.mForegroundRequested).isNull();
+    assertThat(services.mForegroundUid).isEqualTo(-1);
+    assertThat(services.mForegroundCurrentUid).isEqualTo(-1);
+  }
+
+  @Test
+  public void testOnServicesUpdatedWithNonNullForegroundAndNonPaymentServiceInfo_NoChange() {
+    when(mServicesCache.getService(anyInt(), any())).thenReturn(mServiceInfoNonPayment);
+    mResolveInfo.category = CardEmulation.CATEGORY_PAYMENT;
+    mResolveInfo.defaultService = null;
+    services = new PreferredServices(mContext, mServicesCache, mAidCache, mObserver, mCallback);
+    services.mForegroundCurrent = TEST_COMPONENT;
+    services.mForegroundCurrentUid = FOREGROUND_UID;
+    services.mPaymentDefaults.currentPreferred = null;
+    services.mPaymentDefaults.mUserHandle = mUserHandle;
+    services.mPaymentDefaults.preferForeground = false;
+
+    services.onServicesUpdated();
+
+    assertThat(services.mForegroundRequested).isNull();
+    assertThat(services.mForegroundUid).isEqualTo(0);
+    assertThat(services.mForegroundCurrentUid).isEqualTo(FOREGROUND_UID);
+  }
+
+  @Test
+  public void testRegisterPreferredForegroundServiceWithSuccess() {
+    when(mForegroundUtils.registerUidToBackgroundCallback(any(), anyInt())).thenReturn(true);
+    services = new PreferredServices(mContext, mServicesCache, mAidCache, mObserver, mCallback);
+    services.mPaymentDefaults.currentPreferred = TEST_COMPONENT;
+
+    boolean result = services.registerPreferredForegroundService(TEST_COMPONENT, USER_ID);
+
+    assertThat(result).isTrue();
+    assertThat(services.mForegroundRequested).isEqualTo(TEST_COMPONENT);
+    assertThat(services.mForegroundUid).isEqualTo(USER_ID);
+  }
+
+  @Test
+  public void testRegisterPreferredForegroundServiceWithFailure() {
+    when(mForegroundUtils.registerUidToBackgroundCallback(any(), anyInt())).thenReturn(false);
+    services = new PreferredServices(mContext, mServicesCache, mAidCache, mObserver, mCallback);
+    services.mPaymentDefaults.currentPreferred = TEST_COMPONENT;
+
+    boolean result = services.registerPreferredForegroundService(TEST_COMPONENT, USER_ID);
+
+    assertThat(result).isFalse();
+    assertThat(services.mForegroundRequested).isNull();
+    assertThat(services.mForegroundUid).isEqualTo(0);
+  }
+
+  @Test
+  public void testUnregisteredPreferredForegroundServiceInForeground_ReturnsSuccess() {
+    when(mForegroundUtils.isInForeground(anyInt())).thenReturn(true);
+    services = new PreferredServices(mContext, mServicesCache, mAidCache, mObserver, mCallback);
+    services.mForegroundUid = FOREGROUND_UID;
+
+    boolean result = services.unregisteredPreferredForegroundService(FOREGROUND_UID);
+
+    assertThat(result).isTrue();
+    assertThat(services.mForegroundRequested).isNull();
+    assertThat(services.mForegroundUid).isEqualTo(-1);
+  }
+
+  @Test
+  public void testUnregisteredPreferredForegroundServiceInForeground_ReturnsFailure() {
+    when(mForegroundUtils.isInForeground(anyInt())).thenReturn(true);
+    services = new PreferredServices(mContext, mServicesCache, mAidCache, mObserver, mCallback);
+    services.mForegroundUid = FOREGROUND_UID;
+
+    boolean result = services.unregisteredPreferredForegroundService(USER_ID);
+
+    assertThat(result).isFalse();
+    assertThat(services.mForegroundRequested).isNull();
+    assertThat(services.mForegroundUid).isEqualTo(FOREGROUND_UID);
+  }
+
+  @Test
+  public void testUnregisteredPreferredForegroundServiceNotInForeground_ReturnsFailure() {
+    when(mForegroundUtils.isInForeground(anyInt())).thenReturn(false);
+    services = new PreferredServices(mContext, mServicesCache, mAidCache, mObserver, mCallback);
+
+    boolean result = services.unregisteredPreferredForegroundService(USER_ID);
+
+    assertThat(result).isFalse();
+    assertThat(services.mForegroundRequested).isNull();
+    assertThat(services.mForegroundUid).isEqualTo(0);
+  }
+
+  @Test
+  public void testOnUidToBackground_SuccessfullyUnregistersService() {
+    services = new PreferredServices(mContext, mServicesCache, mAidCache, mObserver, mCallback);
+    services.mForegroundUid = FOREGROUND_UID;
+
+    services.onUidToBackground(FOREGROUND_UID);
+
+    assertThat(services.mForegroundUid).isEqualTo(-1);
+  }
+
+  @Test
+  public void testOnUidToBackground_FailsToUnregisterService() {
+    services = new PreferredServices(mContext, mServicesCache, mAidCache, mObserver, mCallback);
+    services.mForegroundUid = FOREGROUND_UID;
+
+    services.onUidToBackground(USER_ID);
+
+    assertThat(services.mForegroundUid).isEqualTo(FOREGROUND_UID);
+  }
+
+  @Test
+  public void testOnHostEmulationActivated() {
+    services = new PreferredServices(mContext, mServicesCache, mAidCache, mObserver, mCallback);
+    services.mNextTapDefault = TEST_COMPONENT;
+
+    services.onHostEmulationActivated();
+
+    assertThat(services.mClearNextTapDefault).isTrue();
+  }
+
+  @Test
+  public void testOnHostEmulationDeactivated() {
+    services = new PreferredServices(mContext, mServicesCache, mAidCache, mObserver, mCallback);
+    services.mClearNextTapDefault = true;
+    services.mNextTapDefault = TEST_COMPONENT;
+
+    services.onHostEmulationDeactivated();
+
+    assertThat(services.mNextTapDefault).isNull();
+    assertThat(services.mClearNextTapDefault).isFalse();
+  }
+
+  @Test
+  public void testOnUserSwitchedWithChange() {
+    when(mObserver.isWalletRoleFeatureEnabled()).thenReturn(false);
+    services = new PreferredServices(mContext, mServicesCache, mAidCache, mObserver, mCallback);
+    services.mPaymentDefaults.preferForeground = false;
+    services.mPaymentDefaults.currentPreferred = TEST_COMPONENT;
+
+    services.onUserSwitched(USER_ID);
+
+    assertThat(services.mPaymentDefaults.preferForeground).isTrue();
+    assertThat(services.mPaymentDefaults.settingsDefault).isEqualTo(null);
+    assertThat(services.mPaymentDefaults.currentPreferred).isEqualTo(null);
+    assertThat(services.mPaymentDefaults.mUserHandle).isEqualTo(mUserHandle);
+    verify(mCallback)
+        .onPreferredPaymentServiceChanged(userIdCaptor.capture(), candidateCaptor.capture());
+    assertThat(userIdCaptor.getValue()).isEqualTo(FOREGROUND_UID);
+    assertThat(candidateCaptor.getValue()).isEqualTo(null);
+  }
+
+  @Test
+  public void testOnUserSwitchedWithNoChange() throws Exception {
+    when(mUserManager.getEnabledProfiles()).thenReturn(getUserHandles());
+    services = new PreferredServices(mContext, mServicesCache, mAidCache, mObserver, mCallback);
+    services.mPaymentDefaults.preferForeground = false;
+    services.mPaymentDefaults.currentPreferred = null;
+    verify(mCallback).onPreferredPaymentServiceChanged(anyInt(), any());
+
+    services.onUserSwitched(USER_ID);
+
+    assertThat(services.mPaymentDefaults.preferForeground).isTrue();
+    assertThat(services.mPaymentDefaults.settingsDefault).isEqualTo(null);
+    assertThat(services.mPaymentDefaults.currentPreferred).isEqualTo(null);
+    assertThat(services.mPaymentDefaults.mUserHandle).isEqualTo(null);
+    verifyNoMoreInteractions(mCallback);
+  }
+
+  @Test
+  public void testPackageHasPreferredServiceWithNullPackageName_ReturnsFalse() {
+    services = new PreferredServices(mContext, mServicesCache, mAidCache, mObserver, mCallback);
+
+    boolean result = services.packageHasPreferredService(/* packageName = */ null);
+
+    assertThat(result).isFalse();
+  }
+
+  @Test
+  public void testPackageHasPreferredServiceWithMatchingPackageName_ReturnsTrue() {
+    services = new PreferredServices(mContext, mServicesCache, mAidCache, mObserver, mCallback);
+    services.mPaymentDefaults.currentPreferred = TEST_COMPONENT;
+
+    boolean result = services.packageHasPreferredService(WALLET_HOLDER_PACKAGE_NAME);
+
+    assertThat(result).isTrue();
+  }
+
+  @Test
+  public void testPackageHasPreferredServiceWithNonMatchingPackageName_ReturnsFalse() {
+    services = new PreferredServices(mContext, mServicesCache, mAidCache, mObserver, mCallback);
+
+    boolean result = services.packageHasPreferredService(WALLET_HOLDER_PACKAGE_NAME);
+
+    assertThat(result).isFalse();
+  }
+
+  @Test
+  public void testDump() {
+    when(mUserManager.getUserName()).thenReturn("");
+    services = new PreferredServices(mContext, mServicesCache, mAidCache, mObserver, mCallback);
+
+    services.dump(null, mPrintWriter, null);
+
+    verify(mPrintWriter, times(8)).println(anyString());
+  }
+
+  private ArrayList<String> getAids() {
+    ArrayList<String> aids = new ArrayList<>();
+    aids.add("aid");
+    return aids;
+  }
+
+  private ArrayList<ApduServiceInfo> getPaymentServices() {
+    ArrayList<ApduServiceInfo> serviceInfos = new ArrayList<>();
+    serviceInfos.add(mServiceInfoPayment);
+    return serviceInfos;
+  }
+
+  private ArrayList<UserHandle> getUserHandles() {
+    ArrayList<UserHandle> list = new ArrayList<>();
+    list.add(mUserHandle);
+    return list;
+  }
+}
\ No newline at end of file
diff --git a/tests/unit/src/com/android/nfc/cardemulation/RegisteredAidCacheTest.java b/tests/unit/src/com/android/nfc/cardemulation/RegisteredAidCacheTest.java
new file mode 100644
index 0000000..69f760e
--- /dev/null
+++ b/tests/unit/src/com/android/nfc/cardemulation/RegisteredAidCacheTest.java
@@ -0,0 +1,626 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.nfc.cardemulation;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.nfc.cardemulation.ApduServiceInfo;
+import android.nfc.cardemulation.CardEmulation;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.nfc.NfcService;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class RegisteredAidCacheTest {
+
+    private static final String PREFIX_AID = "ASDASD*";
+    private static final String SUBSET_AID = "ASDASD#";
+    private static final String EXACT_AID = "TASDASD";
+    private static final String PAYMENT_AID_1 = "A000000004101012";
+    private static final String PAYMENT_AID_2 = "A000000004101018";
+    private static final String NON_PAYMENT_AID_1 = "F053414950454D";
+    private static final String PREFIX_PAYMENT_AID = "A000000004*";
+    private static final String NFC_FOREGROUND_PACKAGE_NAME = "com.android.test.foregroundnfc";
+    private static final String NON_PAYMENT_NFC_PACKAGE_NAME = "com.android.test.nonpaymentnfc";
+    private static final String WALLET_HOLDER_PACKAGE_NAME = "com.android.test.walletroleholder";
+    private static final String WALLET_HOLDER_2_PACKAGE_NAME = "com.android.test.walletroleholder2";
+
+    private static final ComponentName WALLET_PAYMENT_SERVICE
+            = new ComponentName(WALLET_HOLDER_PACKAGE_NAME,
+            "com.android.test.walletroleholder.WalletRoleHolderApduService");
+
+    private static final ComponentName WALLET_PAYMENT_SERVICE_2
+            = new ComponentName(WALLET_HOLDER_PACKAGE_NAME,
+            "com.android.test.walletroleholder.XWalletRoleHolderApduService");
+    private static final ComponentName FOREGROUND_SERVICE
+            = new ComponentName(NFC_FOREGROUND_PACKAGE_NAME,
+            "com.android.test.foregroundnfc.ForegroundApduService");
+    private static final ComponentName NON_PAYMENT_SERVICE =
+            new ComponentName(NON_PAYMENT_NFC_PACKAGE_NAME,
+                    "com.android.test.nonpaymentnfc.NonPaymentApduService");
+
+    private static final ComponentName PAYMENT_SERVICE =
+            new ComponentName(WALLET_HOLDER_2_PACKAGE_NAME,
+                    "com.android.test.walletroleholder.WalletRoleHolderXApduService");
+
+    private static final int USER_ID = 0;
+    private static final UserHandle USER_HANDLE = UserHandle.of(USER_ID);
+
+    @Mock
+    private Context mContext;
+    @Mock
+    private WalletRoleObserver mWalletRoleObserver;
+    @Mock
+    private AidRoutingManager mAidRoutingManager;
+    @Mock
+    private UserManager mUserManager;
+    @Mock
+    private NfcService mNfcService;
+    @Captor
+    private ArgumentCaptor<HashMap<String, AidRoutingManager.AidEntry>> mRoutingEntryMapCaptor;
+
+    private MockitoSession mStaticMockSession;
+
+    RegisteredAidCache mRegisteredAidCache;
+
+    @Before
+    public void setUp() {
+        mStaticMockSession = ExtendedMockito.mockitoSession()
+                .mockStatic(ActivityManager.class)
+                .mockStatic(NfcService.class)
+                .strictness(Strictness.LENIENT)
+                .startMocking();
+        MockitoAnnotations.initMocks(this);
+        when(ActivityManager.getCurrentUser()).thenReturn(USER_ID);
+        when(NfcService.getInstance()).thenReturn(mNfcService);
+        when(mNfcService.getNciVersion()).thenReturn(NfcService.NCI_VERSION_1_0);
+        when(mUserManager.getProfileParent(eq(USER_HANDLE))).thenReturn(USER_HANDLE);
+        when(mContext.createContextAsUser(
+                any(), anyInt())).thenReturn(mContext);
+        when(mContext.getSystemService(eq(UserManager.class))).thenReturn(mUserManager);
+    }
+
+    @After
+    public void tearDown() {
+        mStaticMockSession.finishMocking();
+    }
+
+    @Test
+    public void testConstructor_supportsPrefixAndSubset() {
+        supportPrefixAndSubset(true);
+        mRegisteredAidCache = new RegisteredAidCache(mContext, mWalletRoleObserver,
+                mAidRoutingManager);
+
+        verify(mAidRoutingManager).supportsAidPrefixRouting();
+        verify(mAidRoutingManager).supportsAidSubsetRouting();
+        Assert.assertTrue(mRegisteredAidCache.supportsAidPrefixRegistration());
+        Assert.assertTrue(mRegisteredAidCache.supportsAidSubsetRegistration());
+    }
+
+    @Test
+    public void testConstructor_doesNotSupportsPrefixAndSubset() {
+        supportPrefixAndSubset(false);
+        mRegisteredAidCache = new RegisteredAidCache(mContext, mWalletRoleObserver,
+                mAidRoutingManager);
+
+        verify(mAidRoutingManager).supportsAidPrefixRouting();
+        verify(mAidRoutingManager).supportsAidSubsetRouting();
+        Assert.assertFalse(mRegisteredAidCache.supportsAidPrefixRegistration());
+        Assert.assertFalse(mRegisteredAidCache.supportsAidSubsetRegistration());
+    }
+
+    @Test
+    public void testAidStaticMethods() {
+        Assert.assertTrue(RegisteredAidCache.isPrefix(PREFIX_AID));
+        Assert.assertTrue(RegisteredAidCache.isSubset(SUBSET_AID));
+        Assert.assertTrue(RegisteredAidCache.isExact(EXACT_AID));
+
+        Assert.assertFalse(RegisteredAidCache.isPrefix(EXACT_AID));
+        Assert.assertFalse(RegisteredAidCache.isSubset(EXACT_AID));
+        Assert.assertFalse(RegisteredAidCache.isExact(PREFIX_AID));
+        Assert.assertFalse(RegisteredAidCache.isExact(SUBSET_AID));
+    }
+
+    @Test
+    public void testAidConflictResolution_walletRoleEnabledNfcDisabled_foregroundWins() {
+        setWalletRoleFlag(true);
+        supportPrefixAndSubset(false);
+        mRegisteredAidCache = new RegisteredAidCache(mContext, mWalletRoleObserver,
+                mAidRoutingManager);
+        mRegisteredAidCache.mNfcEnabled = false;
+
+        List<ApduServiceInfo> apduServiceInfos = new ArrayList<>();
+        apduServiceInfos.add(createServiceInfoForAidRouting(
+                WALLET_PAYMENT_SERVICE,
+                true,
+                List.of(PAYMENT_AID_1, NON_PAYMENT_AID_1),
+                List.of(CardEmulation.CATEGORY_PAYMENT, CardEmulation.CATEGORY_OTHER),
+                false,
+                false,
+                USER_ID,
+                true));
+        apduServiceInfos.add(createServiceInfoForAidRouting(
+                FOREGROUND_SERVICE,
+                true,
+                List.of(PAYMENT_AID_1, NON_PAYMENT_AID_1),
+                List.of(CardEmulation.CATEGORY_PAYMENT, CardEmulation.CATEGORY_OTHER),
+                false,
+                false,
+                USER_ID,
+                true));
+        apduServiceInfos.add(createServiceInfoForAidRouting(
+                NON_PAYMENT_SERVICE,
+                true,
+                List.of(NON_PAYMENT_AID_1),
+                List.of(CardEmulation.CATEGORY_OTHER),
+                false,
+                false,
+                USER_ID,
+                true));
+
+        mRegisteredAidCache.generateUserApduServiceInfoLocked(USER_ID, apduServiceInfos);
+        mRegisteredAidCache.generateServiceMapLocked(apduServiceInfos);
+        mRegisteredAidCache.onPreferredForegroundServiceChanged(USER_ID, FOREGROUND_SERVICE);
+        RegisteredAidCache.AidResolveInfo resolveInfo
+                = mRegisteredAidCache.resolveAid(PAYMENT_AID_1);
+
+        verify(mAidRoutingManager).supportsAidPrefixRouting();
+        verify(mAidRoutingManager).supportsAidSubsetRouting();
+        Assert.assertEquals(resolveInfo.defaultService.getComponent(), FOREGROUND_SERVICE);
+        Assert.assertEquals(mRegisteredAidCache.getPreferredService(), FOREGROUND_SERVICE);
+        Assert.assertEquals(resolveInfo.services.size(), 1);
+        Assert.assertEquals(resolveInfo.category, CardEmulation.CATEGORY_PAYMENT);
+        verifyNoMoreInteractions(mAidRoutingManager);
+    }
+
+    @Test
+    public void testAidConflictResolution_walletRoleEnabledNfcEnabled_walletWins() {
+        setWalletRoleFlag(true);
+        supportPrefixAndSubset(false);
+        mRegisteredAidCache = new RegisteredAidCache(mContext, mWalletRoleObserver,
+                mAidRoutingManager);
+        mRegisteredAidCache.mNfcEnabled = true;
+
+        List<ApduServiceInfo> apduServiceInfos = new ArrayList<>();
+        apduServiceInfos.add(createServiceInfoForAidRouting(
+                WALLET_PAYMENT_SERVICE,
+                true,
+                List.of(PAYMENT_AID_1),
+                List.of(CardEmulation.CATEGORY_PAYMENT),
+                false,
+                true,
+                USER_ID,
+                true));
+        apduServiceInfos.add(createServiceInfoForAidRouting(
+                PAYMENT_SERVICE,
+                true,
+                List.of(PAYMENT_AID_1),
+                List.of(CardEmulation.CATEGORY_PAYMENT),
+                false,
+                true,
+                USER_ID,
+                true));
+        apduServiceInfos.add(createServiceInfoForAidRouting(
+                NON_PAYMENT_SERVICE,
+                true,
+                List.of(NON_PAYMENT_AID_1),
+                List.of(CardEmulation.CATEGORY_OTHER),
+                false,
+                false,
+                USER_ID,
+                true));
+
+        mRegisteredAidCache.generateUserApduServiceInfoLocked(USER_ID, apduServiceInfos);
+        mRegisteredAidCache.generateServiceMapLocked(apduServiceInfos);
+        mRegisteredAidCache.onWalletRoleHolderChanged(WALLET_HOLDER_PACKAGE_NAME, USER_ID);
+        RegisteredAidCache.AidResolveInfo paymentResolveInfo
+                = mRegisteredAidCache.resolveAid(PAYMENT_AID_1);
+        RegisteredAidCache.AidResolveInfo nonPaymentResolveInfo
+                = mRegisteredAidCache.resolveAid(NON_PAYMENT_AID_1);
+
+        Assert.assertEquals(paymentResolveInfo.defaultService.getComponent(),
+                WALLET_PAYMENT_SERVICE);
+        Assert.assertEquals(paymentResolveInfo.services.size(), 1);
+        Assert.assertEquals(paymentResolveInfo.category, CardEmulation.CATEGORY_PAYMENT);
+        Assert.assertEquals(nonPaymentResolveInfo.defaultService.getComponent(),
+                NON_PAYMENT_SERVICE);
+        Assert.assertEquals(nonPaymentResolveInfo.services.size(), 1);
+        Assert.assertEquals(nonPaymentResolveInfo.category, CardEmulation.CATEGORY_OTHER);
+        verify(mAidRoutingManager).configureRouting(mRoutingEntryMapCaptor.capture(),
+                eq(false));
+        HashMap<String, AidRoutingManager.AidEntry> routingEntries =
+                mRoutingEntryMapCaptor.getValue();
+        Assert.assertTrue(routingEntries.containsKey(PAYMENT_AID_1));
+        Assert.assertTrue(routingEntries.containsKey(NON_PAYMENT_AID_1));
+        Assert.assertTrue(routingEntries.get(PAYMENT_AID_1).isOnHost);
+        Assert.assertTrue(routingEntries.get(NON_PAYMENT_AID_1).isOnHost);
+        Assert.assertNull(routingEntries.get(PAYMENT_AID_1).offHostSE);
+        Assert.assertNull(routingEntries.get(NON_PAYMENT_AID_1).offHostSE);
+        Assert.assertTrue(mRegisteredAidCache.isRequiresScreenOnServiceExist());
+    }
+
+    @Test
+    public void testAidConflictResolution_walletRoleEnabledNfcEnabledPreFixAid_walletWins() {
+        setWalletRoleFlag(true);
+        supportPrefixAndSubset(true);
+        mRegisteredAidCache = new RegisteredAidCache(mContext, mWalletRoleObserver,
+                mAidRoutingManager);
+        mRegisteredAidCache.mNfcEnabled = true;
+
+        List<ApduServiceInfo> apduServiceInfos = new ArrayList<>();
+        apduServiceInfos.add(createServiceInfoForAidRouting(
+                WALLET_PAYMENT_SERVICE,
+                true,
+                List.of(PREFIX_PAYMENT_AID),
+                List.of(CardEmulation.CATEGORY_PAYMENT),
+                false,
+                true,
+                USER_ID,
+                true));
+        apduServiceInfos.add(createServiceInfoForAidRouting(
+                PAYMENT_SERVICE,
+                true,
+                List.of(PAYMENT_AID_1),
+                List.of(CardEmulation.CATEGORY_PAYMENT),
+                false,
+                true,
+                USER_ID,
+                true));
+        apduServiceInfos.add(createServiceInfoForAidRouting(
+                NON_PAYMENT_SERVICE,
+                true,
+                List.of(NON_PAYMENT_AID_1),
+                List.of(CardEmulation.CATEGORY_OTHER),
+                false,
+                false,
+                USER_ID,
+                true));
+
+        mRegisteredAidCache.generateUserApduServiceInfoLocked(USER_ID, apduServiceInfos);
+        mRegisteredAidCache.generateServiceMapLocked(apduServiceInfos);
+        mRegisteredAidCache.onWalletRoleHolderChanged(WALLET_HOLDER_PACKAGE_NAME, USER_ID);
+        RegisteredAidCache.AidResolveInfo paymentResolveInfo
+                = mRegisteredAidCache.resolveAid(PAYMENT_AID_1);
+        RegisteredAidCache.AidResolveInfo nonPaymentResolveInfo
+                = mRegisteredAidCache.resolveAid(NON_PAYMENT_AID_1);
+
+        Assert.assertEquals(paymentResolveInfo.defaultService.getComponent(),
+                WALLET_PAYMENT_SERVICE);
+        Assert.assertEquals(paymentResolveInfo.services.size(), 1);
+        Assert.assertEquals(paymentResolveInfo.category, CardEmulation.CATEGORY_PAYMENT);
+        Assert.assertEquals(nonPaymentResolveInfo.defaultService.getComponent(),
+                NON_PAYMENT_SERVICE);
+        Assert.assertEquals(nonPaymentResolveInfo.services.size(), 1);
+        Assert.assertEquals(nonPaymentResolveInfo.category, CardEmulation.CATEGORY_OTHER);
+    }
+
+    @Test
+    public void testAidConflictResolution_walletRoleEnabled_twoServicesOnWallet_firstServiceWins() {
+        setWalletRoleFlag(true);
+        supportPrefixAndSubset(false);
+        mRegisteredAidCache = new RegisteredAidCache(mContext, mWalletRoleObserver,
+                mAidRoutingManager);
+
+        List<ApduServiceInfo> apduServiceInfos = new ArrayList<>();
+        apduServiceInfos.add(createServiceInfoForAidRouting(
+                WALLET_PAYMENT_SERVICE,
+                true,
+                List.of(PAYMENT_AID_1, NON_PAYMENT_AID_1),
+                List.of(CardEmulation.CATEGORY_PAYMENT, CardEmulation.CATEGORY_OTHER),
+                false,
+                false,
+                USER_ID,
+                true));
+        apduServiceInfos.add(createServiceInfoForAidRouting(
+                WALLET_PAYMENT_SERVICE_2,
+                true,
+                List.of(PAYMENT_AID_1),
+                List.of(CardEmulation.CATEGORY_PAYMENT),
+                false,
+                false,
+                USER_ID,
+                true));
+        apduServiceInfos.add(createServiceInfoForAidRouting(
+                PAYMENT_SERVICE,
+                true,
+                List.of(PAYMENT_AID_1),
+                List.of(CardEmulation.CATEGORY_PAYMENT),
+                false,
+                false,
+                USER_ID,
+                true));
+
+        mRegisteredAidCache.generateUserApduServiceInfoLocked(USER_ID, apduServiceInfos);
+        mRegisteredAidCache.generateServiceMapLocked(apduServiceInfos);
+        mRegisteredAidCache.onWalletRoleHolderChanged(WALLET_HOLDER_PACKAGE_NAME, USER_ID);
+        RegisteredAidCache.AidResolveInfo resolveInfo
+                = mRegisteredAidCache.resolveAid(PAYMENT_AID_1);
+        Assert.assertEquals(resolveInfo.defaultService.getComponent(), WALLET_PAYMENT_SERVICE);
+        Assert.assertEquals(resolveInfo.services.size(), 2);
+        Assert.assertEquals(resolveInfo.category, CardEmulation.CATEGORY_PAYMENT);
+    }
+
+    @Test
+    public void testOnServicesUpdated_walletRoleEnabled() {
+        setWalletRoleFlag(true);
+        supportPrefixAndSubset(false);
+        mRegisteredAidCache = new RegisteredAidCache(mContext, mWalletRoleObserver,
+                mAidRoutingManager);
+        mRegisteredAidCache.mNfcEnabled = true;
+
+        List<ApduServiceInfo> apduServiceInfos = new ArrayList<>();
+        apduServiceInfos.add(createServiceInfoForAidRouting(
+                WALLET_PAYMENT_SERVICE,
+                true,
+                List.of(PAYMENT_AID_1),
+                List.of(CardEmulation.CATEGORY_PAYMENT),
+                false,
+                true,
+                USER_ID,
+                true));
+        apduServiceInfos.add(createServiceInfoForAidRouting(
+                PAYMENT_SERVICE,
+                true,
+                List.of(PAYMENT_AID_1),
+                List.of(CardEmulation.CATEGORY_PAYMENT),
+                false,
+                true,
+                USER_ID,
+                true));
+        apduServiceInfos.add(createServiceInfoForAidRouting(
+                NON_PAYMENT_SERVICE,
+                true,
+                List.of(NON_PAYMENT_AID_1),
+                List.of(CardEmulation.CATEGORY_OTHER),
+                false,
+                true,
+                USER_ID,
+                true));
+
+        mRegisteredAidCache.onServicesUpdated(USER_ID, apduServiceInfos);
+
+        verify(mAidRoutingManager).supportsAidPrefixRouting();
+        verify(mAidRoutingManager).supportsAidSubsetRouting();
+        Assert.assertTrue(mRegisteredAidCache.mAidServices.containsKey(PAYMENT_AID_1));
+        Assert.assertTrue(mRegisteredAidCache.mAidServices.containsKey(NON_PAYMENT_AID_1));
+        Assert.assertEquals(mRegisteredAidCache.mAidServices.get(PAYMENT_AID_1).size(), 2);
+        Assert.assertEquals(mRegisteredAidCache.mAidServices.get(NON_PAYMENT_AID_1).size(), 1);
+        Assert.assertEquals(mRegisteredAidCache.mAidServices.get(PAYMENT_AID_1).get(0)
+                .service.getComponent(), WALLET_PAYMENT_SERVICE);
+        Assert.assertEquals(mRegisteredAidCache.mAidServices.get(PAYMENT_AID_1).get(1)
+                        .service.getComponent(), PAYMENT_SERVICE);
+        verify(mAidRoutingManager).configureRouting(mRoutingEntryMapCaptor.capture(),
+                eq(false));
+        HashMap<String, AidRoutingManager.AidEntry> routingEntries =
+                mRoutingEntryMapCaptor.getValue();
+        Assert.assertTrue(routingEntries.containsKey(NON_PAYMENT_AID_1));
+        Assert.assertTrue(routingEntries.get(NON_PAYMENT_AID_1).isOnHost);
+        Assert.assertNull(routingEntries.get(NON_PAYMENT_AID_1).offHostSE);
+        Assert.assertTrue(mRegisteredAidCache.isRequiresScreenOnServiceExist());
+    }
+
+    @Test
+    public void testOnNfcEnabled() {
+        setWalletRoleFlag(true);
+        supportPrefixAndSubset(false);
+        mRegisteredAidCache = new RegisteredAidCache(mContext, mWalletRoleObserver,
+                mAidRoutingManager);
+
+        List<ApduServiceInfo> apduServiceInfos = new ArrayList<>();
+        apduServiceInfos.add(createServiceInfoForAidRouting(
+                WALLET_PAYMENT_SERVICE,
+                true,
+                List.of(PAYMENT_AID_1),
+                List.of(CardEmulation.CATEGORY_PAYMENT),
+                false,
+                false,
+                USER_ID,
+                true));
+        apduServiceInfos.add(createServiceInfoForAidRouting(
+                PAYMENT_SERVICE,
+                true,
+                List.of(PAYMENT_AID_1),
+                List.of(CardEmulation.CATEGORY_PAYMENT),
+                false,
+                false,
+                USER_ID,
+                true));
+        apduServiceInfos.add(createServiceInfoForAidRouting(
+                NON_PAYMENT_SERVICE,
+                true,
+                List.of(NON_PAYMENT_AID_1),
+                List.of(CardEmulation.CATEGORY_OTHER),
+                false,
+                false,
+                USER_ID,
+                true));
+
+        mRegisteredAidCache.generateUserApduServiceInfoLocked(USER_ID, apduServiceInfos);
+        mRegisteredAidCache.generateServiceMapLocked(apduServiceInfos);
+        mRegisteredAidCache.onNfcEnabled();
+
+        verify(mAidRoutingManager).supportsAidPrefixRouting();
+        verify(mAidRoutingManager).supportsAidSubsetRouting();
+        verify(mAidRoutingManager).configureRouting(mRoutingEntryMapCaptor.capture(),
+                eq(false));
+        Assert.assertFalse(mRegisteredAidCache.isRequiresScreenOnServiceExist());
+    }
+
+    @Test
+    public void testOnNfcDisabled() {
+        setWalletRoleFlag(true);
+        supportPrefixAndSubset(false);
+        mRegisteredAidCache = new RegisteredAidCache(mContext, mWalletRoleObserver,
+                mAidRoutingManager);
+        mRegisteredAidCache.onNfcDisabled();
+
+        verify(mAidRoutingManager).supportsAidPrefixRouting();
+        verify(mAidRoutingManager).supportsAidSubsetRouting();
+        verify(mAidRoutingManager).onNfccRoutingTableCleared();
+    }
+
+    @Test
+    public void testPollingLoopFilterToForeground_walletRoleEnabled_walletSet() {
+        setWalletRoleFlag(true);
+        supportPrefixAndSubset(false);
+        mRegisteredAidCache = new RegisteredAidCache(mContext, mWalletRoleObserver,
+                mAidRoutingManager);
+        mRegisteredAidCache.mNfcEnabled = true;
+
+        List<ApduServiceInfo> apduServiceInfos = new ArrayList<>();
+        apduServiceInfos.add(createServiceInfoForAidRouting(
+                WALLET_PAYMENT_SERVICE,
+                true,
+                List.of(PAYMENT_AID_1, NON_PAYMENT_AID_1),
+                List.of(CardEmulation.CATEGORY_PAYMENT, CardEmulation.CATEGORY_OTHER),
+                false,
+                false,
+                USER_ID,
+                true));
+        apduServiceInfos.add(createServiceInfoForAidRouting(
+                FOREGROUND_SERVICE,
+                true,
+                List.of(PAYMENT_AID_1, NON_PAYMENT_AID_1),
+                List.of(CardEmulation.CATEGORY_PAYMENT, CardEmulation.CATEGORY_OTHER),
+                false,
+                false,
+                USER_ID,
+                true));
+        apduServiceInfos.add(createServiceInfoForAidRouting(
+                NON_PAYMENT_SERVICE,
+                true,
+                List.of(NON_PAYMENT_AID_1),
+                List.of(CardEmulation.CATEGORY_OTHER),
+                false,
+                false,
+                USER_ID,
+                true));
+
+        mRegisteredAidCache.onWalletRoleHolderChanged(WALLET_HOLDER_PACKAGE_NAME, USER_ID);
+        mRegisteredAidCache.onPreferredForegroundServiceChanged(USER_ID, FOREGROUND_SERVICE);
+
+        ApduServiceInfo resolvedApdu =
+                mRegisteredAidCache.resolvePollingLoopFilterConflict(apduServiceInfos);
+
+        Assert.assertEquals(resolvedApdu, apduServiceInfos.get(1));
+    }
+
+    @Test
+    public void testPollingLoopFilterToWallet_walletRoleEnabled_walletSet() {
+        setWalletRoleFlag(true);
+        supportPrefixAndSubset(false);
+        mRegisteredAidCache = new RegisteredAidCache(mContext, mWalletRoleObserver,
+                mAidRoutingManager);
+        mRegisteredAidCache.mNfcEnabled = true;
+
+        List<ApduServiceInfo> apduServiceInfos = new ArrayList<>();
+        apduServiceInfos.add(createServiceInfoForAidRouting(
+                WALLET_PAYMENT_SERVICE,
+                true,
+                List.of(PAYMENT_AID_1, NON_PAYMENT_AID_1),
+                List.of(CardEmulation.CATEGORY_PAYMENT, CardEmulation.CATEGORY_OTHER),
+                false,
+                false,
+                USER_ID,
+                true));
+        apduServiceInfos.add(createServiceInfoForAidRouting(
+                FOREGROUND_SERVICE,
+                true,
+                List.of(PAYMENT_AID_1, NON_PAYMENT_AID_1),
+                List.of(CardEmulation.CATEGORY_PAYMENT, CardEmulation.CATEGORY_OTHER),
+                false,
+                false,
+                USER_ID,
+                true));
+        apduServiceInfos.add(createServiceInfoForAidRouting(
+                NON_PAYMENT_SERVICE,
+                true,
+                List.of(NON_PAYMENT_AID_1),
+                List.of(CardEmulation.CATEGORY_OTHER),
+                false,
+                false,
+                USER_ID,
+                true));
+
+        mRegisteredAidCache.mDefaultWalletHolderPackageName = WALLET_HOLDER_PACKAGE_NAME;
+
+        ApduServiceInfo resolvedApdu =
+                mRegisteredAidCache.resolvePollingLoopFilterConflict(apduServiceInfos);
+
+        Assert.assertEquals(resolvedApdu, apduServiceInfos.get(0));
+    }
+
+    private void setWalletRoleFlag(boolean flag) {
+        when(mWalletRoleObserver.isWalletRoleFeatureEnabled()).thenReturn(flag);
+    }
+
+    private void supportPrefixAndSubset(boolean support) {
+        when(mAidRoutingManager.supportsAidPrefixRouting()).thenReturn(support);
+        when(mAidRoutingManager.supportsAidSubsetRouting()).thenReturn(support);
+    }
+
+    private static ApduServiceInfo createServiceInfoForAidRouting(ComponentName componentName,
+            boolean onHost,
+            List<String> aids,List<String> categories, boolean requiresUnlock, boolean requiresScreenOn,
+            int uid, boolean isCategoryOtherServiceEnabled) {
+        ApduServiceInfo apduServiceInfo = Mockito.mock(ApduServiceInfo.class);
+        when(apduServiceInfo.isOnHost()).thenReturn(onHost);
+        when(apduServiceInfo.getAids()).thenReturn(aids);
+        when(apduServiceInfo.getUid()).thenReturn(uid);
+        when(apduServiceInfo.requiresUnlock()).thenReturn(requiresUnlock);
+        when(apduServiceInfo.requiresScreenOn()).thenReturn(requiresScreenOn);
+        when(apduServiceInfo.isCategoryOtherServiceEnabled())
+                .thenReturn(isCategoryOtherServiceEnabled);
+        when(apduServiceInfo.getComponent()).thenReturn(componentName);
+        for (int i = 0; i < aids.size(); i++) {
+            String aid = aids.get(i);
+            String category = categories.get(i);
+            when(apduServiceInfo.getCategoryForAid(eq(aid))).thenReturn(category);
+        }
+        return apduServiceInfo;
+    }
+
+}
diff --git a/tests/unit/src/com/android/nfc/cardemulation/RegisteredServicesCacheTest.java b/tests/unit/src/com/android/nfc/cardemulation/RegisteredServicesCacheTest.java
new file mode 100644
index 0000000..94df5d3
--- /dev/null
+++ b/tests/unit/src/com/android/nfc/cardemulation/RegisteredServicesCacheTest.java
@@ -0,0 +1,365 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.nfc.cardemulation;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.nfc.NfcAdapter;
+import android.nfc.cardemulation.ApduServiceInfo;
+import android.nfc.cardemulation.CardEmulation;
+import android.nfc.cardemulation.HostApduService;
+import android.nfc.cardemulation.OffHostApduService;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.nfc.NfcService;
+import com.android.nfc.NfcStatsLog;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@RunWith(AndroidJUnit4.class)
+public class RegisteredServicesCacheTest {
+
+    private static final int USER_ID = 1;
+    private static final UserHandle USER_HANDLE = UserHandle.of(USER_ID);
+    private static final UserHandle USER_HANDLE_QUITE_MODE = UserHandle.of(2);
+    private static final File DIR = new File("/");
+    private static final String ANOTHER_PACKAGE_NAME = "com.android.test.another";
+    private static final String NON_PAYMENT_NFC_PACKAGE_NAME = "com.android.test.nonpaymentnfc";
+    private static final String WALLET_HOLDER_PACKAGE_NAME = "com.android.test.walletroleholder";
+    private static final String ON_HOST_SERVICE_NAME
+            = "com.android.test.walletroleholder.OnHostApduService";
+    private static final String OFF_HOST_SERVICE_NAME
+            = "com.android.test.another.OffHostApduService";
+    private static final String NON_PAYMENT_SERVICE_NAME
+            = "com.android.test.nonpaymentnfc.NonPaymentApduService";
+    private static final ComponentName WALLET_HOLDER_SERVICE_COMPONENT =
+            new ComponentName(WALLET_HOLDER_PACKAGE_NAME, ON_HOST_SERVICE_NAME);
+    private static final ComponentName NON_PAYMENT_SERVICE_COMPONENT =
+            new ComponentName(NON_PAYMENT_NFC_PACKAGE_NAME, NON_PAYMENT_SERVICE_NAME);
+    private static final ComponentName ANOTHER_SERVICE_COMPONENT =
+            new ComponentName(ANOTHER_PACKAGE_NAME, OFF_HOST_SERVICE_NAME);
+    private static final String OFFHOST_SE_STRING = "offhostse";
+    private static final String TRUE_STRING = "true";
+    private static final String FALSE_STRING = "false";
+    private static final String ANDROID_STRING = "android";
+    private static final List<String> PAYMENT_AIDS = List.of("A000000004101011",
+            "A000000004101012", "A000000004101013");
+    private static final List<String> NON_PAYMENT_AID = List.of("F053414950454D");
+
+    @Mock
+    private Context mContext;
+    @Mock
+    private RegisteredServicesCache.Callback mCallback;
+    @Mock
+    private UserManager mUserManager;
+    @Mock
+    private PackageManager mPackageManager;
+    @Mock
+    private RegisteredServicesCache.SettingsFile mDynamicSettingsFile;
+    @Mock
+    private RegisteredServicesCache.SettingsFile mOtherSettingsFile;
+    @Mock
+    private RegisteredServicesCache.ServiceParser mServiceParser;
+    @Captor
+    private ArgumentCaptor<BroadcastReceiver> mReceiverArgumentCaptor;
+    @Captor
+    private ArgumentCaptor<IntentFilter> mIntentFilterArgumentCaptor;
+    @Captor
+    private ArgumentCaptor<PackageManager.ResolveInfoFlags> mFlagArgumentCaptor;
+    @Captor
+    private ArgumentCaptor<Intent> mIntentArgumentCaptor;
+    @Captor
+    private ArgumentCaptor<List<ApduServiceInfo>> mApduServiceListCaptor;
+
+    private MockitoSession mStaticMockSession;
+    private RegisteredServicesCache mRegisteredServicesCache;
+    private Map<String, ApduServiceInfo> mMappedServices;
+
+    @Before
+    public void setUp() throws PackageManager.NameNotFoundException, XmlPullParserException,
+            IOException {
+        mStaticMockSession = ExtendedMockito.mockitoSession()
+                .mockStatic(com.android.nfc.flags.Flags.class)
+                .mockStatic(ActivityManager.class)
+                .mockStatic(NfcStatsLog.class)
+                .mockStatic(UserHandle.class)
+                .mockStatic(NfcAdapter.class)
+                .mockStatic(NfcService.class)
+                .strictness(Strictness.LENIENT)
+                .startMocking();
+        MockitoAnnotations.initMocks(this);
+        mMappedServices = new HashMap<>();
+        when(UserHandle.getUserHandleForUid(eq(USER_ID))).thenReturn(USER_HANDLE);
+        when(UserHandle.of(eq(USER_ID))).thenReturn(USER_HANDLE);
+        when(ActivityManager.getCurrentUser()).thenReturn(USER_ID);
+        when(mContext.getSystemService(eq(UserManager.class))).thenReturn(mUserManager);
+        when(mContext.getFilesDir()).thenReturn(DIR);
+        when(mContext.createContextAsUser(
+                any(), anyInt())).thenReturn(mContext);
+        when(mContext.createPackageContextAsUser(
+                any(), anyInt(), any())).thenReturn(mContext);
+        when(mContext.getPackageManager()).thenReturn(mPackageManager);
+        List<UserHandle> enabledProfiles = new ArrayList<>();
+        enabledProfiles.add(USER_HANDLE);
+        enabledProfiles.add(USER_HANDLE_QUITE_MODE);
+        when(mUserManager.getEnabledProfiles()).thenReturn(enabledProfiles);
+        when(mUserManager.isQuietModeEnabled(eq(USER_HANDLE))).thenReturn(false);
+        when(mUserManager.isQuietModeEnabled(eq(USER_HANDLE_QUITE_MODE))).thenReturn(true);
+        List<ResolveInfo> onHostServicesList = new ArrayList<>();
+        onHostServicesList.add(createServiceResolveInfo(true,
+                WALLET_HOLDER_PACKAGE_NAME, ON_HOST_SERVICE_NAME,
+                List.of(CardEmulation.CATEGORY_PAYMENT)));
+        onHostServicesList.add(createServiceResolveInfo(false,
+                NON_PAYMENT_NFC_PACKAGE_NAME, NON_PAYMENT_SERVICE_NAME,
+                List.of(CardEmulation.CATEGORY_OTHER)));
+        List<ResolveInfo> offHostServicesList = new ArrayList<>();
+        offHostServicesList.add(createServiceResolveInfo(true, ANOTHER_PACKAGE_NAME,
+                OFF_HOST_SERVICE_NAME, List.of(CardEmulation.CATEGORY_OTHER)));
+        when(mPackageManager.queryIntentServicesAsUser(
+                any(), any(), any())).thenAnswer(invocation -> {
+                    Intent intent = invocation.getArgument(0);
+                    if(intent.getAction().equals(OffHostApduService.SERVICE_INTERFACE)) {
+                        return offHostServicesList;
+                    }
+                    if(intent.getAction().equals(HostApduService.SERVICE_INTERFACE)) {
+                        return onHostServicesList;
+                    }
+                    return List.of();
+                });
+        when(mServiceParser.parseApduService(any(), any(), anyBoolean()))
+                .thenAnswer(invocation -> {
+                    ResolveInfo resolveInfo = invocation.getArgument(1);
+                    if(mMappedServices.containsKey(resolveInfo.serviceInfo.name)) {
+                        return mMappedServices.get(resolveInfo.serviceInfo.name);
+                    }
+                    return null;
+                });
+    }
+
+    @After
+    public void tearDown() {
+        mStaticMockSession.finishMocking();
+    }
+
+    // Intent filter registration is actually not happening. It's just a mock verification.
+    @SuppressWarnings({"UnspecifiedRegisterReceiverFlag", "GuardedBy"})
+    @Test
+    public void testConstructor() {
+        mRegisteredServicesCache = new RegisteredServicesCache(mContext, mCallback);
+
+        // Verify that the users handles are populated correctly
+        Assert.assertEquals(1, mRegisteredServicesCache.mUserHandles.size());
+        Assert.assertEquals(mRegisteredServicesCache.mUserHandles.get(0), USER_HANDLE);
+        // Verify that broadcast receivers for apk changes are created and registered properly
+        Assert.assertNotNull(mRegisteredServicesCache.mReceiver.get());
+        verify(mContext).createContextAsUser(eq(USER_HANDLE), eq(0));
+        verify(mContext, times(2)).registerReceiverForAllUsers(
+                mReceiverArgumentCaptor.capture(), mIntentFilterArgumentCaptor.capture(),
+                eq(null), eq(null));
+        IntentFilter packageInstallTrackerIntent = mIntentFilterArgumentCaptor
+                .getAllValues().get(0);
+        Assert.assertTrue(packageInstallTrackerIntent.hasAction(Intent.ACTION_PACKAGE_ADDED));
+        Assert.assertTrue(packageInstallTrackerIntent.hasAction(Intent.ACTION_PACKAGE_CHANGED));
+        Assert.assertTrue(packageInstallTrackerIntent.hasAction(Intent.ACTION_PACKAGE_REMOVED));
+        Assert.assertTrue(packageInstallTrackerIntent.hasAction(Intent.ACTION_PACKAGE_REPLACED));
+        Assert.assertTrue(packageInstallTrackerIntent
+                .hasAction(Intent.ACTION_PACKAGE_FIRST_LAUNCH));
+        Assert.assertTrue(packageInstallTrackerIntent.hasAction(Intent.ACTION_PACKAGE_RESTARTED));
+        Assert.assertTrue(packageInstallTrackerIntent
+                .hasDataScheme(RegisteredServicesCache.PACKAGE_DATA));
+        IntentFilter sdCardIntentFilter = mIntentFilterArgumentCaptor.getAllValues().get(1);
+        Assert.assertTrue(sdCardIntentFilter
+                .hasAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE));
+        Assert.assertTrue(sdCardIntentFilter
+                .hasAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE));
+        Assert.assertEquals(mRegisteredServicesCache.mReceiver.get(),
+                mReceiverArgumentCaptor.getAllValues().get(0));
+        Assert.assertEquals(mRegisteredServicesCache.mReceiver.get(),
+                mReceiverArgumentCaptor.getAllValues().get(1));
+        verify(mContext, times(2)).getFilesDir();
+        // Verify that correct file setting directories are set
+        Assert.assertEquals(mRegisteredServicesCache.mDynamicSettingsFile.getBaseFile()
+                        .getParentFile(), DIR);
+        Assert.assertEquals(mRegisteredServicesCache.mDynamicSettingsFile.getBaseFile()
+                        .getAbsolutePath(), DIR + RegisteredServicesCache.AID_XML_PATH);
+        Assert.assertEquals(mRegisteredServicesCache.mOthersFile.getBaseFile().getParentFile(),
+                DIR);
+        Assert.assertEquals(mRegisteredServicesCache.mOthersFile.getBaseFile()
+                .getAbsolutePath(), DIR + RegisteredServicesCache.OTHER_STATUS_PATH);
+    }
+
+    @Test
+    public void testInitialize_filesExist() throws IOException,
+            PackageManager.NameNotFoundException {
+        mRegisteredServicesCache = new RegisteredServicesCache(mContext, mCallback,
+                mDynamicSettingsFile, mOtherSettingsFile, mServiceParser);
+        InputStream dynamicSettingsIs = InstrumentationRegistry.getInstrumentation()
+                .getContext().getResources().getAssets()
+                .open(RegisteredServicesCache.AID_XML_PATH);
+        InputStream otherSettingsIs = InstrumentationRegistry.getInstrumentation()
+                .getContext().getResources().getAssets()
+                .open(RegisteredServicesCache.OTHER_STATUS_PATH);
+        when(mDynamicSettingsFile.openRead()).thenReturn(dynamicSettingsIs);
+        when(mOtherSettingsFile.openRead()).thenReturn(otherSettingsIs);
+        when(mDynamicSettingsFile.exists()).thenReturn(true);
+        when(mOtherSettingsFile.exists()).thenReturn(true);
+
+        mRegisteredServicesCache.initialize();
+
+        // Verify that file operations are called
+        verify(mDynamicSettingsFile).exists();
+        verify(mOtherSettingsFile).exists();
+        verify(mDynamicSettingsFile).openRead();
+        verify(mOtherSettingsFile).openRead();
+        // Verify that user services are read properly
+        Assert.assertEquals(1, mRegisteredServicesCache.mUserServices.size());
+        RegisteredServicesCache.UserServices userServices
+                = mRegisteredServicesCache.mUserServices.get(USER_ID);
+        Assert.assertEquals(2, userServices.services.size());
+        Assert.assertTrue(userServices.services.containsKey(WALLET_HOLDER_SERVICE_COMPONENT));
+        Assert.assertTrue(userServices.services.containsKey(ANOTHER_SERVICE_COMPONENT));
+        Assert.assertEquals(3, userServices.dynamicSettings.size());
+        // Verify that dynamic settings are read properly
+        Assert.assertTrue(userServices.dynamicSettings
+                .containsKey(WALLET_HOLDER_SERVICE_COMPONENT));
+        Assert.assertTrue(userServices.dynamicSettings.containsKey(NON_PAYMENT_SERVICE_COMPONENT));
+        // Verify that dynamic settings are properly populated for each service in the xml
+        // Verify the details of service 1
+        RegisteredServicesCache.DynamicSettings walletHolderSettings =
+                userServices.dynamicSettings.get(WALLET_HOLDER_SERVICE_COMPONENT);
+        Assert.assertEquals(OFFHOST_SE_STRING+"1", walletHolderSettings.offHostSE);
+        Assert.assertEquals(1, walletHolderSettings.uid);
+        Assert.assertEquals(TRUE_STRING, walletHolderSettings.shouldDefaultToObserveModeStr);
+        Assert.assertTrue(walletHolderSettings.aidGroups
+                .containsKey(CardEmulation.CATEGORY_PAYMENT));
+        Assert.assertTrue(walletHolderSettings.aidGroups.get(CardEmulation.CATEGORY_PAYMENT)
+                        .getAids().containsAll(PAYMENT_AIDS));
+        Assert.assertFalse(walletHolderSettings.aidGroups
+                .containsKey(CardEmulation.CATEGORY_OTHER));
+        // Verify the details of service 2
+        RegisteredServicesCache.DynamicSettings nonPaymentSettings =
+                userServices.dynamicSettings.get(NON_PAYMENT_SERVICE_COMPONENT);
+        Assert.assertEquals(OFFHOST_SE_STRING+"2", nonPaymentSettings.offHostSE);
+        Assert.assertEquals(1, nonPaymentSettings.uid);
+        Assert.assertEquals(FALSE_STRING, nonPaymentSettings.shouldDefaultToObserveModeStr);
+        Assert.assertTrue(nonPaymentSettings.aidGroups
+                .containsKey(CardEmulation.CATEGORY_OTHER));
+        Assert.assertTrue(nonPaymentSettings.aidGroups.get(CardEmulation.CATEGORY_OTHER)
+                .getAids().containsAll(NON_PAYMENT_AID));
+        // Verify that other settings are read properly
+        Assert.assertEquals(1, userServices.others.size());
+        Assert.assertTrue(userServices.others.containsKey(ANOTHER_SERVICE_COMPONENT));
+        RegisteredServicesCache.OtherServiceStatus otherServiceStatus
+                = userServices.others.get(ANOTHER_SERVICE_COMPONENT);
+        Assert.assertTrue(otherServiceStatus.checked);
+        Assert.assertEquals(1, otherServiceStatus.uid);
+        // Verify that the installed services are populated properly
+        verify(mContext)
+                .createPackageContextAsUser(eq(ANDROID_STRING), eq(0), eq(USER_HANDLE));
+        verify(mContext).getPackageManager();
+        verify(mPackageManager, times(2))
+                .queryIntentServicesAsUser(mIntentArgumentCaptor.capture(),
+                        mFlagArgumentCaptor.capture(), eq(USER_HANDLE));
+        Intent onHostIntent = mIntentArgumentCaptor.getAllValues().get(0);
+        Assert.assertEquals(HostApduService.SERVICE_INTERFACE, onHostIntent.getAction());
+        Intent offHostIntent = mIntentArgumentCaptor.getAllValues().get(1);
+        Assert.assertEquals(OffHostApduService.SERVICE_INTERFACE, offHostIntent.getAction());
+        PackageManager.ResolveInfoFlags onHostFlag = mFlagArgumentCaptor.getAllValues().get(0);
+        Assert.assertEquals(PackageManager.GET_META_DATA, onHostFlag.getValue());
+        PackageManager.ResolveInfoFlags offHostFlag = mFlagArgumentCaptor.getAllValues().get(1);
+        Assert.assertEquals(PackageManager.GET_META_DATA, offHostFlag.getValue());
+        // Verify that the installed services are filtered properly
+        verify(mPackageManager).checkPermission(eq(android.Manifest.permission.NFC),
+                eq(WALLET_HOLDER_PACKAGE_NAME));
+        verify(mPackageManager).checkPermission(eq(android.Manifest.permission.NFC),
+                eq(NON_PAYMENT_NFC_PACKAGE_NAME));
+        verify(mPackageManager).checkPermission(eq(android.Manifest.permission.NFC),
+                eq(ANOTHER_PACKAGE_NAME));
+        // Verify that the callback is called with properly installed and filtered services.
+        verify(mCallback).onServicesUpdated(eq(USER_ID), mApduServiceListCaptor.capture(),
+                eq(false));
+        List<ApduServiceInfo> apduServiceInfos = mApduServiceListCaptor.getValue();
+        Assert.assertEquals(2, apduServiceInfos.size());
+        Assert.assertEquals(WALLET_HOLDER_SERVICE_COMPONENT, apduServiceInfos.get(0)
+                .getComponent());
+        Assert.assertEquals(ANOTHER_SERVICE_COMPONENT, apduServiceInfos.get(1).getComponent());
+    }
+
+    private ResolveInfo createServiceResolveInfo(boolean hasPermission,
+                                                 String packageName, String className,
+                                                 List<String> categories) {
+        when(mPackageManager.checkPermission(any(), eq(packageName)))
+                .thenReturn(hasPermission ? PackageManager.PERMISSION_GRANTED
+                        : PackageManager.PERMISSION_DENIED);
+        ApduServiceInfo apduServiceInfo = Mockito.mock(ApduServiceInfo.class);
+        when(apduServiceInfo.getComponent()).thenReturn(new ComponentName(packageName, className));
+        if (!categories.isEmpty()) {
+            for(String category : categories) {
+               when(apduServiceInfo.hasCategory(category)).thenReturn(true);
+            }
+        }
+        mMappedServices.put(className, apduServiceInfo);
+
+        ResolveInfo resolveInfo = new ResolveInfo();
+        resolveInfo.serviceInfo = new ServiceInfo();
+        resolveInfo.serviceInfo.permission = android.Manifest.permission.BIND_NFC_SERVICE;
+        resolveInfo.serviceInfo.exported = true;
+        resolveInfo.serviceInfo.packageName = packageName;
+        resolveInfo.serviceInfo.name = className;
+        return resolveInfo;
+    }
+}
diff --git a/tests/unit/src/com/android/nfc/cardemulation/WalletRoleObserverTest.java b/tests/unit/src/com/android/nfc/cardemulation/WalletRoleObserverTest.java
new file mode 100644
index 0000000..04f42b7
--- /dev/null
+++ b/tests/unit/src/com/android/nfc/cardemulation/WalletRoleObserverTest.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.nfc.cardemulation;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import android.app.role.RoleManager;
+import android.content.Context;
+import android.os.UserHandle;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.google.common.collect.ImmutableList;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+
+
+import java.util.List;
+import java.util.concurrent.Executor;
+
+@RunWith(AndroidJUnit4.class)
+public class WalletRoleObserverTest {
+
+    private static final int USER_ID = 1;
+    private static final UserHandle USER_HANDLE = UserHandle.of(USER_ID);
+
+    private static final String WALLET_ROLE_HOLDER = "gms.wallet.stuff";
+
+    WalletRoleObserver mWalletRoleObserver;
+    @Mock
+    Context mContext;
+    @Mock
+    RoleManager mRoleManager;
+    @Mock
+    WalletRoleObserver.Callback mCallback;
+    @Mock
+    Executor mExecutor;
+    @Captor
+    ArgumentCaptor<String> mRoleNameCaptor;
+    @Captor
+    ArgumentCaptor<UserHandle> mUserHandlerCaptor;
+    @Captor
+    ArgumentCaptor<Executor> mExecutorCaptor;
+    @Captor
+    ArgumentCaptor<String> mRoleHolderCaptor;
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        when(mContext.getMainExecutor()).thenReturn(mExecutor);
+        mWalletRoleObserver = new WalletRoleObserver(mContext, mRoleManager, mCallback);
+    }
+
+    @Test
+    public void testConstructor() {
+        verify(mContext).getMainExecutor();
+        verify(mRoleManager).addOnRoleHoldersChangedListenerAsUser(mExecutorCaptor.capture(),any(),
+                mUserHandlerCaptor.capture());
+        Assert.assertEquals(mExecutor, mExecutorCaptor.getValue());
+        Assert.assertEquals(UserHandle.ALL, mUserHandlerCaptor.getValue());
+    }
+
+    @Test
+    public void testGetDefaultWalletRoleHolder_roleAvailable_returnsTheHolder() {
+        List<String> roleHolders = ImmutableList.of(WALLET_ROLE_HOLDER);
+        when(mRoleManager.isRoleAvailable(eq(RoleManager.ROLE_WALLET))).thenReturn(true);
+        when(mRoleManager.getRoleHoldersAsUser(eq(RoleManager.ROLE_WALLET),
+                eq(USER_HANDLE))).thenReturn(roleHolders);
+
+        String roleHolder = mWalletRoleObserver.getDefaultWalletRoleHolder(USER_ID);
+
+        verify(mRoleManager).isRoleAvailable(mRoleNameCaptor.capture());
+        verify(mRoleManager).getRoleHoldersAsUser(mRoleNameCaptor.capture(),
+                mUserHandlerCaptor.capture());
+        Assert.assertEquals(roleHolder, WALLET_ROLE_HOLDER);
+        Assert.assertEquals(RoleManager.ROLE_WALLET, mRoleNameCaptor.getAllValues().get(0));
+        Assert.assertEquals(RoleManager.ROLE_WALLET, mRoleNameCaptor.getAllValues().get(1));
+    }
+
+    @Test
+    public void testGetDefaultWalletRoleHolder_roleNotAvailable_returnsNull() {
+        when(mRoleManager.isRoleAvailable(eq(RoleManager.ROLE_WALLET))).thenReturn(false);
+
+        String roleHolder = mWalletRoleObserver.getDefaultWalletRoleHolder(USER_ID);
+
+        Assert.assertNull(roleHolder);
+    }
+
+    @Test
+    public void testCallbackFiringOnRoleChange_roleWallet() {
+        List<String> roleHolders = ImmutableList.of(WALLET_ROLE_HOLDER);
+        when(mRoleManager.getRoleHolders(eq(RoleManager.ROLE_WALLET))).thenReturn(roleHolders);
+        mWalletRoleObserver.mOnRoleHoldersChangedListener
+                .onRoleHoldersChanged(RoleManager.ROLE_WALLET, USER_HANDLE);
+
+        verify(mRoleManager).getRoleHolders(mRoleNameCaptor.capture());
+        verify(mCallback).onWalletRoleHolderChanged(mRoleHolderCaptor.capture(), eq(USER_ID));
+        Assert.assertEquals(RoleManager.ROLE_WALLET, mRoleNameCaptor.getValue());
+        Assert.assertEquals(WALLET_ROLE_HOLDER, mRoleHolderCaptor.getValue());
+    }
+
+    @Test
+    public void testCallbackNotFiringOnRoleChange_roleNonWallet() {
+        mWalletRoleObserver.mOnRoleHoldersChangedListener
+                .onRoleHoldersChanged(RoleManager.ROLE_ASSISTANT, USER_HANDLE);
+
+        verifyZeroInteractions(mCallback);
+    }
+
+    @Test
+    public void testOnUserSwitched_callsCallback() {
+        List<String> roleHolders = ImmutableList.of(WALLET_ROLE_HOLDER);
+        when(mRoleManager.isRoleAvailable(eq(RoleManager.ROLE_WALLET))).thenReturn(true);
+        when(mRoleManager.getRoleHoldersAsUser(eq(RoleManager.ROLE_WALLET),
+                eq(USER_HANDLE))).thenReturn(roleHolders);
+
+        mWalletRoleObserver.onUserSwitched(USER_ID);
+        verify(mRoleManager).isRoleAvailable(mRoleNameCaptor.capture());
+        verify(mRoleManager).getRoleHoldersAsUser(mRoleNameCaptor.capture(),
+                mUserHandlerCaptor.capture());
+        verify(mCallback).onWalletRoleHolderChanged(mRoleHolderCaptor.capture(), eq(USER_ID));
+        Assert.assertEquals(WALLET_ROLE_HOLDER, mRoleHolderCaptor.getValue());
+        Assert.assertEquals(RoleManager.ROLE_WALLET, mRoleNameCaptor.getAllValues().get(0));
+        Assert.assertEquals(RoleManager.ROLE_WALLET, mRoleNameCaptor.getAllValues().get(1));
+    }
+
+}
diff --git a/tests/unit/src/com/android/nfc/cardemulation/util/StatsdUtilsTest.java b/tests/unit/src/com/android/nfc/cardemulation/util/StatsdUtilsTest.java
new file mode 100644
index 0000000..6744b2e
--- /dev/null
+++ b/tests/unit/src/com/android/nfc/cardemulation/util/StatsdUtilsTest.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.nfc.cardemulation.util;
+
+import static android.nfc.cardemulation.PollingFrame.POLLING_LOOP_TYPE_UNKNOWN;
+
+import static com.android.nfc.NfcStatsLog.NFC_POLLING_LOOP_NOTIFICATION_REPORTED__PROPRIETARY_FRAME_TYPE__ECP_V1;
+import static com.android.nfc.NfcStatsLog.NFC_POLLING_LOOP_NOTIFICATION_REPORTED__PROPRIETARY_FRAME_TYPE__ECP_V2;
+import static com.android.nfc.NfcStatsLog.NFC_POLLING_LOOP_NOTIFICATION_REPORTED__PROPRIETARY_FRAME_TYPE__PROPRIETARY_FRAME_UNKNOWN;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.nfc.cardemulation.PollingFrame;
+import android.os.Bundle;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import java.util.HexFormat;
+import java.util.Locale;
+
+@RunWith(AndroidJUnit4.class)
+public class StatsdUtilsTest {
+    private final StatsdUtils mStatsdUtils = spy(new StatsdUtils());
+
+    @Test
+    public void testGetFrameType() {
+        assertThat(StatsdUtils.getFrameType(ECP_V1_PAYMENT)).isEqualTo(
+                NFC_POLLING_LOOP_NOTIFICATION_REPORTED__PROPRIETARY_FRAME_TYPE__ECP_V1);
+
+        assertThat(StatsdUtils.getFrameType(ECP_V2_TRANSIT_MBTA)).isEqualTo(
+                NFC_POLLING_LOOP_NOTIFICATION_REPORTED__PROPRIETARY_FRAME_TYPE__ECP_V2);
+
+        assertThat(StatsdUtils.getFrameType(UNKNOWN_FRAME)).isEqualTo(
+                NFC_POLLING_LOOP_NOTIFICATION_REPORTED__PROPRIETARY_FRAME_TYPE__PROPRIETARY_FRAME_UNKNOWN);
+    }
+
+    @Test
+    public void testLogPollingFrame_ecp1Once() {
+        PollingFrame frameData =
+                new PollingFrame(POLLING_LOOP_TYPE_UNKNOWN, ECP_V1_PAYMENT, -1, 0, false);
+
+        mStatsdUtils.tallyPollingFrame(ECP_V1_PAYMENT_KEY, frameData);
+        mStatsdUtils.logPollingFrames();
+
+        StatsdUtils.PollingFrameLog expectedFrame = new StatsdUtils.PollingFrameLog(ECP_V1_PAYMENT);
+        expectedFrame.repeatCount = 1;
+
+        verify(mStatsdUtils).writeToStatsd(expectedFrame);
+    }
+
+    @Test
+    public void testLogPollingFrame_ecp1TwiceInTwoWrites() {
+        PollingFrame frameData =
+                new PollingFrame(POLLING_LOOP_TYPE_UNKNOWN, ECP_V1_PAYMENT, -1, 0, false);
+
+        mStatsdUtils.tallyPollingFrame(ECP_V1_PAYMENT_KEY, frameData);
+        mStatsdUtils.logPollingFrames();
+        mStatsdUtils.tallyPollingFrame(ECP_V1_PAYMENT_KEY, frameData);
+        mStatsdUtils.logPollingFrames();
+
+        StatsdUtils.PollingFrameLog expectedFrame = new StatsdUtils.PollingFrameLog(ECP_V1_PAYMENT);
+        expectedFrame.repeatCount = 1;
+
+        verify(mStatsdUtils, times(2)).tallyPollingFrame(any(), any());
+        verify(mStatsdUtils, times(2)).logPollingFrames();
+        verify(mStatsdUtils, times(2)).writeToStatsd(expectedFrame);
+        verifyNoMoreInteractions(mStatsdUtils);
+    }
+
+    @Test
+    public void testLogPollingFrame_ecp2Repeated() {
+        PollingFrame frameData =
+                new PollingFrame(POLLING_LOOP_TYPE_UNKNOWN, ECP_V2_TRANSIT_MBTA, -1, 0, false);
+
+        mStatsdUtils.tallyPollingFrame(ECP_V2_TRANSIT_MBTA_KEY, frameData);
+        mStatsdUtils.tallyPollingFrame(ECP_V2_TRANSIT_MBTA_KEY, frameData);
+        mStatsdUtils.tallyPollingFrame(ECP_V2_TRANSIT_MBTA_KEY, frameData);
+
+        mStatsdUtils.logPollingFrames();
+
+        StatsdUtils.PollingFrameLog expectedFrame =
+                new StatsdUtils.PollingFrameLog(ECP_V2_TRANSIT_MBTA);
+        expectedFrame.repeatCount = 3;
+        verify(mStatsdUtils).writeToStatsd(expectedFrame);
+    }
+
+    @Test
+    public void testLogPollingFrame_ecp2RepeatedTwoTypes() {
+        PollingFrame frame1Data =
+                new PollingFrame(POLLING_LOOP_TYPE_UNKNOWN, UNKNOWN_FRAME, -1, 0, false);
+
+        PollingFrame frame2Data =
+                new PollingFrame(POLLING_LOOP_TYPE_UNKNOWN, ECP_V2_TRANSIT_MBTA, -1, 0, false);
+
+        mStatsdUtils.tallyPollingFrame(UNKNOWN_FRAME_KEY, frame1Data);
+        mStatsdUtils.tallyPollingFrame(ECP_V2_TRANSIT_MBTA_KEY, frame2Data);
+        mStatsdUtils.tallyPollingFrame(UNKNOWN_FRAME_KEY, frame1Data);
+        mStatsdUtils.logPollingFrames();
+
+        StatsdUtils.PollingFrameLog expectedFrame = new StatsdUtils.PollingFrameLog(UNKNOWN_FRAME);
+        expectedFrame.repeatCount = 2;
+        verify(mStatsdUtils).writeToStatsd(expectedFrame);
+
+        expectedFrame = new StatsdUtils.PollingFrameLog(ECP_V2_TRANSIT_MBTA);
+        expectedFrame.repeatCount = 1;
+
+        verify(mStatsdUtils, times(3)).tallyPollingFrame(any(), any());
+        verify(mStatsdUtils).logPollingFrames();
+        verify(mStatsdUtils).writeToStatsd(expectedFrame);
+        verifyNoMoreInteractions(mStatsdUtils);
+    }
+
+    @Test
+    public void testFieldGain() {
+        PollingFrame frame1Data =
+                new PollingFrame(POLLING_LOOP_TYPE_UNKNOWN, UNKNOWN_FRAME, GAIN_1, 0, false);
+
+        PollingFrame frame2Data =
+                new PollingFrame(POLLING_LOOP_TYPE_UNKNOWN, UNKNOWN_FRAME, GAIN_1, 0, false);
+
+        PollingFrame frame3Data =
+                new PollingFrame(POLLING_LOOP_TYPE_UNKNOWN, UNKNOWN_FRAME, GAIN_2, 0, false);
+
+        mStatsdUtils.tallyPollingFrame(UNKNOWN_FRAME_KEY, frame1Data);
+        mStatsdUtils.tallyPollingFrame(UNKNOWN_FRAME_KEY, frame2Data);
+        mStatsdUtils.tallyPollingFrame(UNKNOWN_FRAME_KEY, frame3Data);
+        mStatsdUtils.logPollingFrames();
+
+        verify(mStatsdUtils, times(3)).tallyPollingFrame(any(), any());
+        verify(mStatsdUtils).logPollingFrames();
+        verify(mStatsdUtils, times(1)).logFieldChanged(true, GAIN_1);
+        verify(mStatsdUtils, times(1)).logFieldChanged(true, GAIN_2);
+        verify(mStatsdUtils).writeToStatsd(any());
+        verifyNoMoreInteractions(mStatsdUtils);
+    }
+
+
+    private static final int GAIN_1 = 42;
+    private static final int GAIN_2 = 25;
+    private static final byte[] ECP_V1_PAYMENT = new byte[]{0x6a, 0x01, 0x00, 0x00, 0x00};
+    private static final String ECP_V1_PAYMENT_KEY =
+            HexFormat.of().formatHex(ECP_V1_PAYMENT).toUpperCase(Locale.ROOT);
+    private static final byte[] ECP_V2_TRANSIT_MBTA =
+            new byte[]{0x6a, 0x02, (byte) 0xc8, 0x01, 0x00, 0x03, 0x00, 0x03, 0x7f, 0x00, 0x00,
+                    0x00, 0x00, 0x71, (byte) 0xe7};
+    private static final String ECP_V2_TRANSIT_MBTA_KEY =
+            HexFormat.of().formatHex(ECP_V2_TRANSIT_MBTA).toUpperCase(Locale.ROOT);
+
+    private static final byte[] UNKNOWN_FRAME =
+            new byte[]{0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
+                    0x21};
+    private static final String UNKNOWN_FRAME_KEY =
+            HexFormat.of().formatHex(UNKNOWN_FRAME).toUpperCase(Locale.ROOT);
+}