nfc(api): Move nfc classes to framework-nfc

Splits out the NFC API classes that are going to be part of NFC mainline
module.

Note: These files will be eventually moved out to packages/modules/Nfc
at some point.

Bug: 303286040
Test: Device boots up after flashing
Test: atest CtsNfcTestCases

Change-Id: I41c1146401236963b9fd83f214fed0b6cecf325e
diff --git a/nfc/Android.bp b/nfc/Android.bp
index 4333374..3c2efe7 100644
--- a/nfc/Android.bp
+++ b/nfc/Android.bp
@@ -10,7 +10,15 @@
 filegroup {
     name: "framework-nfc-non-updatable-sources",
     path: "java",
-    srcs: [],
+    srcs: [
+        "java/android/nfc/NfcServiceManager.java",
+        "java/android/nfc/cardemulation/ApduServiceInfo.aidl",
+        "java/android/nfc/cardemulation/ApduServiceInfo.java",
+        "java/android/nfc/cardemulation/NfcFServiceInfo.aidl",
+        "java/android/nfc/cardemulation/NfcFServiceInfo.java",
+        "java/android/nfc/cardemulation/AidGroup.aidl",
+        "java/android/nfc/cardemulation/AidGroup.java",
+    ],
 }
 
 filegroup {
@@ -30,12 +38,17 @@
     libs: [
         "unsupportedappusage", // for android.compat.annotation.UnsupportedAppUsage
     ],
+    static_libs: [
+        "android.nfc.flags-aconfig-java",
+        "android.permission.flags-aconfig-java",
+    ],
     srcs: [
         ":framework-nfc-updatable-sources",
+        ":framework-nfc-javastream-protos",
     ],
     defaults: ["framework-module-defaults"],
     sdk_version: "module_current",
-    min_sdk_version: "VanillaIceCream",
+    min_sdk_version: "34", // should be 35 (making it 34 for compiling for `-next`)
     installable: true,
     optimize: {
         enabled: false,
@@ -48,4 +61,15 @@
     hidden_api_packages: [
         "com.android.nfc",
     ],
+    impl_library_visibility: [
+        "//frameworks/base:__subpackages__",
+        "//cts/tests/tests/nfc",
+        "//packages/apps/Nfc:__subpackages__",
+    ],
+    jarjar_rules: ":nfc-jarjar-rules",
+}
+
+filegroup {
+    name: "nfc-jarjar-rules",
+    srcs: ["jarjar-rules.txt"],
 }
diff --git a/nfc/api/current.txt b/nfc/api/current.txt
index d802177..24c145f 100644
--- a/nfc/api/current.txt
+++ b/nfc/api/current.txt
@@ -1 +1,455 @@
 // Signature format: 2.0
+package android.nfc {
+
+  public final class AvailableNfcAntenna implements android.os.Parcelable {
+    ctor public AvailableNfcAntenna(int, int);
+    method public int describeContents();
+    method public int getLocationX();
+    method public int getLocationY();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.nfc.AvailableNfcAntenna> CREATOR;
+  }
+
+  public class FormatException extends java.lang.Exception {
+    ctor public FormatException();
+    ctor public FormatException(String);
+    ctor public FormatException(String, Throwable);
+  }
+
+  public final class NdefMessage implements android.os.Parcelable {
+    ctor public NdefMessage(byte[]) throws android.nfc.FormatException;
+    ctor public NdefMessage(android.nfc.NdefRecord, android.nfc.NdefRecord...);
+    ctor public NdefMessage(android.nfc.NdefRecord[]);
+    method public int describeContents();
+    method public int getByteArrayLength();
+    method public android.nfc.NdefRecord[] getRecords();
+    method public byte[] toByteArray();
+    method public void writeToParcel(android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.nfc.NdefMessage> CREATOR;
+  }
+
+  public final class NdefRecord implements android.os.Parcelable {
+    ctor public NdefRecord(short, byte[], byte[], byte[]);
+    ctor @Deprecated public NdefRecord(byte[]) throws android.nfc.FormatException;
+    method public static android.nfc.NdefRecord createApplicationRecord(String);
+    method public static android.nfc.NdefRecord createExternal(String, String, byte[]);
+    method public static android.nfc.NdefRecord createMime(String, byte[]);
+    method public static android.nfc.NdefRecord createTextRecord(String, String);
+    method public static android.nfc.NdefRecord createUri(android.net.Uri);
+    method public static android.nfc.NdefRecord createUri(String);
+    method public int describeContents();
+    method public byte[] getId();
+    method public byte[] getPayload();
+    method public short getTnf();
+    method public byte[] getType();
+    method @Deprecated public byte[] toByteArray();
+    method public String toMimeType();
+    method public android.net.Uri toUri();
+    method public void writeToParcel(android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.nfc.NdefRecord> CREATOR;
+    field public static final byte[] RTD_ALTERNATIVE_CARRIER;
+    field public static final byte[] RTD_HANDOVER_CARRIER;
+    field public static final byte[] RTD_HANDOVER_REQUEST;
+    field public static final byte[] RTD_HANDOVER_SELECT;
+    field public static final byte[] RTD_SMART_POSTER;
+    field public static final byte[] RTD_TEXT;
+    field public static final byte[] RTD_URI;
+    field public static final short TNF_ABSOLUTE_URI = 3; // 0x3
+    field public static final short TNF_EMPTY = 0; // 0x0
+    field public static final short TNF_EXTERNAL_TYPE = 4; // 0x4
+    field public static final short TNF_MIME_MEDIA = 2; // 0x2
+    field public static final short TNF_UNCHANGED = 6; // 0x6
+    field public static final short TNF_UNKNOWN = 5; // 0x5
+    field public static final short TNF_WELL_KNOWN = 1; // 0x1
+  }
+
+  public final class NfcAdapter {
+    method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean allowTransaction();
+    method public void disableForegroundDispatch(android.app.Activity);
+    method public void disableReaderMode(android.app.Activity);
+    method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean disallowTransaction();
+    method public void enableForegroundDispatch(android.app.Activity, android.app.PendingIntent, android.content.IntentFilter[], String[][]);
+    method public void enableReaderMode(android.app.Activity, android.nfc.NfcAdapter.ReaderCallback, int, android.os.Bundle);
+    method public static android.nfc.NfcAdapter getDefaultAdapter(android.content.Context);
+    method @Nullable public android.nfc.NfcAntennaInfo getNfcAntennaInfo();
+    method @FlaggedApi("android.nfc.enable_nfc_charging") @Nullable public android.nfc.WlcLDeviceInfo getWlcLDeviceInfo();
+    method public boolean ignore(android.nfc.Tag, int, android.nfc.NfcAdapter.OnTagRemovedListener, android.os.Handler);
+    method public boolean isEnabled();
+    method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean isObserveModeSupported();
+    method @FlaggedApi("android.nfc.enable_nfc_reader_option") public boolean isReaderOptionEnabled();
+    method @FlaggedApi("android.nfc.enable_nfc_reader_option") public boolean isReaderOptionSupported();
+    method public boolean isSecureNfcEnabled();
+    method public boolean isSecureNfcSupported();
+    method @FlaggedApi("android.nfc.enable_nfc_charging") public boolean isWlcEnabled();
+    method @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public void resetDiscoveryTechnology(@NonNull android.app.Activity);
+    method @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public void setDiscoveryTechnology(@NonNull android.app.Activity, int, int);
+    field public static final String ACTION_ADAPTER_STATE_CHANGED = "android.nfc.action.ADAPTER_STATE_CHANGED";
+    field public static final String ACTION_NDEF_DISCOVERED = "android.nfc.action.NDEF_DISCOVERED";
+    field @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public static final String ACTION_PREFERRED_PAYMENT_CHANGED = "android.nfc.action.PREFERRED_PAYMENT_CHANGED";
+    field public static final String ACTION_TAG_DISCOVERED = "android.nfc.action.TAG_DISCOVERED";
+    field public static final String ACTION_TECH_DISCOVERED = "android.nfc.action.TECH_DISCOVERED";
+    field @RequiresPermission(android.Manifest.permission.NFC_TRANSACTION_EVENT) public static final String ACTION_TRANSACTION_DETECTED = "android.nfc.action.TRANSACTION_DETECTED";
+    field public static final String EXTRA_ADAPTER_STATE = "android.nfc.extra.ADAPTER_STATE";
+    field public static final String EXTRA_AID = "android.nfc.extra.AID";
+    field public static final String EXTRA_DATA = "android.nfc.extra.DATA";
+    field public static final String EXTRA_ID = "android.nfc.extra.ID";
+    field public static final String EXTRA_NDEF_MESSAGES = "android.nfc.extra.NDEF_MESSAGES";
+    field public static final String EXTRA_PREFERRED_PAYMENT_CHANGED_REASON = "android.nfc.extra.PREFERRED_PAYMENT_CHANGED_REASON";
+    field public static final String EXTRA_READER_PRESENCE_CHECK_DELAY = "presence";
+    field public static final String EXTRA_SECURE_ELEMENT_NAME = "android.nfc.extra.SECURE_ELEMENT_NAME";
+    field public static final String EXTRA_TAG = "android.nfc.extra.TAG";
+    field @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public static final int FLAG_LISTEN_DISABLE = 0; // 0x0
+    field @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public static final int FLAG_LISTEN_KEEP = -1; // 0xffffffff
+    field @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public static final int FLAG_LISTEN_NFC_PASSIVE_A = 1; // 0x1
+    field @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public static final int FLAG_LISTEN_NFC_PASSIVE_B = 2; // 0x2
+    field @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public static final int FLAG_LISTEN_NFC_PASSIVE_F = 4; // 0x4
+    field @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public static final int FLAG_READER_DISABLE = 0; // 0x0
+    field @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public static final int FLAG_READER_KEEP = -1; // 0xffffffff
+    field public static final int FLAG_READER_NFC_A = 1; // 0x1
+    field public static final int FLAG_READER_NFC_B = 2; // 0x2
+    field public static final int FLAG_READER_NFC_BARCODE = 16; // 0x10
+    field public static final int FLAG_READER_NFC_F = 4; // 0x4
+    field public static final int FLAG_READER_NFC_V = 8; // 0x8
+    field public static final int FLAG_READER_NO_PLATFORM_SOUNDS = 256; // 0x100
+    field public static final int FLAG_READER_SKIP_NDEF_CHECK = 128; // 0x80
+    field public static final int PREFERRED_PAYMENT_CHANGED = 2; // 0x2
+    field public static final int PREFERRED_PAYMENT_LOADED = 1; // 0x1
+    field public static final int PREFERRED_PAYMENT_UPDATED = 3; // 0x3
+    field public static final int STATE_OFF = 1; // 0x1
+    field public static final int STATE_ON = 3; // 0x3
+    field public static final int STATE_TURNING_OFF = 4; // 0x4
+    field public static final int STATE_TURNING_ON = 2; // 0x2
+  }
+
+  @Deprecated public static interface NfcAdapter.CreateBeamUrisCallback {
+    method @Deprecated public android.net.Uri[] createBeamUris(android.nfc.NfcEvent);
+  }
+
+  @Deprecated public static interface NfcAdapter.CreateNdefMessageCallback {
+    method @Deprecated public android.nfc.NdefMessage createNdefMessage(android.nfc.NfcEvent);
+  }
+
+  @Deprecated public static interface NfcAdapter.OnNdefPushCompleteCallback {
+    method @Deprecated public void onNdefPushComplete(android.nfc.NfcEvent);
+  }
+
+  public static interface NfcAdapter.OnTagRemovedListener {
+    method public void onTagRemoved();
+  }
+
+  public static interface NfcAdapter.ReaderCallback {
+    method public void onTagDiscovered(android.nfc.Tag);
+  }
+
+  public final class NfcAntennaInfo implements android.os.Parcelable {
+    ctor public NfcAntennaInfo(int, int, boolean, @NonNull java.util.List<android.nfc.AvailableNfcAntenna>);
+    method public int describeContents();
+    method @NonNull public java.util.List<android.nfc.AvailableNfcAntenna> getAvailableNfcAntennas();
+    method public int getDeviceHeight();
+    method public int getDeviceWidth();
+    method public boolean isDeviceFoldable();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.nfc.NfcAntennaInfo> CREATOR;
+  }
+
+  public final class NfcEvent {
+    field public final android.nfc.NfcAdapter nfcAdapter;
+    field public final int peerLlcpMajorVersion;
+    field public final int peerLlcpMinorVersion;
+  }
+
+  public final class NfcManager {
+    method public android.nfc.NfcAdapter getDefaultAdapter();
+  }
+
+  public final class Tag implements android.os.Parcelable {
+    method public int describeContents();
+    method public byte[] getId();
+    method public String[] getTechList();
+    method public void writeToParcel(android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.nfc.Tag> CREATOR;
+  }
+
+  public class TagLostException extends java.io.IOException {
+    ctor public TagLostException();
+    ctor public TagLostException(String);
+  }
+
+  @FlaggedApi("android.nfc.enable_nfc_charging") public final class WlcLDeviceInfo implements android.os.Parcelable {
+    ctor public WlcLDeviceInfo(double, double, double, int);
+    method public int describeContents();
+    method public double getBatteryLevel();
+    method public double getProductId();
+    method public int getState();
+    method public double getTemperature();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field public static final int CONNECTED_CHARGING = 2; // 0x2
+    field public static final int CONNECTED_DISCHARGING = 3; // 0x3
+    field @NonNull public static final android.os.Parcelable.Creator<android.nfc.WlcLDeviceInfo> CREATOR;
+    field public static final int DISCONNECTED = 1; // 0x1
+  }
+
+}
+
+package android.nfc.cardemulation {
+
+  public final class CardEmulation {
+    method public boolean categoryAllowsForegroundPreference(String);
+    method @Nullable @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public java.util.List<java.lang.String> getAidsForPreferredPaymentService();
+    method public java.util.List<java.lang.String> getAidsForService(android.content.ComponentName, String);
+    method @Nullable @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public CharSequence getDescriptionForPreferredPaymentService();
+    method public static android.nfc.cardemulation.CardEmulation getInstance(android.nfc.NfcAdapter);
+    method @Nullable @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public String getRouteDestinationForPreferredPaymentService();
+    method public int getSelectionModeForCategory(String);
+    method public boolean isDefaultServiceForAid(android.content.ComponentName, String);
+    method public boolean isDefaultServiceForCategory(android.content.ComponentName, String);
+    method public boolean registerAidsForService(android.content.ComponentName, String, java.util.List<java.lang.String>);
+    method public boolean removeAidsForService(android.content.ComponentName, String);
+    method @NonNull @RequiresPermission(android.Manifest.permission.NFC) public boolean setOffHostForService(@NonNull android.content.ComponentName, @NonNull String);
+    method public boolean setPreferredService(android.app.Activity, android.content.ComponentName);
+    method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean setServiceObserveModeDefault(@NonNull android.content.ComponentName, boolean);
+    method public boolean supportsAidPrefixRegistration();
+    method @NonNull @RequiresPermission(android.Manifest.permission.NFC) public boolean unsetOffHostForService(@NonNull android.content.ComponentName);
+    method public boolean unsetPreferredService(android.app.Activity);
+    field @Deprecated public static final String ACTION_CHANGE_DEFAULT = "android.nfc.cardemulation.action.ACTION_CHANGE_DEFAULT";
+    field public static final String CATEGORY_OTHER = "other";
+    field public static final String CATEGORY_PAYMENT = "payment";
+    field public static final String EXTRA_CATEGORY = "category";
+    field public static final String EXTRA_SERVICE_COMPONENT = "component";
+    field public static final int SELECTION_MODE_ALWAYS_ASK = 1; // 0x1
+    field public static final int SELECTION_MODE_ASK_IF_CONFLICT = 2; // 0x2
+    field public static final int SELECTION_MODE_PREFER_DEFAULT = 0; // 0x0
+  }
+
+  public abstract class HostApduService extends android.app.Service {
+    ctor public HostApduService();
+    method public final void notifyUnhandled();
+    method public final android.os.IBinder onBind(android.content.Intent);
+    method public abstract void onDeactivated(int);
+    method public abstract byte[] processCommandApdu(byte[], android.os.Bundle);
+    method @FlaggedApi("android.nfc.nfc_read_polling_loop") public void processPollingFrames(@NonNull java.util.List<android.os.Bundle>);
+    method public final void sendResponseApdu(byte[]);
+    field public static final int DEACTIVATION_DESELECTED = 1; // 0x1
+    field public static final int DEACTIVATION_LINK_LOSS = 0; // 0x0
+    field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final String POLLING_LOOP_DATA_KEY = "android.nfc.cardemulation.DATA";
+    field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final String POLLING_LOOP_GAIN_KEY = "android.nfc.cardemulation.GAIN";
+    field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final String POLLING_LOOP_TIMESTAMP_KEY = "android.nfc.cardemulation.TIMESTAMP";
+    field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final char POLLING_LOOP_TYPE_A = 65; // 0x0041 'A'
+    field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final char POLLING_LOOP_TYPE_B = 66; // 0x0042 'B'
+    field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final char POLLING_LOOP_TYPE_F = 70; // 0x0046 'F'
+    field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final String POLLING_LOOP_TYPE_KEY = "android.nfc.cardemulation.TYPE";
+    field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final char POLLING_LOOP_TYPE_OFF = 88; // 0x0058 'X'
+    field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final char POLLING_LOOP_TYPE_ON = 79; // 0x004f 'O'
+    field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final char POLLING_LOOP_TYPE_UNKNOWN = 85; // 0x0055 'U'
+    field public static final String SERVICE_INTERFACE = "android.nfc.cardemulation.action.HOST_APDU_SERVICE";
+    field public static final String SERVICE_META_DATA = "android.nfc.cardemulation.host_apdu_service";
+  }
+
+  public abstract class HostNfcFService extends android.app.Service {
+    ctor public HostNfcFService();
+    method public final android.os.IBinder onBind(android.content.Intent);
+    method public abstract void onDeactivated(int);
+    method public abstract byte[] processNfcFPacket(byte[], android.os.Bundle);
+    method public final void sendResponsePacket(byte[]);
+    field public static final int DEACTIVATION_LINK_LOSS = 0; // 0x0
+    field public static final String SERVICE_INTERFACE = "android.nfc.cardemulation.action.HOST_NFCF_SERVICE";
+    field public static final String SERVICE_META_DATA = "android.nfc.cardemulation.host_nfcf_service";
+  }
+
+  public final class NfcFCardEmulation {
+    method public boolean disableService(android.app.Activity) throws java.lang.RuntimeException;
+    method public boolean enableService(android.app.Activity, android.content.ComponentName) throws java.lang.RuntimeException;
+    method public static android.nfc.cardemulation.NfcFCardEmulation getInstance(android.nfc.NfcAdapter);
+    method public String getNfcid2ForService(android.content.ComponentName) throws java.lang.RuntimeException;
+    method public String getSystemCodeForService(android.content.ComponentName) throws java.lang.RuntimeException;
+    method public boolean registerSystemCodeForService(android.content.ComponentName, String) throws java.lang.RuntimeException;
+    method public boolean setNfcid2ForService(android.content.ComponentName, String) throws java.lang.RuntimeException;
+    method public boolean unregisterSystemCodeForService(android.content.ComponentName) throws java.lang.RuntimeException;
+  }
+
+  public abstract class OffHostApduService extends android.app.Service {
+    ctor public OffHostApduService();
+    field public static final String SERVICE_INTERFACE = "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE";
+    field public static final String SERVICE_META_DATA = "android.nfc.cardemulation.off_host_apdu_service";
+  }
+
+}
+
+package android.nfc.tech {
+
+  public final class IsoDep implements android.nfc.tech.TagTechnology {
+    method public void close() throws java.io.IOException;
+    method public void connect() throws java.io.IOException;
+    method public static android.nfc.tech.IsoDep get(android.nfc.Tag);
+    method public byte[] getHiLayerResponse();
+    method public byte[] getHistoricalBytes();
+    method public int getMaxTransceiveLength();
+    method public android.nfc.Tag getTag();
+    method public int getTimeout();
+    method public boolean isConnected();
+    method public boolean isExtendedLengthApduSupported();
+    method public void setTimeout(int);
+    method public byte[] transceive(byte[]) throws java.io.IOException;
+  }
+
+  public final class MifareClassic implements android.nfc.tech.TagTechnology {
+    method public boolean authenticateSectorWithKeyA(int, byte[]) throws java.io.IOException;
+    method public boolean authenticateSectorWithKeyB(int, byte[]) throws java.io.IOException;
+    method public int blockToSector(int);
+    method public void close() throws java.io.IOException;
+    method public void connect() throws java.io.IOException;
+    method public void decrement(int, int) throws java.io.IOException;
+    method public static android.nfc.tech.MifareClassic get(android.nfc.Tag);
+    method public int getBlockCount();
+    method public int getBlockCountInSector(int);
+    method public int getMaxTransceiveLength();
+    method public int getSectorCount();
+    method public int getSize();
+    method public android.nfc.Tag getTag();
+    method public int getTimeout();
+    method public int getType();
+    method public void increment(int, int) throws java.io.IOException;
+    method public boolean isConnected();
+    method public byte[] readBlock(int) throws java.io.IOException;
+    method public void restore(int) throws java.io.IOException;
+    method public int sectorToBlock(int);
+    method public void setTimeout(int);
+    method public byte[] transceive(byte[]) throws java.io.IOException;
+    method public void transfer(int) throws java.io.IOException;
+    method public void writeBlock(int, byte[]) throws java.io.IOException;
+    field public static final int BLOCK_SIZE = 16; // 0x10
+    field public static final byte[] KEY_DEFAULT;
+    field public static final byte[] KEY_MIFARE_APPLICATION_DIRECTORY;
+    field public static final byte[] KEY_NFC_FORUM;
+    field public static final int SIZE_1K = 1024; // 0x400
+    field public static final int SIZE_2K = 2048; // 0x800
+    field public static final int SIZE_4K = 4096; // 0x1000
+    field public static final int SIZE_MINI = 320; // 0x140
+    field public static final int TYPE_CLASSIC = 0; // 0x0
+    field public static final int TYPE_PLUS = 1; // 0x1
+    field public static final int TYPE_PRO = 2; // 0x2
+    field public static final int TYPE_UNKNOWN = -1; // 0xffffffff
+  }
+
+  public final class MifareUltralight implements android.nfc.tech.TagTechnology {
+    method public void close() throws java.io.IOException;
+    method public void connect() throws java.io.IOException;
+    method public static android.nfc.tech.MifareUltralight get(android.nfc.Tag);
+    method public int getMaxTransceiveLength();
+    method public android.nfc.Tag getTag();
+    method public int getTimeout();
+    method public int getType();
+    method public boolean isConnected();
+    method public byte[] readPages(int) throws java.io.IOException;
+    method public void setTimeout(int);
+    method public byte[] transceive(byte[]) throws java.io.IOException;
+    method public void writePage(int, byte[]) throws java.io.IOException;
+    field public static final int PAGE_SIZE = 4; // 0x4
+    field public static final int TYPE_ULTRALIGHT = 1; // 0x1
+    field public static final int TYPE_ULTRALIGHT_C = 2; // 0x2
+    field public static final int TYPE_UNKNOWN = -1; // 0xffffffff
+  }
+
+  public final class Ndef implements android.nfc.tech.TagTechnology {
+    method public boolean canMakeReadOnly();
+    method public void close() throws java.io.IOException;
+    method public void connect() throws java.io.IOException;
+    method public static android.nfc.tech.Ndef get(android.nfc.Tag);
+    method public android.nfc.NdefMessage getCachedNdefMessage();
+    method public int getMaxSize();
+    method public android.nfc.NdefMessage getNdefMessage() throws android.nfc.FormatException, java.io.IOException;
+    method public android.nfc.Tag getTag();
+    method public String getType();
+    method public boolean isConnected();
+    method public boolean isWritable();
+    method public boolean makeReadOnly() throws java.io.IOException;
+    method public void writeNdefMessage(android.nfc.NdefMessage) throws android.nfc.FormatException, java.io.IOException;
+    field public static final String MIFARE_CLASSIC = "com.nxp.ndef.mifareclassic";
+    field public static final String NFC_FORUM_TYPE_1 = "org.nfcforum.ndef.type1";
+    field public static final String NFC_FORUM_TYPE_2 = "org.nfcforum.ndef.type2";
+    field public static final String NFC_FORUM_TYPE_3 = "org.nfcforum.ndef.type3";
+    field public static final String NFC_FORUM_TYPE_4 = "org.nfcforum.ndef.type4";
+  }
+
+  public final class NdefFormatable implements android.nfc.tech.TagTechnology {
+    method public void close() throws java.io.IOException;
+    method public void connect() throws java.io.IOException;
+    method public void format(android.nfc.NdefMessage) throws android.nfc.FormatException, java.io.IOException;
+    method public void formatReadOnly(android.nfc.NdefMessage) throws android.nfc.FormatException, java.io.IOException;
+    method public static android.nfc.tech.NdefFormatable get(android.nfc.Tag);
+    method public android.nfc.Tag getTag();
+    method public boolean isConnected();
+  }
+
+  public final class NfcA implements android.nfc.tech.TagTechnology {
+    method public void close() throws java.io.IOException;
+    method public void connect() throws java.io.IOException;
+    method public static android.nfc.tech.NfcA get(android.nfc.Tag);
+    method public byte[] getAtqa();
+    method public int getMaxTransceiveLength();
+    method public short getSak();
+    method public android.nfc.Tag getTag();
+    method public int getTimeout();
+    method public boolean isConnected();
+    method public void setTimeout(int);
+    method public byte[] transceive(byte[]) throws java.io.IOException;
+  }
+
+  public final class NfcB implements android.nfc.tech.TagTechnology {
+    method public void close() throws java.io.IOException;
+    method public void connect() throws java.io.IOException;
+    method public static android.nfc.tech.NfcB get(android.nfc.Tag);
+    method public byte[] getApplicationData();
+    method public int getMaxTransceiveLength();
+    method public byte[] getProtocolInfo();
+    method public android.nfc.Tag getTag();
+    method public boolean isConnected();
+    method public byte[] transceive(byte[]) throws java.io.IOException;
+  }
+
+  public final class NfcBarcode implements android.nfc.tech.TagTechnology {
+    method public void close() throws java.io.IOException;
+    method public void connect() throws java.io.IOException;
+    method public static android.nfc.tech.NfcBarcode get(android.nfc.Tag);
+    method public byte[] getBarcode();
+    method public android.nfc.Tag getTag();
+    method public int getType();
+    method public boolean isConnected();
+    field public static final int TYPE_KOVIO = 1; // 0x1
+    field public static final int TYPE_UNKNOWN = -1; // 0xffffffff
+  }
+
+  public final class NfcF implements android.nfc.tech.TagTechnology {
+    method public void close() throws java.io.IOException;
+    method public void connect() throws java.io.IOException;
+    method public static android.nfc.tech.NfcF get(android.nfc.Tag);
+    method public byte[] getManufacturer();
+    method public int getMaxTransceiveLength();
+    method public byte[] getSystemCode();
+    method public android.nfc.Tag getTag();
+    method public int getTimeout();
+    method public boolean isConnected();
+    method public void setTimeout(int);
+    method public byte[] transceive(byte[]) throws java.io.IOException;
+  }
+
+  public final class NfcV implements android.nfc.tech.TagTechnology {
+    method public void close() throws java.io.IOException;
+    method public void connect() throws java.io.IOException;
+    method public static android.nfc.tech.NfcV get(android.nfc.Tag);
+    method public byte getDsfId();
+    method public int getMaxTransceiveLength();
+    method public byte getResponseFlags();
+    method public android.nfc.Tag getTag();
+    method public boolean isConnected();
+    method public byte[] transceive(byte[]) throws java.io.IOException;
+  }
+
+  public interface TagTechnology extends java.io.Closeable {
+    method public void connect() throws java.io.IOException;
+    method public android.nfc.Tag getTag();
+    method public boolean isConnected();
+  }
+
+}
+
diff --git a/nfc/api/module-lib-current.txt b/nfc/api/module-lib-current.txt
index d802177..5ebe911 100644
--- a/nfc/api/module-lib-current.txt
+++ b/nfc/api/module-lib-current.txt
@@ -1 +1,10 @@
 // Signature format: 2.0
+package android.nfc {
+
+  public class NfcFrameworkInitializer {
+    method public static void registerServiceWrappers();
+    method public static void setNfcServiceManager(@NonNull android.nfc.NfcServiceManager);
+  }
+
+}
+
diff --git a/nfc/api/removed.txt b/nfc/api/removed.txt
index d802177..fb82b5d 100644
--- a/nfc/api/removed.txt
+++ b/nfc/api/removed.txt
@@ -1 +1,17 @@
 // Signature format: 2.0
+package android.nfc {
+
+  public final class NfcAdapter {
+    method @Deprecated @android.compat.annotation.UnsupportedAppUsage public void disableForegroundNdefPush(android.app.Activity);
+    method @Deprecated @android.compat.annotation.UnsupportedAppUsage public void enableForegroundNdefPush(android.app.Activity, android.nfc.NdefMessage);
+    method @Deprecated @android.compat.annotation.UnsupportedAppUsage public boolean invokeBeam(android.app.Activity);
+    method @Deprecated @android.compat.annotation.UnsupportedAppUsage public boolean isNdefPushEnabled();
+    method @Deprecated @android.compat.annotation.UnsupportedAppUsage public void setBeamPushUris(android.net.Uri[], android.app.Activity);
+    method @Deprecated @android.compat.annotation.UnsupportedAppUsage public void setBeamPushUrisCallback(android.nfc.NfcAdapter.CreateBeamUrisCallback, android.app.Activity);
+    method @Deprecated @android.compat.annotation.UnsupportedAppUsage public void setNdefPushMessage(android.nfc.NdefMessage, android.app.Activity, android.app.Activity...);
+    method @Deprecated @android.compat.annotation.UnsupportedAppUsage public void setNdefPushMessageCallback(android.nfc.NfcAdapter.CreateNdefMessageCallback, android.app.Activity, android.app.Activity...);
+    method @Deprecated @android.compat.annotation.UnsupportedAppUsage public void setOnNdefPushCompleteCallback(android.nfc.NfcAdapter.OnNdefPushCompleteCallback, android.app.Activity, android.app.Activity...);
+  }
+
+}
+
diff --git a/nfc/api/system-current.txt b/nfc/api/system-current.txt
index d802177..40672a1 100644
--- a/nfc/api/system-current.txt
+++ b/nfc/api/system-current.txt
@@ -1 +1,53 @@
 // Signature format: 2.0
+package android.nfc {
+
+  public final class NfcAdapter {
+    method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean addNfcUnlockHandler(android.nfc.NfcAdapter.NfcUnlockHandler, String[]);
+    method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean disable();
+    method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean disable(boolean);
+    method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enable();
+    method @FlaggedApi("android.nfc.enable_nfc_reader_option") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableReaderOption(boolean);
+    method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableSecureNfc(boolean);
+    method @FlaggedApi("android.nfc.enable_nfc_charging") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableWlc(boolean);
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") public int getAdapterState();
+    method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public java.util.Map<java.lang.String,java.lang.Boolean> getTagIntentAppPreferenceForUser(int);
+    method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean isControllerAlwaysOn();
+    method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean isControllerAlwaysOnSupported();
+    method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean isTagIntentAppPreferenceSupported();
+    method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public void registerControllerAlwaysOnListener(@NonNull java.util.concurrent.Executor, @NonNull android.nfc.NfcAdapter.ControllerAlwaysOnListener);
+    method @FlaggedApi("android.nfc.enable_nfc_charging") public void registerWlcStateListener(@NonNull java.util.concurrent.Executor, @NonNull android.nfc.NfcAdapter.WlcStateListener);
+    method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean removeNfcUnlockHandler(android.nfc.NfcAdapter.NfcUnlockHandler);
+    method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean setControllerAlwaysOn(boolean);
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setReaderMode(boolean);
+    method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public int setTagIntentAppPreferenceForUser(int, @NonNull String, boolean);
+    method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public void unregisterControllerAlwaysOnListener(@NonNull android.nfc.NfcAdapter.ControllerAlwaysOnListener);
+    method @FlaggedApi("android.nfc.enable_nfc_charging") public void unregisterWlcStateListener(@NonNull android.nfc.NfcAdapter.WlcStateListener);
+    field @FlaggedApi("android.nfc.enable_nfc_mainline") public static final String ACTION_REQUIRE_UNLOCK_FOR_NFC = "android.nfc.action.REQUIRE_UNLOCK_FOR_NFC";
+    field public static final int TAG_INTENT_APP_PREF_RESULT_PACKAGE_NOT_FOUND = -1; // 0xffffffff
+    field public static final int TAG_INTENT_APP_PREF_RESULT_SUCCESS = 0; // 0x0
+    field public static final int TAG_INTENT_APP_PREF_RESULT_UNAVAILABLE = -2; // 0xfffffffe
+  }
+
+  public static interface NfcAdapter.ControllerAlwaysOnListener {
+    method public void onControllerAlwaysOnChanged(boolean);
+  }
+
+  public static interface NfcAdapter.NfcUnlockHandler {
+    method public boolean onUnlockAttempted(android.nfc.Tag);
+  }
+
+  @FlaggedApi("android.nfc.enable_nfc_charging") public static interface NfcAdapter.WlcStateListener {
+    method public void onWlcStateChanged(@NonNull android.nfc.WlcLDeviceInfo);
+  }
+
+}
+
+package android.nfc.cardemulation {
+
+  public final class CardEmulation {
+    method @FlaggedApi("android.permission.flags.wallet_role_enabled") @Nullable @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public android.nfc.cardemulation.ApduServiceInfo getPreferredPaymentService();
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public java.util.List<android.nfc.cardemulation.ApduServiceInfo> getServices(@NonNull String, int);
+  }
+
+}
+
diff --git a/nfc/api/system-removed.txt b/nfc/api/system-removed.txt
index d802177..c6eaa57 100644
--- a/nfc/api/system-removed.txt
+++ b/nfc/api/system-removed.txt
@@ -1 +1,12 @@
 // Signature format: 2.0
+package android.nfc {
+
+  public final class NfcAdapter {
+    method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean disableNdefPush();
+    method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableNdefPush();
+    method public void setNdefPushMessage(android.nfc.NdefMessage, android.app.Activity, int);
+    field public static final int FLAG_NDEF_PUSH_NO_CONFIRM = 1; // 0x1
+  }
+
+}
+
diff --git a/nfc/jarjar-rules.txt b/nfc/jarjar-rules.txt
new file mode 100644
index 0000000..4cd652d
--- /dev/null
+++ b/nfc/jarjar-rules.txt
@@ -0,0 +1,38 @@
+# Used by framework-nfc 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
+
+# Used by framework-nfc for reading trunk stable flags
+rule android.nfc.FakeFeatureFlagsImpl* com.android.nfc.x.@0
+rule android.nfc.FeatureFlags* com.android.nfc.x.@0
+rule android.nfc.Flags* com.android.nfc.x.@0
+rule android.permission.flags.** com.android.nfc.x.@0
+
+# Used by framework-nfc for misc utilities
+rule android.os.PatternMatcher* com.android.nfc.x.@0
+
+rule com.android.incident.Privacy* com.android.nfc.x.@0
+rule com.android.incident.PrivacyFlags* com.android.nfc.x.@0
diff --git a/nfc/java/android/nfc/ApduList.aidl b/nfc/java/android/nfc/ApduList.aidl
new file mode 100644
index 0000000..f6236b2
--- /dev/null
+++ b/nfc/java/android/nfc/ApduList.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc;
+
+parcelable ApduList;
\ No newline at end of file
diff --git a/nfc/java/android/nfc/ApduList.java b/nfc/java/android/nfc/ApduList.java
new file mode 100644
index 0000000..027141d
--- /dev/null
+++ b/nfc/java/android/nfc/ApduList.java
@@ -0,0 +1,68 @@
+package android.nfc;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @hide
+ */
+public class ApduList implements Parcelable {
+
+    private ArrayList<byte[]> commands = new ArrayList<byte[]>();
+
+    public ApduList() {
+    }
+
+    public void add(byte[] command) {
+        commands.add(command);
+    }
+
+    public List<byte[]> get() {
+        return commands;
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<ApduList> CREATOR =
+        new Parcelable.Creator<ApduList>() {
+        @Override
+        public ApduList createFromParcel(Parcel in) {
+            return new ApduList(in);
+        }
+
+        @Override
+        public ApduList[] newArray(int size) {
+            return new ApduList[size];
+        }
+    };
+
+    private ApduList(Parcel in) {
+        int count = in.readInt();
+
+        for (int i = 0 ; i < count ; i++) {
+
+            int length = in.readInt();
+            byte[] cmd = new byte[length];
+            in.readByteArray(cmd);
+            commands.add(cmd);
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(commands.size());
+
+        for (byte[] cmd : commands) {
+            dest.writeInt(cmd.length);
+            dest.writeByteArray(cmd);
+        }
+    }
+}
+
+
diff --git a/nfc/java/android/nfc/AvailableNfcAntenna.aidl b/nfc/java/android/nfc/AvailableNfcAntenna.aidl
new file mode 100644
index 0000000..9d06e2d
--- /dev/null
+++ b/nfc/java/android/nfc/AvailableNfcAntenna.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc;
+
+parcelable AvailableNfcAntenna;
diff --git a/nfc/java/android/nfc/AvailableNfcAntenna.java b/nfc/java/android/nfc/AvailableNfcAntenna.java
new file mode 100644
index 0000000..6e6512a
--- /dev/null
+++ b/nfc/java/android/nfc/AvailableNfcAntenna.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Represents a single available Nfc antenna
+ * on an Android device.
+ */
+public final class AvailableNfcAntenna implements Parcelable {
+    /**
+     * Location of the antenna on the Y axis in millimeters.
+     * 0 is the bottom-left when the user is facing the screen
+     * and the device orientation is Portrait.
+     */
+    private final int mLocationX;
+    /**
+     * Location of the antenna on the Y axis in millimeters.
+     * 0 is the bottom-left when the user is facing the screen
+     * and the device orientation is Portrait.
+     */
+    private final int mLocationY;
+
+    public AvailableNfcAntenna(int locationX, int locationY) {
+        this.mLocationX = locationX;
+        this.mLocationY = locationY;
+    }
+
+    /**
+     * Location of the antenna on the X axis in millimeters.
+     * 0 is the bottom-left when the user is facing the screen
+     * and the device orientation is Portrait.
+     */
+    public int getLocationX() {
+        return mLocationX;
+    }
+
+    /**
+     * Location of the antenna on the Y axis in millimeters.
+     * 0 is the bottom-left when the user is facing the screen
+     * and the device orientation is Portrait.
+     */
+    public int getLocationY() {
+        return mLocationY;
+    }
+
+    private AvailableNfcAntenna(Parcel in) {
+        this.mLocationX = in.readInt();
+        this.mLocationY = in.readInt();
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<AvailableNfcAntenna>
+            CREATOR = new Parcelable.Creator<AvailableNfcAntenna>() {
+                @Override
+                public AvailableNfcAntenna createFromParcel(Parcel in) {
+                    return new AvailableNfcAntenna(in);
+                }
+
+                @Override
+                public AvailableNfcAntenna[] newArray(int size) {
+                    return new AvailableNfcAntenna[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mLocationX);
+        dest.writeInt(mLocationY);
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + mLocationX;
+        result = prime * result + mLocationY;
+        return result;
+    }
+
+    /**
+     * Returns true if the specified AvailableNfcAntenna contains
+     * identical specifications.
+     */
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (this == obj) return true;
+        if (obj == null) return false;
+        if (getClass() != obj.getClass()) return false;
+        AvailableNfcAntenna other = (AvailableNfcAntenna) obj;
+        if (this.mLocationX != other.mLocationX) return false;
+        return this.mLocationY == other.mLocationY;
+    }
+
+    @Override
+    public String toString() {
+        return "AvailableNfcAntenna " + "x: " + mLocationX + " y: " + mLocationY;
+    }
+}
diff --git a/nfc/java/android/nfc/Constants.java b/nfc/java/android/nfc/Constants.java
new file mode 100644
index 0000000..f768330
--- /dev/null
+++ b/nfc/java/android/nfc/Constants.java
@@ -0,0 +1,29 @@
+/*
+ * 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 android.nfc;
+
+/**
+ * @hide
+ * TODO(b/303286040): Holds @hide API constants. Formalize these APIs.
+ */
+public final class Constants {
+    private Constants() { }
+
+    public static final String SETTINGS_SECURE_NFC_PAYMENT_FOREGROUND = "nfc_payment_foreground";
+    public static final String SETTINGS_SECURE_NFC_PAYMENT_DEFAULT_COMPONENT = "nfc_payment_default_component";
+    public static final String FEATURE_NFC_ANY = "android.hardware.nfc.any";
+}
diff --git a/nfc/java/android/nfc/ErrorCodes.java b/nfc/java/android/nfc/ErrorCodes.java
new file mode 100644
index 0000000..d2c81cd
--- /dev/null
+++ b/nfc/java/android/nfc/ErrorCodes.java
@@ -0,0 +1,114 @@
+/*
+ * 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 android.nfc;
+
+import android.compat.annotation.UnsupportedAppUsage;
+
+/**
+ * This class defines all the error codes that can be returned by the service
+ * and producing an exception on the application level. These are needed since
+ * binders does not support exceptions.
+ *
+ * @hide
+ */
+public class ErrorCodes {
+
+    @UnsupportedAppUsage
+    public static boolean isError(int code) {
+        if (code < 0) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    public static String asString(int code) {
+        switch (code) {
+            case SUCCESS: return "SUCCESS";
+            case ERROR_IO: return "IO";
+            case ERROR_CANCELLED: return "CANCELLED";
+            case ERROR_TIMEOUT: return "TIMEOUT";
+            case ERROR_BUSY: return "BUSY";
+            case ERROR_CONNECT: return "CONNECT/DISCONNECT";
+//            case ERROR_DISCONNECT: return "DISCONNECT";
+            case ERROR_READ: return "READ";
+            case ERROR_WRITE: return "WRITE";
+            case ERROR_INVALID_PARAM: return "INVALID_PARAM";
+            case ERROR_INSUFFICIENT_RESOURCES: return "INSUFFICIENT_RESOURCES";
+            case ERROR_SOCKET_CREATION: return "SOCKET_CREATION";
+            case ERROR_SOCKET_NOT_CONNECTED: return "SOCKET_NOT_CONNECTED";
+            case ERROR_BUFFER_TO_SMALL: return "BUFFER_TO_SMALL";
+            case ERROR_SAP_USED: return "SAP_USED";
+            case ERROR_SERVICE_NAME_USED: return "SERVICE_NAME_USED";
+            case ERROR_SOCKET_OPTIONS: return "SOCKET_OPTIONS";
+            case ERROR_NFC_ON: return "NFC_ON";
+            case ERROR_NOT_INITIALIZED: return "NOT_INITIALIZED";
+            case ERROR_SE_ALREADY_SELECTED: return "SE_ALREADY_SELECTED";
+            case ERROR_SE_CONNECTED: return "SE_CONNECTED";
+            case ERROR_NO_SE_CONNECTED: return "NO_SE_CONNECTED";
+            case ERROR_NOT_SUPPORTED: return "NOT_SUPPORTED";
+            default: return "UNKNOWN ERROR";
+        }
+    }
+
+    public static final int SUCCESS = 0;
+
+    public static final int ERROR_IO = -1;
+
+    public static final int ERROR_CANCELLED = -2;
+
+    public static final int ERROR_TIMEOUT = -3;
+
+    public static final int ERROR_BUSY = -4;
+
+    public static final int ERROR_CONNECT = -5;
+
+    public static final int ERROR_DISCONNECT = -5;
+
+    public static final int ERROR_READ = -6;
+
+    public static final int ERROR_WRITE = -7;
+
+    public static final int ERROR_INVALID_PARAM = -8;
+
+    public static final int ERROR_INSUFFICIENT_RESOURCES = -9;
+
+    public static final int ERROR_SOCKET_CREATION = -10;
+
+    public static final int ERROR_SOCKET_NOT_CONNECTED = -11;
+
+    public static final int ERROR_BUFFER_TO_SMALL = -12;
+
+    public static final int ERROR_SAP_USED = -13;
+
+    public static final int ERROR_SERVICE_NAME_USED = -14;
+
+    public static final int ERROR_SOCKET_OPTIONS = -15;
+
+    public static final int ERROR_NFC_ON = -16;
+
+    public static final int ERROR_NOT_INITIALIZED = -17;
+
+    public static final int ERROR_SE_ALREADY_SELECTED = -18;
+
+    public static final int ERROR_SE_CONNECTED = -19;
+
+    public static final int ERROR_NO_SE_CONNECTED = -20;
+
+    public static final int ERROR_NOT_SUPPORTED = -21;
+
+}
diff --git a/nfc/java/android/nfc/FormatException.java b/nfc/java/android/nfc/FormatException.java
new file mode 100644
index 0000000..a57de1e
--- /dev/null
+++ b/nfc/java/android/nfc/FormatException.java
@@ -0,0 +1,31 @@
+/*
+ * 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 android.nfc;
+
+public class FormatException extends Exception {
+    public FormatException() {
+        super();
+    }
+
+    public FormatException(String message) {
+        super(message);
+    }
+
+    public FormatException(String message, Throwable e) {
+        super(message, e);
+    }
+}
diff --git a/nfc/java/android/nfc/IAppCallback.aidl b/nfc/java/android/nfc/IAppCallback.aidl
new file mode 100644
index 0000000..b06bf06
--- /dev/null
+++ b/nfc/java/android/nfc/IAppCallback.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc;
+
+import android.nfc.Tag;
+
+/**
+ * @hide
+ */
+interface IAppCallback
+{
+    oneway void onTagDiscovered(in Tag tag);
+}
diff --git a/nfc/java/android/nfc/INfcAdapter.aidl b/nfc/java/android/nfc/INfcAdapter.aidl
new file mode 100644
index 0000000..286cf28
--- /dev/null
+++ b/nfc/java/android/nfc/INfcAdapter.aidl
@@ -0,0 +1,100 @@
+/*
+ * 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 android.nfc;
+
+import android.app.PendingIntent;
+import android.content.IntentFilter;
+import android.nfc.NdefMessage;
+import android.nfc.Tag;
+import android.nfc.TechListParcel;
+import android.nfc.IAppCallback;
+import android.nfc.INfcAdapterExtras;
+import android.nfc.INfcControllerAlwaysOnListener;
+import android.nfc.INfcTag;
+import android.nfc.INfcCardEmulation;
+import android.nfc.INfcFCardEmulation;
+import android.nfc.INfcUnlockHandler;
+import android.nfc.ITagRemovedCallback;
+import android.nfc.INfcDta;
+import android.nfc.INfcWlcStateListener;
+import android.nfc.NfcAntennaInfo;
+import android.os.Bundle;
+import android.nfc.WlcLDeviceInfo;
+
+/**
+ * @hide
+ */
+interface INfcAdapter
+{
+    INfcTag getNfcTagInterface();
+    INfcCardEmulation getNfcCardEmulationInterface();
+    INfcFCardEmulation getNfcFCardEmulationInterface();
+    INfcAdapterExtras getNfcAdapterExtrasInterface(in String pkg);
+    INfcDta getNfcDtaInterface(in String pkg);
+    int getState();
+    boolean disable(boolean saveState);
+    boolean enable();
+    void pausePolling(int timeoutInMs);
+    void resumePolling();
+
+    void setForegroundDispatch(in PendingIntent intent,
+            in IntentFilter[] filters, in TechListParcel techLists);
+    void setAppCallback(in IAppCallback callback);
+
+    boolean ignore(int nativeHandle, int debounceMs, ITagRemovedCallback callback);
+
+    void dispatch(in Tag tag);
+
+    void setReaderMode (IBinder b, IAppCallback callback, int flags, in Bundle extras);
+
+    void addNfcUnlockHandler(INfcUnlockHandler unlockHandler, in int[] techList);
+    void removeNfcUnlockHandler(INfcUnlockHandler unlockHandler);
+
+    void verifyNfcPermission();
+    boolean isNfcSecureEnabled();
+    boolean deviceSupportsNfcSecure();
+    boolean setNfcSecure(boolean enable);
+    NfcAntennaInfo getNfcAntennaInfo();
+
+    boolean setControllerAlwaysOn(boolean value);
+    boolean isControllerAlwaysOn();
+    boolean isControllerAlwaysOnSupported();
+    void registerControllerAlwaysOnListener(in INfcControllerAlwaysOnListener listener);
+    void unregisterControllerAlwaysOnListener(in INfcControllerAlwaysOnListener listener);
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)")
+    boolean isTagIntentAppPreferenceSupported();
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)")
+    Map getTagIntentAppPreferenceForUser(int userId);
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)")
+    int setTagIntentAppPreferenceForUser(int userId, String pkg, boolean allow);
+
+    boolean isReaderOptionEnabled();
+    boolean isReaderOptionSupported();
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)")
+    boolean enableReaderOption(boolean enable);
+    boolean isObserveModeSupported();
+    boolean setObserveMode(boolean enabled);
+
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)")
+    boolean enableWlc(boolean enable);
+    boolean isWlcEnabled();
+    void registerWlcStateListener(in INfcWlcStateListener listener);
+    void unregisterWlcStateListener(in INfcWlcStateListener listener);
+    WlcLDeviceInfo getWlcLDeviceInfo();
+
+    void updateDiscoveryTechnology(IBinder b, int pollFlags, int listenFlags);
+}
diff --git a/nfc/java/android/nfc/INfcAdapterExtras.aidl b/nfc/java/android/nfc/INfcAdapterExtras.aidl
new file mode 100644
index 0000000..cde57c5
--- /dev/null
+++ b/nfc/java/android/nfc/INfcAdapterExtras.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc;
+
+import android.os.Bundle;
+
+
+/**
+ * {@hide}
+ */
+interface INfcAdapterExtras {
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
+    Bundle open(in String pkg, IBinder b);
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
+    Bundle close(in String pkg, IBinder b);
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
+    Bundle transceive(in String pkg, in byte[] data_in);
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
+    int getCardEmulationRoute(in String pkg);
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
+    void setCardEmulationRoute(in String pkg, int route);
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
+    void authenticate(in String pkg, in byte[] token);
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
+    String getDriverName(in String pkg);
+}
diff --git a/nfc/java/android/nfc/INfcCardEmulation.aidl b/nfc/java/android/nfc/INfcCardEmulation.aidl
new file mode 100644
index 0000000..f4b4604
--- /dev/null
+++ b/nfc/java/android/nfc/INfcCardEmulation.aidl
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc;
+
+import android.content.ComponentName;
+import android.nfc.cardemulation.AidGroup;
+import android.nfc.cardemulation.ApduServiceInfo;
+import android.os.RemoteCallback;
+
+/**
+ * @hide
+ */
+interface INfcCardEmulation
+{
+    boolean isDefaultServiceForCategory(int userHandle, in ComponentName service, String category);
+    boolean isDefaultServiceForAid(int userHandle, in ComponentName service, String aid);
+    boolean setDefaultServiceForCategory(int userHandle, in ComponentName service, String category);
+    boolean setDefaultForNextTap(int userHandle, in ComponentName service);
+    boolean setServiceObserveModeDefault(int userId, in android.content.ComponentName service, boolean enable);
+    boolean registerAidGroupForService(int userHandle, in ComponentName service, in AidGroup aidGroup);
+    boolean setOffHostForService(int userHandle, in ComponentName service, in String offHostSecureElement);
+    boolean unsetOffHostForService(int userHandle, in ComponentName service);
+    AidGroup getAidGroupForService(int userHandle, in ComponentName service, String category);
+    boolean removeAidGroupForService(int userHandle, in ComponentName service, String category);
+    List<ApduServiceInfo> getServices(int userHandle, in String category);
+    boolean setPreferredService(in ComponentName service);
+    boolean unsetPreferredService();
+    boolean supportsAidPrefixRegistration();
+    ApduServiceInfo getPreferredPaymentService(int userHandle);
+    boolean setServiceEnabledForCategoryOther(int userHandle, in ComponentName app, boolean status);
+    boolean isDefaultPaymentRegistered();
+
+    boolean overrideRoutingTable(int userHandle, String protocol, String technology);
+    boolean recoverRoutingTable(int userHandle);
+}
diff --git a/nfc/java/android/nfc/INfcControllerAlwaysOnListener.aidl b/nfc/java/android/nfc/INfcControllerAlwaysOnListener.aidl
new file mode 100644
index 0000000..1bb7680
--- /dev/null
+++ b/nfc/java/android/nfc/INfcControllerAlwaysOnListener.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2021 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 android.nfc;
+
+/**
+ * @hide
+ */
+oneway interface INfcControllerAlwaysOnListener {
+  /**
+   * Called whenever the controller always on state changes
+   *
+   * @param isEnabled true if the state is enabled, false otherwise
+   */
+  void onControllerAlwaysOnChanged(boolean isEnabled);
+}
diff --git a/nfc/java/android/nfc/INfcDta.aidl b/nfc/java/android/nfc/INfcDta.aidl
new file mode 100644
index 0000000..4cc5927
--- /dev/null
+++ b/nfc/java/android/nfc/INfcDta.aidl
@@ -0,0 +1,34 @@
+ /*
+  * Copyright (C) 2017 NXP Semiconductors
+  *
+  * 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 android.nfc;
+
+import android.os.Bundle;
+
+/**
+ * {@hide}
+ */
+interface INfcDta {
+
+    void enableDta();
+    void disableDta();
+    boolean enableServer(String serviceName, int serviceSap, int miu,
+            int rwSize,int testCaseId);
+    void disableServer();
+    boolean enableClient(String serviceName, int miu, int rwSize,
+            int testCaseId);
+    void disableClient();
+    boolean registerMessageService(String msgServiceName);
+}
diff --git a/nfc/java/android/nfc/INfcFCardEmulation.aidl b/nfc/java/android/nfc/INfcFCardEmulation.aidl
new file mode 100644
index 0000000..124bfac
--- /dev/null
+++ b/nfc/java/android/nfc/INfcFCardEmulation.aidl
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc;
+
+import android.content.ComponentName;
+import android.nfc.cardemulation.NfcFServiceInfo;
+
+/**
+ * @hide
+ */
+interface INfcFCardEmulation
+{
+    String getSystemCodeForService(int userHandle, in ComponentName service);
+    boolean registerSystemCodeForService(int userHandle, in ComponentName service, String systemCode);
+    boolean removeSystemCodeForService(int userHandle, in ComponentName service);
+    String getNfcid2ForService(int userHandle, in ComponentName service);
+    boolean setNfcid2ForService(int userHandle, in ComponentName service, String nfcid2);
+    boolean enableNfcFForegroundService(in ComponentName service);
+    boolean disableNfcFForegroundService();
+    List<NfcFServiceInfo> getNfcFServices(int userHandle);
+    int getMaxNumOfRegisterableSystemCodes();
+}
diff --git a/nfc/java/android/nfc/INfcTag.aidl b/nfc/java/android/nfc/INfcTag.aidl
new file mode 100644
index 0000000..170df71
--- /dev/null
+++ b/nfc/java/android/nfc/INfcTag.aidl
@@ -0,0 +1,50 @@
+/*
+ * 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 android.nfc;
+
+import android.nfc.NdefMessage;
+import android.nfc.Tag;
+import android.nfc.TransceiveResult;
+
+/**
+ * @hide
+ */
+interface INfcTag
+{
+    int connect(int nativeHandle, int technology);
+    int reconnect(int nativeHandle);
+    int[] getTechList(int nativeHandle);
+    boolean isNdef(int nativeHandle);
+    boolean isPresent(int nativeHandle);
+    TransceiveResult transceive(int nativeHandle, in byte[] data, boolean raw);
+
+    NdefMessage ndefRead(int nativeHandle);
+    int ndefWrite(int nativeHandle, in NdefMessage msg);
+    int ndefMakeReadOnly(int nativeHandle);
+    boolean ndefIsWritable(int nativeHandle);
+    int formatNdef(int nativeHandle, in byte[] key);
+    Tag rediscover(int nativehandle);
+
+    int setTimeout(int technology, int timeout);
+    int getTimeout(int technology);
+    void resetTimeouts();
+    boolean canMakeReadOnly(int ndefType);
+    int getMaxTransceiveLength(int technology);
+    boolean getExtendedLengthApdusSupported();
+
+    boolean isTagUpToDate(long cookie);
+}
diff --git a/nfc/java/android/nfc/INfcUnlockHandler.aidl b/nfc/java/android/nfc/INfcUnlockHandler.aidl
new file mode 100644
index 0000000..e1cace9
--- /dev/null
+++ b/nfc/java/android/nfc/INfcUnlockHandler.aidl
@@ -0,0 +1,12 @@
+package android.nfc;
+
+import android.nfc.Tag;
+
+/**
+ * @hide
+ */
+interface INfcUnlockHandler {
+
+    boolean onUnlockAttempted(in Tag tag);
+
+}
diff --git a/nfc/java/android/nfc/INfcWlcStateListener.aidl b/nfc/java/android/nfc/INfcWlcStateListener.aidl
new file mode 100644
index 0000000..c2b7075
--- /dev/null
+++ b/nfc/java/android/nfc/INfcWlcStateListener.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright 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 android.nfc;
+
+import android.nfc.WlcLDeviceInfo;
+/**
+ * @hide
+ */
+oneway interface INfcWlcStateListener {
+  /**
+   * Called whenever NFC WLC state changes
+   *
+   * @param wlcLDeviceInfo NFC wlc listener information
+   */
+  void onWlcStateChanged(in WlcLDeviceInfo wlcLDeviceInfo);
+}
diff --git a/nfc/java/android/nfc/ITagRemovedCallback.aidl b/nfc/java/android/nfc/ITagRemovedCallback.aidl
new file mode 100644
index 0000000..2a06ff3
--- /dev/null
+++ b/nfc/java/android/nfc/ITagRemovedCallback.aidl
@@ -0,0 +1,8 @@
+package android.nfc;
+
+/**
+ * @hide
+ */
+oneway interface ITagRemovedCallback {
+    void onTagRemoved();
+}
diff --git a/nfc/java/android/nfc/NdefMessage.aidl b/nfc/java/android/nfc/NdefMessage.aidl
new file mode 100644
index 0000000..378b9d0
--- /dev/null
+++ b/nfc/java/android/nfc/NdefMessage.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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 android.nfc;
+
+parcelable NdefMessage;
diff --git a/nfc/java/android/nfc/NdefMessage.java b/nfc/java/android/nfc/NdefMessage.java
new file mode 100644
index 0000000..553f6c0
--- /dev/null
+++ b/nfc/java/android/nfc/NdefMessage.java
@@ -0,0 +1,272 @@
+/*
+ * 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 android.nfc;
+
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.proto.ProtoOutputStream;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+/**
+ * Represents an immutable NDEF Message.
+ * <p>
+ * NDEF (NFC Data Exchange Format) is a light-weight binary format,
+ * used to encapsulate typed data. It is specified by the NFC Forum,
+ * for transmission and storage with NFC, however it is transport agnostic.
+ * <p>
+ * NDEF defines messages and records. An NDEF Record contains
+ * typed data, such as MIME-type media, a URI, or a custom
+ * application payload. An NDEF Message is a container for
+ * one or more NDEF Records.
+ * <p>
+ * When an Android device receives an NDEF Message
+ * (for example by reading an NFC tag) it processes it through
+ * a dispatch mechanism to determine an activity to launch.
+ * The type of the <em>first</em> record in the message has
+ * special importance for message dispatch, so design this record
+ * carefully.
+ * <p>
+ * Use {@link #NdefMessage(byte[])} to construct an NDEF Message from
+ * binary data, or {@link #NdefMessage(NdefRecord[])} to
+ * construct from one or more {@link NdefRecord}s.
+ * <p class="note">
+ * {@link NdefMessage} and {@link NdefRecord} implementations are
+ * always available, even on Android devices that do not have NFC hardware.
+ * <p class="note">
+ * {@link NdefRecord}s are intended to be immutable (and thread-safe),
+ * however they may contain mutable fields. So take care not to modify
+ * mutable fields passed into constructors, or modify mutable fields
+ * obtained by getter methods, unless such modification is explicitly
+ * marked as safe.
+ *
+ * @see NfcAdapter#ACTION_NDEF_DISCOVERED
+ * @see NdefRecord
+ */
+public final class NdefMessage implements Parcelable {
+    private final NdefRecord[] mRecords;
+
+    /**
+     * Construct an NDEF Message by parsing raw bytes.<p>
+     * Strict validation of the NDEF binary structure is performed:
+     * there must be at least one record, every record flag must
+     * be correct, and the total length of the message must match
+     * the length of the input data.<p>
+     * This parser can handle chunked records, and converts them
+     * into logical {@link NdefRecord}s within the message.<p>
+     * Once the input data has been parsed to one or more logical
+     * records, basic validation of the tnf, type, id, and payload fields
+     * of each record is performed, as per the documentation on
+     * on {@link NdefRecord#NdefRecord(short, byte[], byte[], byte[])}<p>
+     * If either strict validation of the binary format fails, or
+     * basic validation during record construction fails, a
+     * {@link FormatException} is thrown<p>
+     * Deep inspection of the type, id and payload fields of
+     * each record is not performed, so it is possible to parse input
+     * that has a valid binary format and confirms to the basic
+     * validation requirements of
+     * {@link NdefRecord#NdefRecord(short, byte[], byte[], byte[])},
+     * but fails more strict requirements as specified by the
+     * NFC Forum.
+     *
+     * <p class="note">
+     * It is safe to re-use the data byte array after construction:
+     * this constructor will make an internal copy of all necessary fields.
+     *
+     * @param data raw bytes to parse
+     * @throws FormatException if the data cannot be parsed
+     */
+    public NdefMessage(byte[] data) throws FormatException {
+        if (data == null) throw new NullPointerException("data is null");
+        ByteBuffer buffer = ByteBuffer.wrap(data);
+
+        mRecords = NdefRecord.parse(buffer, false);
+
+        if (buffer.remaining() > 0) {
+            throw new FormatException("trailing data");
+        }
+    }
+
+    /**
+     * Construct an NDEF Message from one or more NDEF Records.
+     *
+     * @param record first record (mandatory)
+     * @param records additional records (optional)
+     */
+    public NdefMessage(NdefRecord record, NdefRecord ... records) {
+        // validate
+        if (record == null) throw new NullPointerException("record cannot be null");
+
+        for (NdefRecord r : records) {
+            if (r == null) {
+                throw new NullPointerException("record cannot be null");
+            }
+        }
+
+        mRecords = new NdefRecord[1 + records.length];
+        mRecords[0] = record;
+        System.arraycopy(records, 0, mRecords, 1, records.length);
+    }
+
+    /**
+     * Construct an NDEF Message from one or more NDEF Records.
+     *
+     * @param records one or more records
+     */
+    public NdefMessage(NdefRecord[] records) {
+        // validate
+        if (records.length < 1) {
+            throw new IllegalArgumentException("must have at least one record");
+        }
+        for (NdefRecord r : records) {
+            if (r == null) {
+                throw new NullPointerException("records cannot contain null");
+            }
+        }
+
+        mRecords = records;
+    }
+
+    /**
+     * Get the NDEF Records inside this NDEF Message.<p>
+     * An {@link NdefMessage} always has one or more NDEF Records: so the
+     * following code to retrieve the first record is always safe
+     * (no need to check for null or array length >= 1):
+     * <pre>
+     * NdefRecord firstRecord = ndefMessage.getRecords()[0];
+     * </pre>
+     *
+     * @return array of one or more NDEF records.
+     */
+    public NdefRecord[] getRecords() {
+        return mRecords;
+    }
+
+    /**
+     * Return the length of this NDEF Message if it is written to a byte array
+     * with {@link #toByteArray}.<p>
+     * An NDEF Message can be formatted to bytes in different ways
+     * depending on chunking, SR, and ID flags, so the length returned
+     * by this method may not be equal to the length of the original
+     * byte array used to construct this NDEF Message. However it will
+     * always be equal to the length of the byte array produced by
+     * {@link #toByteArray}.
+     *
+     * @return length of this NDEF Message when written to bytes with {@link #toByteArray}
+     * @see #toByteArray
+     */
+    public int getByteArrayLength() {
+        int length = 0;
+        for (NdefRecord r : mRecords) {
+            length += r.getByteLength();
+        }
+        return length;
+    }
+
+    /**
+     * Return this NDEF Message as raw bytes.<p>
+     * The NDEF Message is formatted as per the NDEF 1.0 specification,
+     * and the byte array is suitable for network transmission or storage
+     * in an NFC Forum NDEF compatible tag.<p>
+     * This method will not chunk any records, and will always use the
+     * short record (SR) format and omit the identifier field when possible.
+     *
+     * @return NDEF Message in binary format
+     * @see #getByteArrayLength()
+     */
+    public byte[] toByteArray() {
+        int length = getByteArrayLength();
+        ByteBuffer buffer = ByteBuffer.allocate(length);
+
+        for (int i=0; i<mRecords.length; i++) {
+            boolean mb = (i == 0);  // first record
+            boolean me = (i == mRecords.length - 1);  // last record
+            mRecords[i].writeToByteBuffer(buffer, mb, me);
+        }
+
+        return buffer.array();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mRecords.length);
+        dest.writeTypedArray(mRecords, flags);
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<NdefMessage> CREATOR =
+            new Parcelable.Creator<NdefMessage>() {
+        @Override
+        public NdefMessage createFromParcel(Parcel in) {
+            int recordsLength = in.readInt();
+            NdefRecord[] records = new NdefRecord[recordsLength];
+            in.readTypedArray(records, NdefRecord.CREATOR);
+            return new NdefMessage(records);
+        }
+        @Override
+        public NdefMessage[] newArray(int size) {
+            return new NdefMessage[size];
+        }
+    };
+
+    @Override
+    public int hashCode() {
+        return Arrays.hashCode(mRecords);
+    }
+
+    /**
+     * Returns true if the specified NDEF Message contains
+     * identical NDEF Records.
+     */
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (this == obj) return true;
+        if (obj == null) return false;
+        if (getClass() != obj.getClass()) return false;
+        NdefMessage other = (NdefMessage) obj;
+        return Arrays.equals(mRecords, other.mRecords);
+    }
+
+    @Override
+    public String toString() {
+        return "NdefMessage " + Arrays.toString(mRecords);
+    }
+
+    /**
+     * Dump debugging information as a NdefMessageProto
+     * @hide
+     *
+     * Note:
+     * See proto definition in frameworks/base/core/proto/android/nfc/ndef.proto
+     * When writing a nested message, must call {@link ProtoOutputStream#start(long)} before and
+     * {@link ProtoOutputStream#end(long)} after.
+     * Never reuse a proto field number. When removing a field, mark it as reserved.
+     */
+    public void dumpDebug(ProtoOutputStream proto) {
+        for (NdefRecord record : mRecords) {
+            long token = proto.start(NdefMessageProto.NDEF_RECORDS);
+            record.dumpDebug(proto);
+            proto.end(token);
+        }
+    }
+}
\ No newline at end of file
diff --git a/nfc/java/android/nfc/NdefRecord.aidl b/nfc/java/android/nfc/NdefRecord.aidl
new file mode 100644
index 0000000..10f89d0
--- /dev/null
+++ b/nfc/java/android/nfc/NdefRecord.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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 android.nfc;
+
+parcelable NdefRecord;
\ No newline at end of file
diff --git a/nfc/java/android/nfc/NdefRecord.java b/nfc/java/android/nfc/NdefRecord.java
new file mode 100644
index 0000000..7bf4355
--- /dev/null
+++ b/nfc/java/android/nfc/NdefRecord.java
@@ -0,0 +1,1080 @@
+/*
+ * 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 android.nfc;
+
+import android.annotation.Nullable;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.proto.ProtoOutputStream;
+
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Represents an immutable NDEF Record.
+ * <p>
+ * NDEF (NFC Data Exchange Format) is a light-weight binary format,
+ * used to encapsulate typed data. It is specified by the NFC Forum,
+ * for transmission and storage with NFC, however it is transport agnostic.
+ * <p>
+ * NDEF defines messages and records. An NDEF Record contains
+ * typed data, such as MIME-type media, a URI, or a custom
+ * application payload. An NDEF Message is a container for
+ * one or more NDEF Records.
+ * <p>
+ * This class represents logical (complete) NDEF Records, and can not be
+ * used to represent chunked (partial) NDEF Records. However
+ * {@link NdefMessage#NdefMessage(byte[])} can be used to parse a message
+ * containing chunked records, and will return a message with unchunked
+ * (complete) records.
+ * <p>
+ * A logical NDEF Record always contains a 3-bit TNF (Type Name Field)
+ * that provides high level typing for the rest of the record. The
+ * remaining fields are variable length and not always present:
+ * <ul>
+ * <li><em>type</em>: detailed typing for the payload</li>
+ * <li><em>id</em>: identifier meta-data, not commonly used</li>
+ * <li><em>payload</em>: the actual payload</li>
+ * </ul>
+ * <p>
+ * Helpers such as {@link NdefRecord#createUri}, {@link NdefRecord#createMime}
+ * and {@link NdefRecord#createExternal} are included to create well-formatted
+ * NDEF Records with correctly set tnf, type, id and payload fields, please
+ * use these helpers whenever possible.
+ * <p>
+ * Use the constructor {@link #NdefRecord(short, byte[], byte[], byte[])}
+ * if you know what you are doing and what to set the fields individually.
+ * Only basic validation is performed with this constructor, so it is possible
+ * to create records that do not confirm to the strict NFC Forum
+ * specifications.
+ * <p>
+ * The binary representation of an NDEF Record includes additional flags to
+ * indicate location with an NDEF message, provide support for chunking of
+ * NDEF records, and to pack optional fields. This class does not expose
+ * those details. To write an NDEF Record as binary you must first put it
+ * into an {@link NdefMessage}, then call {@link NdefMessage#toByteArray()}.
+ * <p class="note">
+ * {@link NdefMessage} and {@link NdefRecord} implementations are
+ * always available, even on Android devices that do not have NFC hardware.
+ * <p class="note">
+ * {@link NdefRecord}s are intended to be immutable (and thread-safe),
+ * however they may contain mutable fields. So take care not to modify
+ * mutable fields passed into constructors, or modify mutable fields
+ * obtained by getter methods, unless such modification is explicitly
+ * marked as safe.
+ *
+ * @see NfcAdapter#ACTION_NDEF_DISCOVERED
+ * @see NdefMessage
+ */
+public final class NdefRecord implements Parcelable {
+    /**
+     * Indicates the record is empty.<p>
+     * Type, id and payload fields are empty in a {@literal TNF_EMPTY} record.
+     */
+    public static final short TNF_EMPTY = 0x00;
+
+    /**
+     * Indicates the type field contains a well-known RTD type name.<p>
+     * Use this tnf with RTD types such as {@link #RTD_TEXT}, {@link #RTD_URI}.
+     * <p>
+     * The RTD type name format is specified in NFCForum-TS-RTD_1.0.
+     *
+     * @see #RTD_URI
+     * @see #RTD_TEXT
+     * @see #RTD_SMART_POSTER
+     * @see #createUri
+     */
+    public static final short TNF_WELL_KNOWN = 0x01;
+
+    /**
+     * Indicates the type field contains a media-type BNF
+     * construct, defined by RFC 2046.<p>
+     * Use this with MIME type names such as {@literal "image/jpeg"}, or
+     * using the helper {@link #createMime}.
+     *
+     * @see #createMime
+     */
+    public static final short TNF_MIME_MEDIA = 0x02;
+
+    /**
+     * Indicates the type field contains an absolute-URI
+     * BNF construct defined by RFC 3986.<p>
+     * When creating new records prefer {@link #createUri},
+     * since it offers more compact URI encoding
+     * ({@literal #RTD_URI} allows compression of common URI prefixes).
+     *
+     * @see #createUri
+     */
+    public static final short TNF_ABSOLUTE_URI = 0x03;
+
+    /**
+     * Indicates the type field contains an external type name.<p>
+     * Used to encode custom payloads. When creating new records
+     * use the helper {@link #createExternal}.<p>
+     * The external-type RTD format is specified in NFCForum-TS-RTD_1.0.<p>
+     * <p>
+     * Note this TNF should not be used with RTD_TEXT or RTD_URI constants.
+     * Those are well known RTD constants, not external RTD constants.
+     *
+     * @see #createExternal
+     */
+    public static final short TNF_EXTERNAL_TYPE = 0x04;
+
+    /**
+     * Indicates the payload type is unknown.<p>
+     * NFC Forum explains this should be treated similarly to the
+     * "application/octet-stream" MIME type. The payload
+     * type is not explicitly encoded within the record.
+     * <p>
+     * The type field is empty in an {@literal TNF_UNKNOWN} record.
+     */
+    public static final short TNF_UNKNOWN = 0x05;
+
+    /**
+     * Indicates the payload is an intermediate or final chunk of a chunked
+     * NDEF Record.<p>
+     * {@literal TNF_UNCHANGED} can not be used with this class
+     * since all {@link NdefRecord}s are already unchunked, however they
+     * may appear in the binary format.
+     */
+    public static final short TNF_UNCHANGED = 0x06;
+
+    /**
+     * Reserved TNF type.
+     * <p>
+     * The NFC Forum NDEF Specification v1.0 suggests for NDEF parsers to treat this
+     * value like TNF_UNKNOWN.
+     * @hide
+     */
+    public static final short TNF_RESERVED = 0x07;
+
+    /**
+     * RTD Text type. For use with {@literal TNF_WELL_KNOWN}.
+     * @see #TNF_WELL_KNOWN
+     */
+    public static final byte[] RTD_TEXT = {0x54};  // "T"
+
+    /**
+     * RTD URI type. For use with {@literal TNF_WELL_KNOWN}.
+     * @see #TNF_WELL_KNOWN
+     */
+    public static final byte[] RTD_URI = {0x55};   // "U"
+
+    /**
+     * RTD Smart Poster type. For use with {@literal TNF_WELL_KNOWN}.
+     * @see #TNF_WELL_KNOWN
+     */
+    public static final byte[] RTD_SMART_POSTER = {0x53, 0x70};  // "Sp"
+
+    /**
+     * RTD Alternative Carrier type. For use with {@literal TNF_WELL_KNOWN}.
+     * @see #TNF_WELL_KNOWN
+     */
+    public static final byte[] RTD_ALTERNATIVE_CARRIER = {0x61, 0x63};  // "ac"
+
+    /**
+     * RTD Handover Carrier type. For use with {@literal TNF_WELL_KNOWN}.
+     * @see #TNF_WELL_KNOWN
+     */
+    public static final byte[] RTD_HANDOVER_CARRIER = {0x48, 0x63};  // "Hc"
+
+    /**
+     * RTD Handover Request type. For use with {@literal TNF_WELL_KNOWN}.
+     * @see #TNF_WELL_KNOWN
+     */
+    public static final byte[] RTD_HANDOVER_REQUEST = {0x48, 0x72};  // "Hr"
+
+    /**
+     * RTD Handover Select type. For use with {@literal TNF_WELL_KNOWN}.
+     * @see #TNF_WELL_KNOWN
+     */
+    public static final byte[] RTD_HANDOVER_SELECT = {0x48, 0x73}; // "Hs"
+
+    /**
+     * RTD Android app type. For use with {@literal TNF_EXTERNAL}.
+     * <p>
+     * The payload of a record with type RTD_ANDROID_APP
+     * should be the package name identifying an application.
+     * Multiple RTD_ANDROID_APP records may be included
+     * in a single {@link NdefMessage}.
+     * <p>
+     * Use {@link #createApplicationRecord(String)} to create
+     * RTD_ANDROID_APP records.
+     * @hide
+     */
+    public static final byte[] RTD_ANDROID_APP = "android.com:pkg".getBytes();
+
+    private static final byte FLAG_MB = (byte) 0x80;
+    private static final byte FLAG_ME = (byte) 0x40;
+    private static final byte FLAG_CF = (byte) 0x20;
+    private static final byte FLAG_SR = (byte) 0x10;
+    private static final byte FLAG_IL = (byte) 0x08;
+
+    /**
+     * NFC Forum "URI Record Type Definition"<p>
+     * This is a mapping of "URI Identifier Codes" to URI string prefixes,
+     * per section 3.2.2 of the NFC Forum URI Record Type Definition document.
+     */
+    private static final String[] URI_PREFIX_MAP = new String[] {
+            "", // 0x00
+            "http://www.", // 0x01
+            "https://www.", // 0x02
+            "http://", // 0x03
+            "https://", // 0x04
+            "tel:", // 0x05
+            "mailto:", // 0x06
+            "ftp://anonymous:anonymous@", // 0x07
+            "ftp://ftp.", // 0x08
+            "ftps://", // 0x09
+            "sftp://", // 0x0A
+            "smb://", // 0x0B
+            "nfs://", // 0x0C
+            "ftp://", // 0x0D
+            "dav://", // 0x0E
+            "news:", // 0x0F
+            "telnet://", // 0x10
+            "imap:", // 0x11
+            "rtsp://", // 0x12
+            "urn:", // 0x13
+            "pop:", // 0x14
+            "sip:", // 0x15
+            "sips:", // 0x16
+            "tftp:", // 0x17
+            "btspp://", // 0x18
+            "btl2cap://", // 0x19
+            "btgoep://", // 0x1A
+            "tcpobex://", // 0x1B
+            "irdaobex://", // 0x1C
+            "file://", // 0x1D
+            "urn:epc:id:", // 0x1E
+            "urn:epc:tag:", // 0x1F
+            "urn:epc:pat:", // 0x20
+            "urn:epc:raw:", // 0x21
+            "urn:epc:", // 0x22
+            "urn:nfc:", // 0x23
+    };
+
+    private static final int MAX_PAYLOAD_SIZE = 10 * (1 << 20);  // 10 MB payload limit
+
+    private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
+
+    private final short mTnf;
+    private final byte[] mType;
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    private final byte[] mId;
+    private final byte[] mPayload;
+
+    /**
+     * Create a new Android Application Record (AAR).
+     * <p>
+     * This record indicates to other Android devices the package
+     * that should be used to handle the entire NDEF message.
+     * You can embed this record anywhere into your message
+     * to ensure that the intended package receives the message.
+     * <p>
+     * When an Android device dispatches an {@link NdefMessage}
+     * containing one or more Android application records,
+     * the applications contained in those records will be the
+     * preferred target for the {@link NfcAdapter#ACTION_NDEF_DISCOVERED}
+     * intent, in the order in which they appear in the message.
+     * This dispatch behavior was first added to Android in
+     * Ice Cream Sandwich.
+     * <p>
+     * If none of the applications have a are installed on the device,
+     * a Market link will be opened to the first application.
+     * <p>
+     * Note that Android application records do not overrule
+     * applications that have called
+     * {@link NfcAdapter#enableForegroundDispatch}.
+     *
+     * @param packageName Android package name
+     * @return Android application NDEF record
+     */
+    public static NdefRecord createApplicationRecord(String packageName) {
+        if (packageName == null) throw new NullPointerException("packageName is null");
+        if (packageName.length() == 0) throw new IllegalArgumentException("packageName is empty");
+
+        return new NdefRecord(TNF_EXTERNAL_TYPE, RTD_ANDROID_APP, null,
+                packageName.getBytes(StandardCharsets.UTF_8));
+    }
+
+    /**
+     * Create a new NDEF Record containing a URI.<p>
+     * Use this method to encode a URI (or URL) into an NDEF Record.<p>
+     * Uses the well known URI type representation: {@link #TNF_WELL_KNOWN}
+     * and {@link #RTD_URI}. This is the most efficient encoding
+     * of a URI into NDEF.<p>
+     * The uri parameter will be normalized with
+     * {@link Uri#normalizeScheme} to set the scheme to lower case to
+     * follow Android best practices for intent filtering.
+     * However the unchecked exception
+     * {@link IllegalArgumentException} may be thrown if the uri
+     * parameter has serious problems, for example if it is empty, so always
+     * catch this exception if you are passing user-generated data into this
+     * method.<p>
+     *
+     * Reference specification: NFCForum-TS-RTD_URI_1.0
+     *
+     * @param uri URI to encode.
+     * @return an NDEF Record containing the URI
+     * @throws IllegalArugmentException if the uri is empty or invalid
+     */
+    public static NdefRecord createUri(Uri uri) {
+        if (uri == null) throw new NullPointerException("uri is null");
+
+        uri = uri.normalizeScheme();
+        String uriString = uri.toString();
+        if (uriString.length() == 0) throw new IllegalArgumentException("uri is empty");
+
+        byte prefix = 0;
+        for (int i = 1; i < URI_PREFIX_MAP.length; i++) {
+            if (uriString.startsWith(URI_PREFIX_MAP[i])) {
+                prefix = (byte) i;
+                uriString = uriString.substring(URI_PREFIX_MAP[i].length());
+                break;
+            }
+        }
+        byte[] uriBytes = uriString.getBytes(StandardCharsets.UTF_8);
+        byte[] recordBytes = new byte[uriBytes.length + 1];
+        recordBytes[0] = prefix;
+        System.arraycopy(uriBytes, 0, recordBytes, 1, uriBytes.length);
+        return new NdefRecord(TNF_WELL_KNOWN, RTD_URI, null, recordBytes);
+    }
+
+    /**
+     * Create a new NDEF Record containing a URI.<p>
+     * Use this method to encode a URI (or URL) into an NDEF Record.<p>
+     * Uses the well known URI type representation: {@link #TNF_WELL_KNOWN}
+     * and {@link #RTD_URI}. This is the most efficient encoding
+     * of a URI into NDEF.<p>
+      * The uriString parameter will be normalized with
+     * {@link Uri#normalizeScheme} to set the scheme to lower case to
+     * follow Android best practices for intent filtering.
+     * However the unchecked exception
+     * {@link IllegalArgumentException} may be thrown if the uriString
+     * parameter has serious problems, for example if it is empty, so always
+     * catch this exception if you are passing user-generated data into this
+     * method.<p>
+     *
+     * Reference specification: NFCForum-TS-RTD_URI_1.0
+     *
+     * @param uriString string URI to encode.
+     * @return an NDEF Record containing the URI
+     * @throws IllegalArugmentException if the uriString is empty or invalid
+     */
+    public static NdefRecord createUri(String uriString) {
+        return createUri(Uri.parse(uriString));
+    }
+
+    /**
+     * Create a new NDEF Record containing MIME data.<p>
+     * Use this method to encode MIME-typed data into an NDEF Record,
+     * such as "text/plain", or "image/jpeg".<p>
+     * The mimeType parameter will be normalized with
+     * {@link Intent#normalizeMimeType} to follow Android best
+     * practices for intent filtering, for example to force lower-case.
+     * However the unchecked exception
+     * {@link IllegalArgumentException} may be thrown
+     * if the mimeType parameter has serious problems,
+     * for example if it is empty, so always catch this
+     * exception if you are passing user-generated data into this method.
+     * <p>
+     * For efficiency, This method might not make an internal copy of the
+     * mimeData byte array, so take care not
+     * to modify the mimeData byte array while still using the returned
+     * NdefRecord.
+     *
+     * @param mimeType a valid MIME type
+     * @param mimeData MIME data as bytes
+     * @return an NDEF Record containing the MIME-typed data
+     * @throws IllegalArugmentException if the mimeType is empty or invalid
+     *
+     */
+    public static NdefRecord createMime(String mimeType, byte[] mimeData) {
+        if (mimeType == null) throw new NullPointerException("mimeType is null");
+
+        // We only do basic MIME type validation: trying to follow the
+        // RFCs strictly only ends in tears, since there are lots of MIME
+        // types in common use that are not strictly valid as per RFC rules
+        mimeType = Intent.normalizeMimeType(mimeType);
+        if (mimeType.length() == 0) throw new IllegalArgumentException("mimeType is empty");
+        int slashIndex = mimeType.indexOf('/');
+        if (slashIndex == 0) throw new IllegalArgumentException("mimeType must have major type");
+        if (slashIndex == mimeType.length() - 1) {
+            throw new IllegalArgumentException("mimeType must have minor type");
+        }
+        // missing '/' is allowed
+
+        // MIME RFCs suggest ASCII encoding for content-type
+        byte[] typeBytes = mimeType.getBytes(StandardCharsets.US_ASCII);
+        return new NdefRecord(TNF_MIME_MEDIA, typeBytes, null, mimeData);
+    }
+
+    /**
+     * Create a new NDEF Record containing external (application-specific) data.<p>
+     * Use this method to encode application specific data into an NDEF Record.
+     * The data is typed by a domain name (usually your Android package name) and
+     * a domain-specific type. This data is packaged into a "NFC Forum External
+     * Type" NDEF Record.<p>
+     * NFC Forum requires that the domain and type used in an external record
+     * are treated as case insensitive, however Android intent filtering is
+     * always case sensitive. So this method will force the domain and type to
+     * lower-case before creating the NDEF Record.<p>
+     * The unchecked exception {@link IllegalArgumentException} will be thrown
+     * if the domain and type have serious problems, for example if either field
+     * is empty, so always catch this
+     * exception if you are passing user-generated data into this method.<p>
+     * There are no such restrictions on the payload data.<p>
+     * For efficiency, This method might not make an internal copy of the
+     * data byte array, so take care not
+     * to modify the data byte array while still using the returned
+     * NdefRecord.
+     *
+     * Reference specification: NFCForum-TS-RTD_1.0
+     * @param domain domain-name of issuing organization
+     * @param type domain-specific type of data
+     * @param data payload as bytes
+     * @throws IllegalArugmentException if either domain or type are empty or invalid
+     */
+    public static NdefRecord createExternal(String domain, String type, byte[] data) {
+        if (domain == null) throw new NullPointerException("domain is null");
+        if (type == null) throw new NullPointerException("type is null");
+
+        domain = domain.trim().toLowerCase(Locale.ROOT);
+        type = type.trim().toLowerCase(Locale.ROOT);
+
+        if (domain.length() == 0) throw new IllegalArgumentException("domain is empty");
+        if (type.length() == 0) throw new IllegalArgumentException("type is empty");
+
+        byte[] byteDomain = domain.getBytes(StandardCharsets.UTF_8);
+        byte[] byteType = type.getBytes(StandardCharsets.UTF_8);
+        byte[] b = new byte[byteDomain.length + 1 + byteType.length];
+        System.arraycopy(byteDomain, 0, b, 0, byteDomain.length);
+        b[byteDomain.length] = ':';
+        System.arraycopy(byteType, 0, b, byteDomain.length + 1, byteType.length);
+
+        return new NdefRecord(TNF_EXTERNAL_TYPE, b, null, data);
+    }
+
+    /**
+     * Create a new NDEF record containing UTF-8 text data.<p>
+     *
+     * The caller can either specify the language code for the provided text,
+     * or otherwise the language code corresponding to the current default
+     * locale will be used.
+     *
+     * Reference specification: NFCForum-TS-RTD_Text_1.0
+     * @param languageCode The languageCode for the record. If locale is empty or null,
+     *                     the language code of the current default locale will be used.
+     * @param text   The text to be encoded in the record. Will be represented in UTF-8 format.
+     * @throws IllegalArgumentException if text is null
+     */
+    public static NdefRecord createTextRecord(String languageCode, String text) {
+        if (text == null) throw new NullPointerException("text is null");
+
+        byte[] textBytes = text.getBytes(StandardCharsets.UTF_8);
+
+        byte[] languageCodeBytes = null;
+        if (languageCode != null && !languageCode.isEmpty()) {
+            languageCodeBytes = languageCode.getBytes(StandardCharsets.US_ASCII);
+        } else {
+            languageCodeBytes = Locale.getDefault().getLanguage().
+                    getBytes(StandardCharsets.US_ASCII);
+        }
+        // We only have 6 bits to indicate ISO/IANA language code.
+        if (languageCodeBytes.length >= 64) {
+            throw new IllegalArgumentException("language code is too long, must be <64 bytes.");
+        }
+        ByteBuffer buffer = ByteBuffer.allocate(1 + languageCodeBytes.length + textBytes.length);
+
+        byte status = (byte) (languageCodeBytes.length & 0xFF);
+        buffer.put(status);
+        buffer.put(languageCodeBytes);
+        buffer.put(textBytes);
+
+        return new NdefRecord(TNF_WELL_KNOWN, RTD_TEXT, null, buffer.array());
+    }
+
+    /**
+     * Construct an NDEF Record from its component fields.<p>
+     * Recommend to use helpers such as {#createUri} or
+     * {{@link #createExternal} where possible, since they perform
+     * stricter validation that the record is correctly formatted
+     * as per NDEF specifications. However if you know what you are
+     * doing then this constructor offers the most flexibility.<p>
+     * An {@link NdefRecord} represents a logical (complete)
+     * record, and cannot represent NDEF Record chunks.<p>
+     * Basic validation of the tnf, type, id and payload is performed
+     * as per the following rules:
+     * <ul>
+     * <li>The tnf paramter must be a 3-bit value.</li>
+     * <li>Records with a tnf of {@link #TNF_EMPTY} cannot have a type,
+     * id or payload.</li>
+     * <li>Records with a tnf of {@link #TNF_UNKNOWN} or {@literal 0x07}
+     * cannot have a type.</li>
+     * <li>Records with a tnf of {@link #TNF_UNCHANGED} are not allowed
+     * since this class only represents complete (unchunked) records.</li>
+     * </ul>
+     * This minimal validation is specified by
+     * NFCForum-TS-NDEF_1.0 section 3.2.6 (Type Name Format).<p>
+     * If any of the above validation
+     * steps fail then {@link IllegalArgumentException} is thrown.<p>
+     * Deep inspection of the type, id and payload fields is not
+     * performed, so it is possible to create NDEF Records
+     * that conform to section 3.2.6
+     * but fail other more strict NDEF specification requirements. For
+     * example, the payload may be invalid given the tnf and type.
+     * <p>
+     * To omit a type, id or payload field, set the parameter to an
+     * empty byte array or null.
+     *
+     * @param tnf  a 3-bit TNF constant
+     * @param type byte array, containing zero to 255 bytes, or null
+     * @param id   byte array, containing zero to 255 bytes, or null
+     * @param payload byte array, containing zero to (2 ** 32 - 1) bytes,
+     *                or null
+     * @throws IllegalArugmentException if a valid record cannot be created
+     */
+    public NdefRecord(short tnf, byte[] type, byte[] id, byte[] payload) {
+        /* convert nulls */
+        if (type == null) type = EMPTY_BYTE_ARRAY;
+        if (id == null) id = EMPTY_BYTE_ARRAY;
+        if (payload == null) payload = EMPTY_BYTE_ARRAY;
+
+        String message = validateTnf(tnf, type, id, payload);
+        if (message != null) {
+            throw new IllegalArgumentException(message);
+        }
+
+        mTnf = tnf;
+        mType = type;
+        mId = id;
+        mPayload = payload;
+    }
+
+    /**
+     * Construct an NDEF Record from raw bytes.<p>
+     * This method is deprecated, use {@link NdefMessage#NdefMessage(byte[])}
+     * instead. This is because it does not make sense to parse a record:
+     * the NDEF binary format is only defined for a message, and the
+     * record flags MB and ME do not make sense outside of the context of
+     * an entire message.<p>
+     * This implementation will attempt to parse a single record by ignoring
+     * the MB and ME flags, and otherwise following the rules of
+     * {@link NdefMessage#NdefMessage(byte[])}.<p>
+     *
+     * @param data raw bytes to parse
+     * @throws FormatException if the data cannot be parsed into a valid record
+     * @deprecated use {@link NdefMessage#NdefMessage(byte[])} instead.
+     */
+    @Deprecated
+    public NdefRecord(byte[] data) throws FormatException {
+        ByteBuffer buffer = ByteBuffer.wrap(data);
+        NdefRecord[] rs = parse(buffer, true);
+
+        if (buffer.remaining() > 0) {
+            throw new FormatException("data too long");
+        }
+
+        mTnf = rs[0].mTnf;
+        mType = rs[0].mType;
+        mId = rs[0].mId;
+        mPayload = rs[0].mPayload;
+    }
+
+    /**
+     * Returns the 3-bit TNF.
+     * <p>
+     * TNF is the top-level type.
+     */
+    public short getTnf() {
+        return mTnf;
+    }
+
+    /**
+     * Returns the variable length Type field.
+     * <p>
+     * This should be used in conjunction with the TNF field to determine the
+     * payload format.
+     * <p>
+     * Returns an empty byte array if this record
+     * does not have a type field.
+     */
+    public byte[] getType() {
+        return mType.clone();
+    }
+
+    /**
+     * Returns the variable length ID.
+     * <p>
+     * Returns an empty byte array if this record
+     * does not have an id field.
+     */
+    public byte[] getId() {
+        return mId.clone();
+    }
+
+    /**
+     * Returns the variable length payload.
+     * <p>
+     * Returns an empty byte array if this record
+     * does not have a payload field.
+     */
+    public byte[] getPayload() {
+        return mPayload.clone();
+    }
+
+    /**
+     * Return this NDEF Record as a byte array.<p>
+     * This method is deprecated, use {@link NdefMessage#toByteArray}
+     * instead. This is because the NDEF binary format is not defined for
+     * a record outside of the context of a message: the MB and ME flags
+     * cannot be set without knowing the location inside a message.<p>
+     * This implementation will attempt to serialize a single record by
+     * always setting the MB and ME flags (in other words, assume this
+     * is a single-record NDEF Message).<p>
+     *
+     * @deprecated use {@link NdefMessage#toByteArray()} instead
+     */
+    @Deprecated
+    public byte[] toByteArray() {
+        ByteBuffer buffer = ByteBuffer.allocate(getByteLength());
+        writeToByteBuffer(buffer, true, true);
+        return buffer.array();
+    }
+
+    /**
+     * Map this record to a MIME type, or return null if it cannot be mapped.<p>
+     * Currently this method considers all {@link #TNF_MIME_MEDIA} records to
+     * be MIME records, as well as some {@link #TNF_WELL_KNOWN} records such as
+     * {@link #RTD_TEXT}. If this is a MIME record then the MIME type as string
+     * is returned, otherwise null is returned.<p>
+     * This method does not perform validation that the MIME type is
+     * actually valid. It always attempts to
+     * return a string containing the type if this is a MIME record.<p>
+     * The returned MIME type will by normalized to lower-case using
+     * {@link Intent#normalizeMimeType}.<p>
+     * The MIME payload can be obtained using {@link #getPayload}.
+     *
+     * @return MIME type as a string, or null if this is not a MIME record
+     */
+    public String toMimeType() {
+        switch (mTnf) {
+            case NdefRecord.TNF_WELL_KNOWN:
+                if (Arrays.equals(mType, NdefRecord.RTD_TEXT)) {
+                    return "text/plain";
+                }
+                break;
+            case NdefRecord.TNF_MIME_MEDIA:
+                String mimeType = new String(mType, StandardCharsets.US_ASCII);
+                return Intent.normalizeMimeType(mimeType);
+        }
+        return null;
+    }
+
+    /**
+     * Map this record to a URI, or return null if it cannot be mapped.<p>
+     * Currently this method considers the following to be URI records:
+     * <ul>
+     * <li>{@link #TNF_ABSOLUTE_URI} records.</li>
+     * <li>{@link #TNF_WELL_KNOWN} with a type of {@link #RTD_URI}.</li>
+     * <li>{@link #TNF_WELL_KNOWN} with a type of {@link #RTD_SMART_POSTER}
+     * and containing a URI record in the NDEF message nested in the payload.
+     * </li>
+     * <li>{@link #TNF_EXTERNAL_TYPE} records.</li>
+     * </ul>
+     * If this is not a URI record by the above rules, then null is returned.<p>
+     * This method does not perform validation that the URI is
+     * actually valid: it always attempts to create and return a URI if
+     * this record appears to be a URI record by the above rules.<p>
+     * The returned URI will be normalized to have a lower case scheme
+     * using {@link Uri#normalizeScheme}.<p>
+     *
+     * @return URI, or null if this is not a URI record
+     */
+    public Uri toUri() {
+        return toUri(false);
+    }
+
+    private Uri toUri(boolean inSmartPoster) {
+        switch (mTnf) {
+            case TNF_WELL_KNOWN:
+                if (Arrays.equals(mType, RTD_SMART_POSTER) && !inSmartPoster) {
+                    try {
+                        // check payload for a nested NDEF Message containing a URI
+                        NdefMessage nestedMessage = new NdefMessage(mPayload);
+                        for (NdefRecord nestedRecord : nestedMessage.getRecords()) {
+                            Uri uri = nestedRecord.toUri(true);
+                            if (uri != null) {
+                                return uri;
+                            }
+                        }
+                    } catch (FormatException e) {  }
+                } else if (Arrays.equals(mType, RTD_URI)) {
+                    Uri wktUri = parseWktUri();
+                    return (wktUri != null ? wktUri.normalizeScheme() : null);
+                }
+                break;
+
+            case TNF_ABSOLUTE_URI:
+                Uri uri = Uri.parse(new String(mType, StandardCharsets.UTF_8));
+                return uri.normalizeScheme();
+
+            case TNF_EXTERNAL_TYPE:
+                if (inSmartPoster) {
+                    break;
+                }
+                return Uri.parse("vnd.android.nfc://ext/" + new String(mType, StandardCharsets.US_ASCII));
+        }
+        return null;
+    }
+
+    /**
+     * Return complete URI of {@link #TNF_WELL_KNOWN}, {@link #RTD_URI} records.
+     * @return complete URI, or null if invalid
+     */
+    private Uri parseWktUri() {
+        if (mPayload.length < 2) {
+            return null;
+        }
+
+        // payload[0] contains the URI Identifier Code, as per
+        // NFC Forum "URI Record Type Definition" section 3.2.2.
+        int prefixIndex = (mPayload[0] & (byte)0xFF);
+        if (prefixIndex < 0 || prefixIndex >= URI_PREFIX_MAP.length) {
+            return null;
+        }
+        String prefix = URI_PREFIX_MAP[prefixIndex];
+        String suffix = new String(Arrays.copyOfRange(mPayload, 1, mPayload.length),
+                StandardCharsets.UTF_8);
+        return Uri.parse(prefix + suffix);
+    }
+
+    /**
+     * Main record parsing method.<p>
+     * Expects NdefMessage to begin immediately, allows trailing data.<p>
+     * Currently has strict validation of all fields as per NDEF 1.0
+     * specification section 2.5. We will attempt to keep this as strict as
+     * possible to encourage well-formatted NDEF.<p>
+     * Always returns 1 or more NdefRecord's, or throws FormatException.
+     *
+     * @param buffer ByteBuffer to read from
+     * @param ignoreMbMe ignore MB and ME flags, and read only 1 complete record
+     * @return one or more records
+     * @throws FormatException on any parsing error
+     */
+    static NdefRecord[] parse(ByteBuffer buffer, boolean ignoreMbMe) throws FormatException {
+        List<NdefRecord> records = new ArrayList<NdefRecord>();
+
+        try {
+            byte[] type = null;
+            byte[] id = null;
+            byte[] payload = null;
+            ArrayList<byte[]> chunks = new ArrayList<byte[]>();
+            boolean inChunk = false;
+            short chunkTnf = -1;
+            boolean me = false;
+
+            while (!me) {
+                byte flag = buffer.get();
+
+                boolean mb = (flag & NdefRecord.FLAG_MB) != 0;
+                me = (flag & NdefRecord.FLAG_ME) != 0;
+                boolean cf = (flag & NdefRecord.FLAG_CF) != 0;
+                boolean sr = (flag & NdefRecord.FLAG_SR) != 0;
+                boolean il = (flag & NdefRecord.FLAG_IL) != 0;
+                short tnf = (short)(flag & 0x07);
+
+                if (!mb && records.size() == 0 && !inChunk && !ignoreMbMe) {
+                    throw new FormatException("expected MB flag");
+                } else if (mb && (records.size() != 0 || inChunk) && !ignoreMbMe) {
+                    throw new FormatException("unexpected MB flag");
+                } else if (inChunk && il) {
+                    throw new FormatException("unexpected IL flag in non-leading chunk");
+                } else if (cf && me) {
+                    throw new FormatException("unexpected ME flag in non-trailing chunk");
+                } else if (inChunk && tnf != NdefRecord.TNF_UNCHANGED) {
+                    throw new FormatException("expected TNF_UNCHANGED in non-leading chunk");
+                } else if (!inChunk && tnf == NdefRecord.TNF_UNCHANGED) {
+                    throw new FormatException("" +
+                            "unexpected TNF_UNCHANGED in first chunk or unchunked record");
+                }
+
+                int typeLength = buffer.get() & 0xFF;
+                long payloadLength = sr ? (buffer.get() & 0xFF) : (buffer.getInt() & 0xFFFFFFFFL);
+                int idLength = il ? (buffer.get() & 0xFF) : 0;
+
+                if (inChunk && typeLength != 0) {
+                    throw new FormatException("expected zero-length type in non-leading chunk");
+                }
+
+                if (!inChunk) {
+                    type = (typeLength > 0 ? new byte[typeLength] : EMPTY_BYTE_ARRAY);
+                    id = (idLength > 0 ? new byte[idLength] : EMPTY_BYTE_ARRAY);
+                    buffer.get(type);
+                    buffer.get(id);
+                }
+
+                ensureSanePayloadSize(payloadLength);
+                payload = (payloadLength > 0 ? new byte[(int)payloadLength] : EMPTY_BYTE_ARRAY);
+                buffer.get(payload);
+
+                if (cf && !inChunk) {
+                    // first chunk
+                    if (typeLength == 0 && tnf != NdefRecord.TNF_UNKNOWN) {
+                        throw new FormatException("expected non-zero type length in first chunk");
+                    }
+                    chunks.clear();
+                    chunkTnf = tnf;
+                }
+                if (cf || inChunk) {
+                    // any chunk
+                    chunks.add(payload);
+                }
+                if (!cf && inChunk) {
+                    // last chunk, flatten the payload
+                    payloadLength = 0;
+                    for (byte[] p : chunks) {
+                        payloadLength += p.length;
+                    }
+                    ensureSanePayloadSize(payloadLength);
+                    payload = new byte[(int)payloadLength];
+                    int i = 0;
+                    for (byte[] p : chunks) {
+                        System.arraycopy(p, 0, payload, i, p.length);
+                        i += p.length;
+                    }
+                    tnf = chunkTnf;
+                }
+                if (cf) {
+                    // more chunks to come
+                    inChunk = true;
+                    continue;
+                } else {
+                    inChunk = false;
+                }
+
+                String error = validateTnf(tnf, type, id, payload);
+                if (error != null) {
+                    throw new FormatException(error);
+                }
+                records.add(new NdefRecord(tnf, type, id, payload));
+                if (ignoreMbMe) {  // for parsing a single NdefRecord
+                    break;
+                }
+            }
+        } catch (BufferUnderflowException e) {
+            throw new FormatException("expected more data", e);
+        }
+        return records.toArray(new NdefRecord[records.size()]);
+    }
+
+    private static void ensureSanePayloadSize(long size) throws FormatException {
+        if (size > MAX_PAYLOAD_SIZE) {
+            throw new FormatException(
+                    "payload above max limit: " + size + " > " + MAX_PAYLOAD_SIZE);
+        }
+    }
+
+    /**
+     * Perform simple validation that the tnf is valid.<p>
+     * Validates the requirements of NFCForum-TS-NDEF_1.0 section
+     * 3.2.6 (Type Name Format). This just validates that the tnf
+     * is valid, and that the relevant type, id and payload
+     * fields are present (or empty) for this tnf. It does not
+     * perform any deep inspection of the type, id and payload fields.<p>
+     * Also does not allow TNF_UNCHANGED since this class is only used
+     * to present logical (unchunked) records.
+     *
+     * @return null if valid, or a string error if invalid.
+     */
+    static String validateTnf(short tnf, byte[] type, byte[] id, byte[] payload) {
+        switch (tnf) {
+            case TNF_EMPTY:
+                if (type.length != 0 || id.length != 0 || payload.length != 0) {
+                    return "unexpected data in TNF_EMPTY record";
+                }
+                return null;
+            case TNF_WELL_KNOWN:
+            case TNF_MIME_MEDIA:
+            case TNF_ABSOLUTE_URI:
+            case TNF_EXTERNAL_TYPE:
+                return null;
+            case TNF_UNKNOWN:
+            case TNF_RESERVED:
+                if (type.length != 0) {
+                    return "unexpected type field in TNF_UNKNOWN or TNF_RESERVEd record";
+                }
+                return null;
+            case TNF_UNCHANGED:
+                return "unexpected TNF_UNCHANGED in first chunk or logical record";
+            default:
+                return String.format("unexpected tnf value: 0x%02x", tnf);
+        }
+    }
+
+    /**
+     * Serialize record for network transmission.<p>
+     * Uses specified MB and ME flags.<p>
+     * Does not chunk records.
+     */
+    void writeToByteBuffer(ByteBuffer buffer, boolean mb, boolean me) {
+        boolean sr = mPayload.length < 256;
+        boolean il = mTnf == TNF_EMPTY ? true : mId.length > 0;
+
+        byte flags = (byte)((mb ? FLAG_MB : 0) | (me ? FLAG_ME : 0) |
+                (sr ? FLAG_SR : 0) | (il ? FLAG_IL : 0) | mTnf);
+        buffer.put(flags);
+
+        buffer.put((byte)mType.length);
+        if (sr) {
+            buffer.put((byte)mPayload.length);
+        } else {
+            buffer.putInt(mPayload.length);
+        }
+        if (il) {
+            buffer.put((byte)mId.length);
+        }
+
+        buffer.put(mType);
+        buffer.put(mId);
+        buffer.put(mPayload);
+    }
+
+    /**
+     * Get byte length of serialized record.
+     */
+    int getByteLength() {
+        int length = 3 + mType.length + mId.length + mPayload.length;
+
+        boolean sr = mPayload.length < 256;
+        boolean il = mTnf == TNF_EMPTY ? true : mId.length > 0;
+
+        if (!sr) length += 3;
+        if (il) length += 1;
+
+        return length;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mTnf);
+        dest.writeInt(mType.length);
+        dest.writeByteArray(mType);
+        dest.writeInt(mId.length);
+        dest.writeByteArray(mId);
+        dest.writeInt(mPayload.length);
+        dest.writeByteArray(mPayload);
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<NdefRecord> CREATOR =
+            new Parcelable.Creator<NdefRecord>() {
+        @Override
+        public NdefRecord createFromParcel(Parcel in) {
+            short tnf = (short)in.readInt();
+            int typeLength = in.readInt();
+            byte[] type = new byte[typeLength];
+            in.readByteArray(type);
+            int idLength = in.readInt();
+            byte[] id = new byte[idLength];
+            in.readByteArray(id);
+            int payloadLength = in.readInt();
+            byte[] payload = new byte[payloadLength];
+            in.readByteArray(payload);
+
+            return new NdefRecord(tnf, type, id, payload);
+        }
+        @Override
+        public NdefRecord[] newArray(int size) {
+            return new NdefRecord[size];
+        }
+    };
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + Arrays.hashCode(mId);
+        result = prime * result + Arrays.hashCode(mPayload);
+        result = prime * result + mTnf;
+        result = prime * result + Arrays.hashCode(mType);
+        return result;
+    }
+
+    /**
+     * Returns true if the specified NDEF Record contains
+     * identical tnf, type, id and payload fields.
+     */
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (this == obj) return true;
+        if (obj == null) return false;
+        if (getClass() != obj.getClass()) return false;
+        NdefRecord other = (NdefRecord) obj;
+        if (!Arrays.equals(mId, other.mId)) return false;
+        if (!Arrays.equals(mPayload, other.mPayload)) return false;
+        if (mTnf != other.mTnf) return false;
+        return Arrays.equals(mType, other.mType);
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder b = new StringBuilder(String.format("NdefRecord tnf=%X", mTnf));
+        if (mType.length > 0) b.append(" type=").append(bytesToString(mType));
+        if (mId.length > 0) b.append(" id=").append(bytesToString(mId));
+        if (mPayload.length > 0) b.append(" payload=").append(bytesToString(mPayload));
+        return b.toString();
+    }
+
+    /**
+     * Dump debugging information as a NdefRecordProto
+     * @hide
+     *
+     * Note:
+     * See proto definition in frameworks/base/core/proto/android/nfc/ndef.proto
+     * When writing a nested message, must call {@link ProtoOutputStream#start(long)} before and
+     * {@link ProtoOutputStream#end(long)} after.
+     * Never reuse a proto field number. When removing a field, mark it as reserved.
+     */
+    public void dumpDebug(ProtoOutputStream proto) {
+        proto.write(NdefRecordProto.TYPE, mType);
+        proto.write(NdefRecordProto.ID, mId);
+        proto.write(NdefRecordProto.PAYLOAD_BYTES, mPayload.length);
+    }
+
+    private static StringBuilder bytesToString(byte[] bs) {
+        StringBuilder s = new StringBuilder();
+        for (byte b : bs) {
+            s.append(String.format("%02X", b));
+        }
+        return s;
+    }
+}
diff --git a/nfc/java/android/nfc/NfcActivityManager.java b/nfc/java/android/nfc/NfcActivityManager.java
new file mode 100644
index 0000000..f03fc0a
--- /dev/null
+++ b/nfc/java/android/nfc/NfcActivityManager.java
@@ -0,0 +1,410 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc;
+
+import android.app.Activity;
+import android.app.Application;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.nfc.NfcAdapter.ReaderCallback;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Manages NFC API's that are coupled to the life-cycle of an Activity.
+ *
+ * <p>Uses {@link Application#registerActivityLifecycleCallbacks} to hook
+ * into activity life-cycle events such as onPause() and onResume().
+ *
+ * @hide
+ */
+public final class NfcActivityManager extends IAppCallback.Stub
+        implements Application.ActivityLifecycleCallbacks {
+    static final String TAG = NfcAdapter.TAG;
+    static final Boolean DBG = false;
+
+    @UnsupportedAppUsage
+    final NfcAdapter mAdapter;
+
+    // All objects in the lists are protected by this
+    final List<NfcApplicationState> mApps;  // Application(s) that have NFC state. Usually one
+    final List<NfcActivityState> mActivities;  // Activities that have NFC state
+
+    /**
+     * NFC State associated with an {@link Application}.
+     */
+    class NfcApplicationState {
+        int refCount = 0;
+        final Application app;
+        public NfcApplicationState(Application app) {
+            this.app = app;
+        }
+        public void register() {
+            refCount++;
+            if (refCount == 1) {
+                this.app.registerActivityLifecycleCallbacks(NfcActivityManager.this);
+            }
+        }
+        public void unregister() {
+            refCount--;
+            if (refCount == 0) {
+                this.app.unregisterActivityLifecycleCallbacks(NfcActivityManager.this);
+            } else if (refCount < 0) {
+                Log.e(TAG, "-ve refcount for " + app);
+            }
+        }
+    }
+
+    NfcApplicationState findAppState(Application app) {
+        for (NfcApplicationState appState : mApps) {
+            if (appState.app == app) {
+                return appState;
+            }
+        }
+        return null;
+    }
+
+    void registerApplication(Application app) {
+        NfcApplicationState appState = findAppState(app);
+        if (appState == null) {
+            appState = new NfcApplicationState(app);
+            mApps.add(appState);
+        }
+        appState.register();
+    }
+
+    void unregisterApplication(Application app) {
+        NfcApplicationState appState = findAppState(app);
+        if (appState == null) {
+            Log.e(TAG, "app was not registered " + app);
+            return;
+        }
+        appState.unregister();
+    }
+
+    /**
+     * NFC state associated with an {@link Activity}
+     */
+    class NfcActivityState {
+        boolean resumed = false;
+        Activity activity;
+        NfcAdapter.ReaderCallback readerCallback = null;
+        int readerModeFlags = 0;
+        Bundle readerModeExtras = null;
+        Binder token;
+
+        int mPollTech = NfcAdapter.FLAG_USE_ALL_TECH;
+        int mListenTech = NfcAdapter.FLAG_USE_ALL_TECH;
+
+        public NfcActivityState(Activity activity) {
+            if (activity.isDestroyed()) {
+                throw new IllegalStateException("activity is already destroyed");
+            }
+            // Check if activity is resumed right now, as we will not
+            // immediately get a callback for that.
+            resumed = activity.isResumed();
+
+            this.activity = activity;
+            this.token = new Binder();
+            registerApplication(activity.getApplication());
+        }
+        public void destroy() {
+            unregisterApplication(activity.getApplication());
+            resumed = false;
+            activity = null;
+            readerCallback = null;
+            readerModeFlags = 0;
+            readerModeExtras = null;
+            token = null;
+
+            mPollTech = NfcAdapter.FLAG_USE_ALL_TECH;
+            mListenTech = NfcAdapter.FLAG_USE_ALL_TECH;
+        }
+        @Override
+        public String toString() {
+            StringBuilder s = new StringBuilder("[");
+            s.append(readerCallback);
+            s.append("]");
+            return s.toString();
+        }
+    }
+
+    /** find activity state from mActivities */
+    synchronized NfcActivityState findActivityState(Activity activity) {
+        for (NfcActivityState state : mActivities) {
+            if (state.activity == activity) {
+                return state;
+            }
+        }
+        return null;
+    }
+
+    /** find or create activity state from mActivities */
+    synchronized NfcActivityState getActivityState(Activity activity) {
+        NfcActivityState state = findActivityState(activity);
+        if (state == null) {
+            state = new NfcActivityState(activity);
+            mActivities.add(state);
+        }
+        return state;
+    }
+
+    synchronized NfcActivityState findResumedActivityState() {
+        for (NfcActivityState state : mActivities) {
+            if (state.resumed) {
+                return state;
+            }
+        }
+        return null;
+    }
+
+    synchronized void destroyActivityState(Activity activity) {
+        NfcActivityState activityState = findActivityState(activity);
+        if (activityState != null) {
+            activityState.destroy();
+            mActivities.remove(activityState);
+        }
+    }
+
+    public NfcActivityManager(NfcAdapter adapter) {
+        mAdapter = adapter;
+        mActivities = new LinkedList<NfcActivityState>();
+        mApps = new ArrayList<NfcApplicationState>(1);  // Android VM usually has 1 app
+    }
+
+    public void enableReaderMode(Activity activity, ReaderCallback callback, int flags,
+            Bundle extras) {
+        boolean isResumed;
+        Binder token;
+        synchronized (NfcActivityManager.this) {
+            NfcActivityState state = getActivityState(activity);
+            state.readerCallback = callback;
+            state.readerModeFlags = flags;
+            state.readerModeExtras = extras;
+            token = state.token;
+            isResumed = state.resumed;
+        }
+        if (isResumed) {
+            setReaderMode(token, flags, extras);
+        }
+    }
+
+    public void disableReaderMode(Activity activity) {
+        boolean isResumed;
+        Binder token;
+        synchronized (NfcActivityManager.this) {
+            NfcActivityState state = getActivityState(activity);
+            state.readerCallback = null;
+            state.readerModeFlags = 0;
+            state.readerModeExtras = null;
+            token = state.token;
+            isResumed = state.resumed;
+        }
+        if (isResumed) {
+            setReaderMode(token, 0, null);
+        }
+
+    }
+
+    public void setReaderMode(Binder token, int flags, Bundle extras) {
+        if (DBG) Log.d(TAG, "Setting reader mode");
+        try {
+            NfcAdapter.sService.setReaderMode(token, this, flags, extras);
+        } catch (RemoteException e) {
+            mAdapter.attemptDeadServiceRecovery(e);
+        }
+    }
+
+    /**
+     * Request or unrequest NFC service callbacks.
+     * Makes IPC call - do not hold lock.
+     */
+    void requestNfcServiceCallback() {
+        try {
+            NfcAdapter.sService.setAppCallback(this);
+        } catch (RemoteException e) {
+            mAdapter.attemptDeadServiceRecovery(e);
+        }
+    }
+
+    void verifyNfcPermission() {
+        try {
+            NfcAdapter.sService.verifyNfcPermission();
+        } catch (RemoteException e) {
+            mAdapter.attemptDeadServiceRecovery(e);
+        }
+    }
+
+    @Override
+    public void onTagDiscovered(Tag tag) throws RemoteException {
+        NfcAdapter.ReaderCallback callback;
+        synchronized (NfcActivityManager.this) {
+            NfcActivityState state = findResumedActivityState();
+            if (state == null) return;
+
+            callback = state.readerCallback;
+        }
+
+        // Make callback without lock
+        if (callback != null) {
+            callback.onTagDiscovered(tag);
+        }
+
+    }
+    /** Callback from Activity life-cycle, on main thread */
+    @Override
+    public void onActivityCreated(Activity activity, Bundle savedInstanceState) { /* NO-OP */ }
+
+    /** Callback from Activity life-cycle, on main thread */
+    @Override
+    public void onActivityStarted(Activity activity) { /* NO-OP */ }
+
+    /** Callback from Activity life-cycle, on main thread */
+    @Override
+    public void onActivityResumed(Activity activity) {
+        int readerModeFlags = 0;
+        Bundle readerModeExtras = null;
+        Binder token;
+        int pollTech;
+        int listenTech;
+
+        synchronized (NfcActivityManager.this) {
+            NfcActivityState state = findActivityState(activity);
+            if (DBG) Log.d(TAG, "onResume() for " + activity + " " + state);
+            if (state == null) return;
+            state.resumed = true;
+            token = state.token;
+            readerModeFlags = state.readerModeFlags;
+            readerModeExtras = state.readerModeExtras;
+
+            pollTech = state.mPollTech;
+            listenTech = state.mListenTech;
+        }
+        if (readerModeFlags != 0) {
+            setReaderMode(token, readerModeFlags, readerModeExtras);
+        } else if (listenTech != NfcAdapter.FLAG_USE_ALL_TECH
+                || pollTech != NfcAdapter.FLAG_USE_ALL_TECH) {
+            changeDiscoveryTech(token, pollTech, listenTech);
+        }
+        requestNfcServiceCallback();
+    }
+
+    /** Callback from Activity life-cycle, on main thread */
+    @Override
+    public void onActivityPaused(Activity activity) {
+        boolean readerModeFlagsSet;
+        Binder token;
+        int pollTech;
+        int listenTech;
+
+        synchronized (NfcActivityManager.this) {
+            NfcActivityState state = findActivityState(activity);
+            if (DBG) Log.d(TAG, "onPause() for " + activity + " " + state);
+            if (state == null) return;
+            state.resumed = false;
+            token = state.token;
+            readerModeFlagsSet = state.readerModeFlags != 0;
+
+            pollTech = state.mPollTech;
+            listenTech = state.mListenTech;
+        }
+        if (readerModeFlagsSet) {
+            // Restore default p2p modes
+            setReaderMode(token, 0, null);
+        } else if (listenTech != NfcAdapter.FLAG_USE_ALL_TECH
+                || pollTech != NfcAdapter.FLAG_USE_ALL_TECH) {
+            changeDiscoveryTech(token,
+                    NfcAdapter.FLAG_USE_ALL_TECH, NfcAdapter.FLAG_USE_ALL_TECH);
+        }
+    }
+
+    /** Callback from Activity life-cycle, on main thread */
+    @Override
+    public void onActivityStopped(Activity activity) { /* NO-OP */ }
+
+    /** Callback from Activity life-cycle, on main thread */
+    @Override
+    public void onActivitySaveInstanceState(Activity activity, Bundle outState) { /* NO-OP */ }
+
+    /** Callback from Activity life-cycle, on main thread */
+    @Override
+    public void onActivityDestroyed(Activity activity) {
+        synchronized (NfcActivityManager.this) {
+            NfcActivityState state = findActivityState(activity);
+            if (DBG) Log.d(TAG, "onDestroy() for " + activity + " " + state);
+            if (state != null) {
+                // release all associated references
+                destroyActivityState(activity);
+            }
+        }
+    }
+
+    /** setDiscoveryTechnology() implementation */
+    public void setDiscoveryTech(Activity activity, int pollTech, int listenTech) {
+        boolean isResumed;
+        Binder token;
+        boolean readerModeFlagsSet;
+        synchronized (NfcActivityManager.this) {
+            NfcActivityState state = getActivityState(activity);
+            readerModeFlagsSet = state.readerModeFlags != 0;
+            state.mListenTech = listenTech;
+            state.mPollTech = pollTech;
+            token = state.token;
+            isResumed = state.resumed;
+        }
+        if (!readerModeFlagsSet && isResumed) {
+            changeDiscoveryTech(token, pollTech, listenTech);
+        } else if (readerModeFlagsSet) {
+            throw new IllegalStateException("Cannot be used when the Reader Mode is enabled");
+        }
+    }
+
+    /** resetDiscoveryTechnology() implementation */
+    public void resetDiscoveryTech(Activity activity) {
+        boolean isResumed;
+        Binder token;
+        boolean readerModeFlagsSet;
+        synchronized (NfcActivityManager.this) {
+            NfcActivityState state = getActivityState(activity);
+            readerModeFlagsSet = state.readerModeFlags != 0;
+            state.mListenTech = NfcAdapter.FLAG_USE_ALL_TECH;
+            state.mPollTech = NfcAdapter.FLAG_USE_ALL_TECH;
+            token = state.token;
+            isResumed = state.resumed;
+        }
+        if (readerModeFlagsSet) {
+            disableReaderMode(activity);
+        } else if (isResumed) {
+            changeDiscoveryTech(token, NfcAdapter.FLAG_USE_ALL_TECH, NfcAdapter.FLAG_USE_ALL_TECH);
+        }
+
+    }
+
+    private void changeDiscoveryTech(Binder token, int pollTech, int listenTech) {
+        try {
+            NfcAdapter.sService.updateDiscoveryTechnology(token, pollTech, listenTech);
+        } catch (RemoteException e) {
+            mAdapter.attemptDeadServiceRecovery(e);
+        }
+    }
+
+}
diff --git a/nfc/java/android/nfc/NfcAdapter.java b/nfc/java/android/nfc/NfcAdapter.java
new file mode 100644
index 0000000..75f5491
--- /dev/null
+++ b/nfc/java/android/nfc/NfcAdapter.java
@@ -0,0 +1,2909 @@
+/*
+ * 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 android.nfc;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.annotation.UserIdInt;
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.nfc.tech.MifareClassic;
+import android.nfc.tech.Ndef;
+import android.nfc.tech.NfcA;
+import android.nfc.tech.NfcF;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * Represents the local NFC adapter.
+ * <p>
+ * Use the helper {@link #getDefaultAdapter(Context)} to get the default NFC
+ * adapter for this Android device.
+ *
+ * <div class="special reference">
+ * <h3>Developer Guides</h3>
+ * <p>For more information about using NFC, read the
+ * <a href="{@docRoot}guide/topics/nfc/index.html">Near Field Communication</a> developer guide.</p>
+ * <p>To perform basic file sharing between devices, read
+ * <a href="{@docRoot}training/beam-files/index.html">Sharing Files with NFC</a>.
+ * </div>
+ */
+public final class NfcAdapter {
+    static final String TAG = "NFC";
+
+    private final NfcControllerAlwaysOnListener mControllerAlwaysOnListener;
+    private final NfcWlcStateListener mNfcWlcStateListener;
+
+    /**
+     * Intent to start an activity when a tag with NDEF payload is discovered.
+     *
+     * <p>The system inspects the first {@link NdefRecord} in the first {@link NdefMessage} and
+     * looks for a URI, SmartPoster, or MIME record. If a URI or SmartPoster record is found the
+     * intent will contain the URI in its data field. If a MIME record is found the intent will
+     * contain the MIME type in its type field. This allows activities to register
+     * {@link IntentFilter}s targeting specific content on tags. Activities should register the
+     * most specific intent filters possible to avoid the activity chooser dialog, which can
+     * disrupt the interaction with the tag as the user interacts with the screen.
+     *
+     * <p>If the tag has an NDEF payload this intent is started before
+     * {@link #ACTION_TECH_DISCOVERED}. If any activities respond to this intent neither
+     * {@link #ACTION_TECH_DISCOVERED} or {@link #ACTION_TAG_DISCOVERED} will be started.
+     *
+     * <p>The MIME type or data URI of this intent are normalized before dispatch -
+     * so that MIME, URI scheme and URI host are always lower-case.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_NDEF_DISCOVERED = "android.nfc.action.NDEF_DISCOVERED";
+
+    /**
+     * Intent to start an activity when a tag is discovered and activities are registered for the
+     * specific technologies on the tag.
+     *
+     * <p>To receive this intent an activity must include an intent filter
+     * for this action and specify the desired tech types in a
+     * manifest <code>meta-data</code> entry. Here is an example manfiest entry:
+     * <pre>
+     * &lt;activity android:name=".nfc.TechFilter" android:label="NFC/TechFilter"&gt;
+     *     &lt;!-- Add a technology filter --&gt;
+     *     &lt;intent-filter&gt;
+     *         &lt;action android:name="android.nfc.action.TECH_DISCOVERED" /&gt;
+     *     &lt;/intent-filter&gt;
+     *
+     *     &lt;meta-data android:name="android.nfc.action.TECH_DISCOVERED"
+     *         android:resource="@xml/filter_nfc"
+     *     /&gt;
+     * &lt;/activity&gt;</pre>
+     *
+     * <p>The meta-data XML file should contain one or more <code>tech-list</code> entries
+     * each consisting or one or more <code>tech</code> entries. The <code>tech</code> entries refer
+     * to the qualified class name implementing the technology, for example "android.nfc.tech.NfcA".
+     *
+     * <p>A tag matches if any of the
+     * <code>tech-list</code> sets is a subset of {@link Tag#getTechList() Tag.getTechList()}. Each
+     * of the <code>tech-list</code>s is considered independently and the
+     * activity is considered a match is any single <code>tech-list</code> matches the tag that was
+     * discovered. This provides AND and OR semantics for filtering desired techs. Here is an
+     * example that will match any tag using {@link NfcF} or any tag using {@link NfcA},
+     * {@link MifareClassic}, and {@link Ndef}:
+     *
+     * <pre>
+     * &lt;resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"&gt;
+     *     &lt;!-- capture anything using NfcF --&gt;
+     *     &lt;tech-list&gt;
+     *         &lt;tech&gt;android.nfc.tech.NfcF&lt;/tech&gt;
+     *     &lt;/tech-list&gt;
+     *
+     *     &lt;!-- OR --&gt;
+     *
+     *     &lt;!-- capture all MIFARE Classics with NDEF payloads --&gt;
+     *     &lt;tech-list&gt;
+     *         &lt;tech&gt;android.nfc.tech.NfcA&lt;/tech&gt;
+     *         &lt;tech&gt;android.nfc.tech.MifareClassic&lt;/tech&gt;
+     *         &lt;tech&gt;android.nfc.tech.Ndef&lt;/tech&gt;
+     *     &lt;/tech-list&gt;
+     * &lt;/resources&gt;</pre>
+     *
+     * <p>This intent is started after {@link #ACTION_NDEF_DISCOVERED} and before
+     * {@link #ACTION_TAG_DISCOVERED}. If any activities respond to {@link #ACTION_NDEF_DISCOVERED}
+     * this intent will not be started. If any activities respond to this intent
+     * {@link #ACTION_TAG_DISCOVERED} will not be started.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_TECH_DISCOVERED = "android.nfc.action.TECH_DISCOVERED";
+
+    /**
+     * Intent to start an activity when a tag is discovered.
+     *
+     * <p>This intent will not be started when a tag is discovered if any activities respond to
+     * {@link #ACTION_NDEF_DISCOVERED} or {@link #ACTION_TECH_DISCOVERED} for the current tag.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_TAG_DISCOVERED = "android.nfc.action.TAG_DISCOVERED";
+
+    /**
+     * Broadcast Action: Intent to notify an application that a transaction event has occurred
+     * on the Secure Element.
+     *
+     * <p>This intent will only be sent if the application has requested permission for
+     * {@link android.Manifest.permission#NFC_TRANSACTION_EVENT} and if the application has the
+     * necessary access to Secure Element which witnessed the particular event.
+     */
+    @RequiresPermission(android.Manifest.permission.NFC_TRANSACTION_EVENT)
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_TRANSACTION_DETECTED =
+            "android.nfc.action.TRANSACTION_DETECTED";
+
+    /**
+     * Broadcast Action: Intent to notify if the preferred payment service changed.
+     *
+     * <p>This intent will only be sent to the application has requested permission for
+     * {@link android.Manifest.permission#NFC_PREFERRED_PAYMENT_INFO} and if the application
+     * has the necessary access to Secure Element which witnessed the particular event.
+     */
+    @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO)
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_PREFERRED_PAYMENT_CHANGED =
+            "android.nfc.action.PREFERRED_PAYMENT_CHANGED";
+
+    /**
+     * Broadcast to only the activity that handles ACTION_TAG_DISCOVERED
+     * @hide
+     */
+    public static final String ACTION_TAG_LEFT_FIELD = "android.nfc.action.TAG_LOST";
+
+    /**
+     * Mandatory extra containing the {@link Tag} that was discovered for the
+     * {@link #ACTION_NDEF_DISCOVERED}, {@link #ACTION_TECH_DISCOVERED}, and
+     * {@link #ACTION_TAG_DISCOVERED} intents.
+     */
+    public static final String EXTRA_TAG = "android.nfc.extra.TAG";
+
+    /**
+     * Extra containing an array of {@link NdefMessage} present on the discovered tag.<p>
+     * This extra is mandatory for {@link #ACTION_NDEF_DISCOVERED} intents,
+     * and optional for {@link #ACTION_TECH_DISCOVERED}, and
+     * {@link #ACTION_TAG_DISCOVERED} intents.<p>
+     * When this extra is present there will always be at least one
+     * {@link NdefMessage} element. Most NDEF tags have only one NDEF message,
+     * but we use an array for future compatibility.
+     */
+    public static final String EXTRA_NDEF_MESSAGES = "android.nfc.extra.NDEF_MESSAGES";
+
+    /**
+     * Optional extra containing a byte array containing the ID of the discovered tag for
+     * the {@link #ACTION_NDEF_DISCOVERED}, {@link #ACTION_TECH_DISCOVERED}, and
+     * {@link #ACTION_TAG_DISCOVERED} intents.
+     */
+    public static final String EXTRA_ID = "android.nfc.extra.ID";
+
+    /**
+     * Broadcast Action: The state of the local NFC adapter has been
+     * changed.
+     * <p>For example, NFC has been turned on or off.
+     * <p>Always contains the extra field {@link #EXTRA_ADAPTER_STATE}
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_ADAPTER_STATE_CHANGED =
+            "android.nfc.action.ADAPTER_STATE_CHANGED";
+
+    /**
+     * Used as an int extra field in {@link #ACTION_ADAPTER_STATE_CHANGED}
+     * intents to request the current power state. Possible values are:
+     * {@link #STATE_OFF},
+     * {@link #STATE_TURNING_ON},
+     * {@link #STATE_ON},
+     * {@link #STATE_TURNING_OFF},
+     */
+    public static final String EXTRA_ADAPTER_STATE = "android.nfc.extra.ADAPTER_STATE";
+
+    /**
+     * Mandatory byte[] extra field in {@link #ACTION_TRANSACTION_DETECTED}
+     */
+    public static final String EXTRA_AID = "android.nfc.extra.AID";
+
+    /**
+     * Optional byte[] extra field in {@link #ACTION_TRANSACTION_DETECTED}
+     */
+    public static final String EXTRA_DATA = "android.nfc.extra.DATA";
+
+    /**
+     * Mandatory String extra field in {@link #ACTION_TRANSACTION_DETECTED}
+     * Indicates the Secure Element on which the transaction occurred.
+     * eSE1...eSEn for Embedded Secure Elements, SIM1...SIMn for UICC, etc.
+     */
+    public static final String EXTRA_SECURE_ELEMENT_NAME = "android.nfc.extra.SECURE_ELEMENT_NAME";
+
+    /**
+     * Mandatory String extra field in {@link #ACTION_PREFERRED_PAYMENT_CHANGED}
+     * Indicates the condition when trigger this event. Possible values are:
+     * {@link #PREFERRED_PAYMENT_LOADED},
+     * {@link #PREFERRED_PAYMENT_CHANGED},
+     * {@link #PREFERRED_PAYMENT_UPDATED},
+     */
+    public static final String EXTRA_PREFERRED_PAYMENT_CHANGED_REASON =
+            "android.nfc.extra.PREFERRED_PAYMENT_CHANGED_REASON";
+    /**
+     * Nfc is enabled and the preferred payment aids are registered.
+     */
+    public static final int PREFERRED_PAYMENT_LOADED = 1;
+    /**
+     * User selected another payment application as the preferred payment.
+     */
+    public static final int PREFERRED_PAYMENT_CHANGED = 2;
+    /**
+     * Current preferred payment has issued an update (registered/unregistered new aids or has been
+     * updated itself).
+     */
+    public static final int PREFERRED_PAYMENT_UPDATED = 3;
+
+    public static final int STATE_OFF = 1;
+    public static final int STATE_TURNING_ON = 2;
+    public static final int STATE_ON = 3;
+    public static final int STATE_TURNING_OFF = 4;
+
+    /**
+     * Possible states from {@link #getAdapterState}.
+     *
+     * @hide
+     */
+    @IntDef(prefix = { "STATE_" }, value = {
+            STATE_OFF,
+            STATE_TURNING_ON,
+            STATE_ON,
+            STATE_TURNING_OFF
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AdapterState{}
+
+    /**
+     * Flag for use with {@link #enableReaderMode(Activity, ReaderCallback, int, Bundle)}.
+     * <p>
+     * Setting this flag enables polling for Nfc-A technology.
+     */
+    public static final int FLAG_READER_NFC_A = 0x1;
+
+    /**
+     * Flag for use with {@link #enableReaderMode(Activity, ReaderCallback, int, Bundle)}.
+     * <p>
+     * Setting this flag enables polling for Nfc-B technology.
+     */
+    public static final int FLAG_READER_NFC_B = 0x2;
+
+    /**
+     * Flag for use with {@link #enableReaderMode(Activity, ReaderCallback, int, Bundle)}.
+     * <p>
+     * Setting this flag enables polling for Nfc-F technology.
+     */
+    public static final int FLAG_READER_NFC_F = 0x4;
+
+    /**
+     * Flag for use with {@link #enableReaderMode(Activity, ReaderCallback, int, Bundle)}.
+     * <p>
+     * Setting this flag enables polling for Nfc-V (ISO15693) technology.
+     */
+    public static final int FLAG_READER_NFC_V = 0x8;
+
+    /**
+     * Flag for use with {@link #enableReaderMode(Activity, ReaderCallback, int, Bundle)}.
+     * <p>
+     * Setting this flag enables polling for NfcBarcode technology.
+     */
+    public static final int FLAG_READER_NFC_BARCODE = 0x10;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = {"FLAG_READER_"}, value = {
+        FLAG_READER_KEEP,
+        FLAG_READER_DISABLE,
+        FLAG_READER_NFC_A,
+        FLAG_READER_NFC_B,
+        FLAG_READER_NFC_F,
+        FLAG_READER_NFC_V,
+        FLAG_READER_NFC_BARCODE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PollTechnology {}
+
+    /**
+     * Flag for use with {@link #enableReaderMode(Activity, ReaderCallback, int, Bundle)}.
+     * <p>
+     * Setting this flag allows the caller to prevent the
+     * platform from performing an NDEF check on the tags it
+     * finds.
+     */
+    public static final int FLAG_READER_SKIP_NDEF_CHECK = 0x80;
+
+    /**
+     * Flag for use with {@link #enableReaderMode(Activity, ReaderCallback, int, Bundle)}.
+     * <p>
+     * Setting this flag allows the caller to prevent the
+     * platform from playing sounds when it discovers a tag.
+     */
+    public static final int FLAG_READER_NO_PLATFORM_SOUNDS = 0x100;
+
+    /**
+     * Int Extra for use with {@link #enableReaderMode(Activity, ReaderCallback, int, Bundle)}.
+     * <p>
+     * Setting this integer extra allows the calling application to specify
+     * the delay that the platform will use for performing presence checks
+     * on any discovered tag.
+     */
+    public static final String EXTRA_READER_PRESENCE_CHECK_DELAY = "presence";
+
+    /**
+     * Flag for use with {@link #setDiscoveryTechnology(Activity, int, int)}.
+     * <p>
+     * Setting this flag enables listening for Nfc-A technology.
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH)
+    public static final int FLAG_LISTEN_NFC_PASSIVE_A = 0x1;
+
+    /**
+     * Flag for use with {@link #setDiscoveryTechnology(Activity, int, int)}.
+     * <p>
+     * Setting this flag enables listening for Nfc-B technology.
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH)
+    public static final int FLAG_LISTEN_NFC_PASSIVE_B = 1 << 1;
+
+    /**
+     * Flag for use with {@link #setDiscoveryTechnology(Activity, int, int)}.
+     * <p>
+     * Setting this flag enables listening for Nfc-F technology.
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH)
+    public static final int FLAG_LISTEN_NFC_PASSIVE_F = 1 << 2;
+
+    /**
+     * Flags for use with {@link #setDiscoveryTechnology(Activity, int, int)}.
+     * <p>
+     * Setting this flag disables listening.
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH)
+    public static final int FLAG_LISTEN_DISABLE = 0x0;
+
+    /**
+     * Flags for use with {@link #setDiscoveryTechnology(Activity, int, int)}.
+     * <p>
+     * Setting this flag disables polling.
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH)
+    public static final int FLAG_READER_DISABLE = 0x0;
+
+    /**
+     * Flags for use with {@link #setDiscoveryTechnology(Activity, int, int)}.
+     * <p>
+     * Setting this flag makes listening to use current flags.
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH)
+    public static final int FLAG_LISTEN_KEEP = -1;
+
+    /**
+     * Flags for use with {@link #setDiscoveryTechnology(Activity, int, int)}.
+     * <p>
+     * Setting this flag makes polling to use current flags.
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH)
+    public static final int FLAG_READER_KEEP = -1;
+
+    /** @hide */
+    public static final int FLAG_USE_ALL_TECH = 0xff;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = {"FLAG_LISTEN_"}, value = {
+        FLAG_LISTEN_KEEP,
+        FLAG_LISTEN_DISABLE,
+        FLAG_LISTEN_NFC_PASSIVE_A,
+        FLAG_LISTEN_NFC_PASSIVE_B,
+        FLAG_LISTEN_NFC_PASSIVE_F
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ListenTechnology {}
+
+    /**
+     * @hide
+     * @removed
+     */
+    @SystemApi
+    @UnsupportedAppUsage
+    public static final int FLAG_NDEF_PUSH_NO_CONFIRM = 0x1;
+
+    /** @hide */
+    public static final String ACTION_HANDOVER_TRANSFER_STARTED =
+            "android.nfc.action.HANDOVER_TRANSFER_STARTED";
+
+    /** @hide */
+    public static final String ACTION_HANDOVER_TRANSFER_DONE =
+            "android.nfc.action.HANDOVER_TRANSFER_DONE";
+
+    /** @hide */
+    public static final String EXTRA_HANDOVER_TRANSFER_STATUS =
+            "android.nfc.extra.HANDOVER_TRANSFER_STATUS";
+
+    /** @hide */
+    public static final int HANDOVER_TRANSFER_STATUS_SUCCESS = 0;
+    /** @hide */
+    public static final int HANDOVER_TRANSFER_STATUS_FAILURE = 1;
+
+    /** @hide */
+    public static final String EXTRA_HANDOVER_TRANSFER_URI =
+            "android.nfc.extra.HANDOVER_TRANSFER_URI";
+
+    /**
+     * Broadcast Action: Notify possible NFC transaction blocked because device is locked.
+     * <p>An external NFC field detected when device locked and SecureNfc enabled.
+     * @hide
+     */
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    public static final String ACTION_REQUIRE_UNLOCK_FOR_NFC =
+            "android.nfc.action.REQUIRE_UNLOCK_FOR_NFC";
+
+    /**
+     * The requested app is correctly added to the Tag intent app preference.
+     *
+     * @see #setTagIntentAppPreferenceForUser(int userId, String pkg, boolean allow)
+     * @hide
+     */
+    @SystemApi
+    public static final int TAG_INTENT_APP_PREF_RESULT_SUCCESS = 0;
+
+    /**
+     * The requested app is not installed on the device.
+     *
+     * @see #setTagIntentAppPreferenceForUser(int userId, String pkg, boolean allow)
+     * @hide
+     */
+    @SystemApi
+    public static final int TAG_INTENT_APP_PREF_RESULT_PACKAGE_NOT_FOUND = -1;
+
+    /**
+     * The NfcService is not available.
+     *
+     * @see #setTagIntentAppPreferenceForUser(int userId, String pkg, boolean allow)
+     * @hide
+     */
+    @SystemApi
+    public static final int TAG_INTENT_APP_PREF_RESULT_UNAVAILABLE = -2;
+
+    /**
+     * Possible response codes from {@link #setTagIntentAppPreferenceForUser}.
+     *
+     * @hide
+     */
+    @IntDef(prefix = { "TAG_INTENT_APP_PREF_RESULT" }, value = {
+            TAG_INTENT_APP_PREF_RESULT_SUCCESS,
+            TAG_INTENT_APP_PREF_RESULT_PACKAGE_NOT_FOUND,
+            TAG_INTENT_APP_PREF_RESULT_UNAVAILABLE})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface TagIntentAppPreferenceResult {}
+
+    // Guarded by sLock
+    static boolean sIsInitialized = false;
+    static boolean sHasNfcFeature;
+    static boolean sHasCeFeature;
+    static boolean sHasNfcWlcFeature;
+
+    static Object sLock = new Object();
+
+    // Final after first constructor, except for
+    // attemptDeadServiceRecovery() when NFC crashes - we accept a best effort
+    // recovery
+    @UnsupportedAppUsage
+    static INfcAdapter sService;
+    static NfcServiceManager.ServiceRegisterer sServiceRegisterer;
+    static INfcTag sTagService;
+    static INfcCardEmulation sCardEmulationService;
+    static INfcFCardEmulation sNfcFCardEmulationService;
+
+    /**
+     * The NfcAdapter object for each application context.
+     * There is a 1-1 relationship between application context and
+     * NfcAdapter object.
+     */
+    static HashMap<Context, NfcAdapter> sNfcAdapters = new HashMap(); //guard by NfcAdapter.class
+
+    /**
+     * NfcAdapter used with a null context. This ctor was deprecated but we have
+     * to support it for backwards compatibility. New methods that require context
+     * might throw when called on the null-context NfcAdapter.
+     */
+    static NfcAdapter sNullContextNfcAdapter;  // protected by NfcAdapter.class
+
+    final NfcActivityManager mNfcActivityManager;
+    final Context mContext;
+    final HashMap<NfcUnlockHandler, INfcUnlockHandler> mNfcUnlockHandlers;
+    final Object mLock;
+
+    ITagRemovedCallback mTagRemovedListener; // protected by mLock
+
+    /**
+     * A callback to be invoked when the system finds a tag while the foreground activity is
+     * operating in reader mode.
+     * <p>Register your {@code ReaderCallback} implementation with {@link
+     * NfcAdapter#enableReaderMode} and disable it with {@link
+     * NfcAdapter#disableReaderMode}.
+     * @see NfcAdapter#enableReaderMode
+     */
+    public interface ReaderCallback {
+        public void onTagDiscovered(Tag tag);
+    }
+
+    /**
+     * A listener to be invoked when NFC controller always on state changes.
+     * <p>Register your {@code ControllerAlwaysOnListener} implementation with {@link
+     * NfcAdapter#registerControllerAlwaysOnListener} and disable it with {@link
+     * NfcAdapter#unregisterControllerAlwaysOnListener}.
+     * @see #registerControllerAlwaysOnListener
+     * @hide
+     */
+    @SystemApi
+    public interface ControllerAlwaysOnListener {
+        /**
+         * Called on NFC controller always on state changes
+         */
+        void onControllerAlwaysOnChanged(boolean isEnabled);
+    }
+
+    /**
+     * A callback to be invoked when the system successfully delivers your {@link NdefMessage}
+     * to another device.
+     * @deprecated this feature is removed. File sharing can work using other technology like
+     * Bluetooth.
+     */
+    @java.lang.Deprecated
+    public interface OnNdefPushCompleteCallback {
+        /**
+         * Called on successful NDEF push.
+         *
+         * <p>This callback is usually made on a binder thread (not the UI thread).
+         *
+         * @param event {@link NfcEvent} with the {@link NfcEvent#nfcAdapter} field set
+         */
+        public void onNdefPushComplete(NfcEvent event);
+    }
+
+    /**
+     * A callback to be invoked when another NFC device capable of NDEF push (Android Beam)
+     * is within range.
+     * <p>Implement this interface and pass it to {@code
+     * NfcAdapter#setNdefPushMessageCallback setNdefPushMessageCallback()} in order to create an
+     * {@link NdefMessage} at the moment that another device is within range for NFC. Using this
+     * callback allows you to create a message with data that might vary based on the
+     * content currently visible to the user. Alternatively, you can call {@code
+     * #setNdefPushMessage setNdefPushMessage()} if the {@link NdefMessage} always contains the
+     * same data.
+     * @deprecated this feature is removed. File sharing can work using other technology like
+     * Bluetooth.
+     */
+    @java.lang.Deprecated
+    public interface CreateNdefMessageCallback {
+        /**
+         * Called to provide a {@link NdefMessage} to push.
+         *
+         * <p>This callback is usually made on a binder thread (not the UI thread).
+         *
+         * <p>Called when this device is in range of another device
+         * that might support NDEF push. It allows the application to
+         * create the NDEF message only when it is required.
+         *
+         * <p>NDEF push cannot occur until this method returns, so do not
+         * block for too long.
+         *
+         * <p>The Android operating system will usually show a system UI
+         * on top of your activity during this time, so do not try to request
+         * input from the user to complete the callback, or provide custom NDEF
+         * push UI. The user probably will not see it.
+         *
+         * @param event {@link NfcEvent} with the {@link NfcEvent#nfcAdapter} field set
+         * @return NDEF message to push, or null to not provide a message
+         */
+        public NdefMessage createNdefMessage(NfcEvent event);
+    }
+
+
+     /**
+     * @deprecated this feature is removed. File sharing can work using other technology like
+     * Bluetooth.
+     */
+    @java.lang.Deprecated
+    public interface CreateBeamUrisCallback {
+        public Uri[] createBeamUris(NfcEvent event);
+    }
+
+    /**
+     * A callback that is invoked when a tag is removed from the field.
+     * @see NfcAdapter#ignore
+     */
+    public interface OnTagRemovedListener {
+        void onTagRemoved();
+    }
+
+    /**
+     * A callback to be invoked when an application has registered as a
+     * handler to unlock the device given an NFC tag at the lockscreen.
+     * @hide
+     */
+    @SystemApi
+    public interface NfcUnlockHandler {
+        /**
+         * Called at the lock screen to attempt to unlock the device with the given tag.
+         * @param tag the detected tag, to be used to unlock the device
+         * @return true if the device was successfully unlocked
+         */
+        public boolean onUnlockAttempted(Tag tag);
+    }
+
+    /**
+     * Return list of Secure Elements which support off host card emulation.
+     *
+     * @return List<String> containing secure elements on the device which supports
+     *                      off host card emulation. eSE for Embedded secure element,
+     *                      SIM for UICC and so on.
+     * @hide
+     */
+    public @NonNull List<String> getSupportedOffHostSecureElements() {
+        if (mContext == null) {
+            throw new UnsupportedOperationException("You need a context on NfcAdapter to use the "
+                    + " getSupportedOffHostSecureElements APIs");
+        }
+        List<String> offHostSE = new ArrayList<String>();
+        PackageManager pm = mContext.getPackageManager();
+        if (pm == null) {
+            Log.e(TAG, "Cannot get package manager, assuming no off-host CE feature");
+            return offHostSE;
+        }
+        if (pm.hasSystemFeature(PackageManager.FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC)) {
+            offHostSE.add("SIM");
+        }
+        if (pm.hasSystemFeature(PackageManager.FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE)) {
+            offHostSE.add("eSE");
+        }
+        return offHostSE;
+    }
+
+    private static void retrieveServiceRegisterer() {
+        if (sServiceRegisterer == null) {
+            NfcServiceManager manager = NfcFrameworkInitializer.getNfcServiceManager();
+            if (manager == null) {
+                Log.e(TAG, "NfcServiceManager is null");
+                throw new UnsupportedOperationException();
+            }
+            sServiceRegisterer = manager.getNfcManagerServiceRegisterer();
+        }
+    }
+
+    /**
+     * Returns the NfcAdapter for application context,
+     * or throws if NFC is not available.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static synchronized NfcAdapter getNfcAdapter(Context context) {
+        if (context == null) {
+            if (sNullContextNfcAdapter == null) {
+                sNullContextNfcAdapter = new NfcAdapter(null);
+            }
+            return sNullContextNfcAdapter;
+        }
+        if (!sIsInitialized) {
+            PackageManager pm;
+            pm = context.getPackageManager();
+            sHasNfcFeature = pm.hasSystemFeature(PackageManager.FEATURE_NFC);
+            sHasCeFeature =
+                    pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)
+                    || pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION_NFCF)
+                    || pm.hasSystemFeature(PackageManager.FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC)
+                    || pm.hasSystemFeature(PackageManager.FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE);
+            sHasNfcWlcFeature = pm.hasSystemFeature(PackageManager.FEATURE_NFC_CHARGING);
+            /* is this device meant to have NFC */
+            if (!sHasNfcFeature && !sHasCeFeature && !sHasNfcWlcFeature) {
+                Log.v(TAG, "this device does not have NFC support");
+                throw new UnsupportedOperationException();
+            }
+            retrieveServiceRegisterer();
+            sService = getServiceInterface();
+            if (sService == null) {
+                Log.e(TAG, "could not retrieve NFC service");
+                throw new UnsupportedOperationException();
+            }
+            if (sHasNfcFeature) {
+                try {
+                    sTagService = sService.getNfcTagInterface();
+                } catch (RemoteException e) {
+                    sTagService = null;
+                    Log.e(TAG, "could not retrieve NFC Tag service");
+                    throw new UnsupportedOperationException();
+                }
+            }
+            if (sHasCeFeature) {
+                try {
+                    sNfcFCardEmulationService = sService.getNfcFCardEmulationInterface();
+                } catch (RemoteException e) {
+                    sNfcFCardEmulationService = null;
+                    Log.e(TAG, "could not retrieve NFC-F card emulation service");
+                    throw new UnsupportedOperationException();
+                }
+                try {
+                    sCardEmulationService = sService.getNfcCardEmulationInterface();
+                } catch (RemoteException e) {
+                    sCardEmulationService = null;
+                    Log.e(TAG, "could not retrieve card emulation service");
+                    throw new UnsupportedOperationException();
+                }
+            }
+
+            sIsInitialized = true;
+        }
+        NfcAdapter adapter = sNfcAdapters.get(context);
+        if (adapter == null) {
+            adapter = new NfcAdapter(context);
+            sNfcAdapters.put(context, adapter);
+        }
+        return adapter;
+    }
+
+    /** get handle to NFC service interface */
+    private static INfcAdapter getServiceInterface() {
+        /* get a handle to NFC service */
+        IBinder b = sServiceRegisterer.get();
+        if (b == null) {
+            return null;
+        }
+        return INfcAdapter.Stub.asInterface(b);
+    }
+
+    /**
+     * Helper to get the default NFC Adapter.
+     * <p>
+     * Most Android devices will only have one NFC Adapter (NFC Controller).
+     * <p>
+     * This helper is the equivalent of:
+     * <pre>
+     * NfcManager manager = (NfcManager) context.getSystemService(Context.NFC_SERVICE);
+     * NfcAdapter adapter = manager.getDefaultAdapter();</pre>
+     * @param context the calling application's context
+     *
+     * @return the default NFC adapter, or null if no NFC adapter exists
+     */
+    public static NfcAdapter getDefaultAdapter(Context context) {
+        if (context == null) {
+            throw new IllegalArgumentException("context cannot be null");
+        }
+        Context applicationContext = context.getApplicationContext();
+        if (applicationContext == null) {
+            throw new IllegalArgumentException(
+                    "context not associated with any application (using a mock context?)");
+        }
+        retrieveServiceRegisterer();
+        if (sServiceRegisterer.tryGet() == null) {
+            if (sIsInitialized) {
+                synchronized (NfcAdapter.class) {
+                    /* Stale sService pointer */
+                    if (sIsInitialized) sIsInitialized = false;
+                }
+            }
+            return null;
+        }
+        /* Try to initialize the service */
+        NfcManager manager = (NfcManager) context.getSystemService(Context.NFC_SERVICE);
+        if (manager == null) {
+            // NFC not available
+            return null;
+        }
+        return manager.getDefaultAdapter();
+    }
+
+    /**
+     * Legacy NfcAdapter getter, always use {@link #getDefaultAdapter(Context)} instead.<p>
+     * This method was deprecated at API level 10 (Gingerbread MR1) because a context is required
+     * for many NFC API methods. Those methods will fail when called on an NfcAdapter
+     * object created from this method.<p>
+     * @deprecated use {@link #getDefaultAdapter(Context)}
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage
+    public static NfcAdapter getDefaultAdapter() {
+        // introduced in API version 9 (GB 2.3)
+        // deprecated in API version 10 (GB 2.3.3)
+        // removed from public API in version 16 (ICS MR2)
+        // should maintain as a hidden API for binary compatibility for a little longer
+        Log.w(TAG, "WARNING: NfcAdapter.getDefaultAdapter() is deprecated, use " +
+                "NfcAdapter.getDefaultAdapter(Context) instead", new Exception());
+
+        return NfcAdapter.getNfcAdapter(null);
+    }
+
+    NfcAdapter(Context context) {
+        mContext = context;
+        mNfcActivityManager = new NfcActivityManager(this);
+        mNfcUnlockHandlers = new HashMap<NfcUnlockHandler, INfcUnlockHandler>();
+        mTagRemovedListener = null;
+        mLock = new Object();
+        mControllerAlwaysOnListener = new NfcControllerAlwaysOnListener(getService());
+        mNfcWlcStateListener = new NfcWlcStateListener(getService());
+    }
+
+    /**
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public Context getContext() {
+        return mContext;
+    }
+
+    /**
+     * Returns the binder interface to the service.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public INfcAdapter getService() {
+        isEnabled();  // NOP call to recover sService if it is stale
+        return sService;
+    }
+
+    /**
+     * Returns the binder interface to the tag service.
+     * @hide
+     */
+    public INfcTag getTagService() {
+        isEnabled();  // NOP call to recover sTagService if it is stale
+        return sTagService;
+    }
+
+    /**
+     * Returns the binder interface to the card emulation service.
+     * @hide
+     */
+    public INfcCardEmulation getCardEmulationService() {
+        isEnabled();
+        return sCardEmulationService;
+    }
+
+    /**
+     * Returns the binder interface to the NFC-F card emulation service.
+     * @hide
+     */
+    public INfcFCardEmulation getNfcFCardEmulationService() {
+        isEnabled();
+        return sNfcFCardEmulationService;
+    }
+
+    /**
+     * Returns the binder interface to the NFC-DTA test interface.
+     * @hide
+     */
+    public INfcDta getNfcDtaInterface() {
+        if (mContext == null) {
+            throw new UnsupportedOperationException("You need a context on NfcAdapter to use the "
+                    + " NFC extras APIs");
+        }
+        try {
+            return sService.getNfcDtaInterface(mContext.getPackageName());
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+            // Try one more time
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+                return null;
+            }
+            try {
+                return sService.getNfcDtaInterface(mContext.getPackageName());
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+            }
+            return null;
+        }
+    }
+
+    /**
+     * NFC service dead - attempt best effort recovery
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public void attemptDeadServiceRecovery(Exception e) {
+        Log.e(TAG, "NFC service dead - attempting to recover", e);
+        INfcAdapter service = getServiceInterface();
+        if (service == null) {
+            Log.e(TAG, "could not retrieve NFC service during service recovery");
+            // nothing more can be done now, sService is still stale, we'll hit
+            // this recovery path again later
+            return;
+        }
+        // assigning to sService is not thread-safe, but this is best-effort code
+        // and on a well-behaved system should never happen
+        sService = service;
+        if (sHasNfcFeature) {
+            try {
+                sTagService = service.getNfcTagInterface();
+            } catch (RemoteException ee) {
+                sTagService = null;
+                Log.e(TAG, "could not retrieve NFC tag service during service recovery");
+                // nothing more can be done now, sService is still stale, we'll hit
+                // this recovery path again later
+                return;
+            }
+        }
+
+        if (sHasCeFeature) {
+            try {
+                sCardEmulationService = service.getNfcCardEmulationInterface();
+            } catch (RemoteException ee) {
+                sCardEmulationService = null;
+                Log.e(TAG,
+                        "could not retrieve NFC card emulation service during service recovery");
+            }
+
+            try {
+                sNfcFCardEmulationService = service.getNfcFCardEmulationInterface();
+            } catch (RemoteException ee) {
+                sNfcFCardEmulationService = null;
+                Log.e(TAG,
+                        "could not retrieve NFC-F card emulation service during service recovery");
+            }
+        }
+
+        return;
+    }
+
+    private boolean isCardEmulationEnabled() {
+        if (sHasCeFeature) {
+            return (sCardEmulationService != null || sNfcFCardEmulationService != null);
+        }
+        return false;
+    }
+
+    private boolean isTagReadingEnabled() {
+        if (sHasNfcFeature) {
+            return sTagService != null;
+        }
+        return false;
+    }
+
+
+    /**
+     * Return true if this NFC Adapter has any features enabled.
+     *
+     * <p>If this method returns false, the NFC hardware is guaranteed not to
+     * generate or respond to any NFC communication over its NFC radio.
+     * <p>Applications can use this to check if NFC is enabled. Applications
+     * can request Settings UI allowing the user to toggle NFC using:
+     * <p><pre>startActivity(new Intent(Settings.ACTION_NFC_SETTINGS))</pre>
+     *
+     * @see android.provider.Settings#ACTION_NFC_SETTINGS
+     * @return true if this NFC Adapter has any features enabled
+     */
+    public boolean isEnabled() {
+        boolean serviceState = false;
+        try {
+            serviceState = sService.getState() == STATE_ON;
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+            // Try one more time
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+                return false;
+            }
+            try {
+                serviceState = sService.getState() == STATE_ON;
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+            }
+        }
+        return serviceState
+                && (isTagReadingEnabled() || isCardEmulationEnabled() || sHasNfcWlcFeature);
+    }
+
+    /**
+     * Return the state of this NFC Adapter.
+     *
+     * <p>Returns one of {@link #STATE_ON}, {@link #STATE_TURNING_ON},
+     * {@link #STATE_OFF}, {@link #STATE_TURNING_OFF}.
+     *
+     * <p>{@link #isEnabled()} is equivalent to
+     * <code>{@link #getAdapterState()} == {@link #STATE_ON}</code>
+     *
+     * @return the current state of this NFC adapter
+     *
+     * @hide
+     */
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    public @AdapterState int getAdapterState() {
+        try {
+            return sService.getState();
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+            // Try one more time
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+                return NfcAdapter.STATE_OFF;
+            }
+            try {
+                return sService.getState();
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+            }
+            return NfcAdapter.STATE_OFF;
+        }
+    }
+
+    /**
+     * Enable NFC hardware.
+     *
+     * <p>This call is asynchronous. Listen for
+     * {@link #ACTION_ADAPTER_STATE_CHANGED} broadcasts to find out when the
+     * operation is complete.
+     *
+     * <p>If this returns true, then either NFC is already on, or
+     * a {@link #ACTION_ADAPTER_STATE_CHANGED} broadcast will be sent
+     * to indicate a state transition. If this returns false, then
+     * there is some problem that prevents an attempt to turn
+     * NFC on (for example we are in airplane mode and NFC is not
+     * toggleable in airplane mode on this platform).
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+    public boolean enable() {
+        try {
+            return sService.enable();
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+            // Try one more time
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+                return false;
+            }
+            try {
+                return sService.enable();
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Disable NFC hardware.
+     *
+     * <p>No NFC features will work after this call, and the hardware
+     * will not perform or respond to any NFC communication.
+     *
+     * <p>This call is asynchronous. Listen for
+     * {@link #ACTION_ADAPTER_STATE_CHANGED} broadcasts to find out when the
+     * operation is complete.
+     *
+     * <p>If this returns true, then either NFC is already off, or
+     * a {@link #ACTION_ADAPTER_STATE_CHANGED} broadcast will be sent
+     * to indicate a state transition. If this returns false, then
+     * there is some problem that prevents an attempt to turn
+     * NFC off.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+    public boolean disable() {
+        try {
+            return sService.disable(true);
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+            // Try one more time
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+                return false;
+            }
+            try {
+                return sService.disable(true);
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Disable NFC hardware.
+     * @hide
+    */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+    public boolean disable(boolean persist) {
+        try {
+            return sService.disable(persist);
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+            // Try one more time
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+                return false;
+            }
+            try {
+                return sService.disable(persist);
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Pauses polling for a {@code timeoutInMs} millis. If polling must be resumed before timeout,
+     * use {@link #resumePolling()}.
+     * @hide
+     */
+    public void pausePolling(int timeoutInMs) {
+        try {
+            sService.pausePolling(timeoutInMs);
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+        }
+    }
+
+
+    /**
+     * Returns whether the device supports observer mode or not. When observe
+     * mode is enabled, the NFC hardware will listen for NFC readers, but not
+     * respond to them. When observe mode is disabled, the NFC hardware will
+     * resoond to the reader and proceed with the transaction.
+     * @return true if the mode is supported, false otherwise.
+     */
+    @FlaggedApi(Flags.FLAG_NFC_OBSERVE_MODE)
+    public boolean isObserveModeSupported() {
+        try {
+            return sService.isObserveModeSupported();
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+            return false;
+        }
+    }
+
+   /**
+    * Disables observe mode to allow the transaction to proceed. See
+    * {@link #isObserveModeSupported()} for a description of observe mode and
+    * use {@link #disallowTransaction()} to enable observe mode and block
+    * transactions again.
+    *
+    * @return boolean indicating success or failure.
+    */
+    @FlaggedApi(Flags.FLAG_NFC_OBSERVE_MODE)
+    public boolean allowTransaction() {
+        try {
+            return sService.setObserveMode(false);
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+            return false;
+        }
+    }
+
+    /**
+    * Signals that the transaction has completed and observe mode may be
+    * reenabled. See {@link #isObserveModeSupported()} for a description of
+    * observe mode and use {@link #allowTransaction()} to disable observe
+    * mode and allow transactions to proceed.
+    *
+    * @return boolean indicating success or failure.
+    */
+
+    @FlaggedApi(Flags.FLAG_NFC_OBSERVE_MODE)
+    public boolean disallowTransaction() {
+        try {
+            return sService.setObserveMode(true);
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+            return false;
+        }
+    }
+
+    /**
+     * Resumes default polling for the current device state if polling is paused. Calling
+     * this while polling is not paused is a no-op.
+     *
+     * @hide
+     */
+    public void resumePolling() {
+        try {
+            sService.resumePolling();
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+        }
+    }
+
+    /**
+     * Set one or more {@link Uri}s to send using Android Beam (TM). Every
+     * Uri you provide must have either scheme 'file' or scheme 'content'.
+     *
+     * <p>For the data provided through this method, Android Beam tries to
+     * switch to alternate transports such as Bluetooth to achieve a fast
+     * transfer speed. Hence this method is very suitable
+     * for transferring large files such as pictures or songs.
+     *
+     * <p>The receiving side will store the content of each Uri in
+     * a file and present a notification to the user to open the file
+     * with a {@link android.content.Intent} with action
+     * {@link android.content.Intent#ACTION_VIEW}.
+     * If multiple URIs are sent, the {@link android.content.Intent} will refer
+     * to the first of the stored files.
+     *
+     * <p>This method may be called at any time before {@link Activity#onDestroy},
+     * but the URI(s) are only made available for Android Beam when the
+     * specified activity(s) are in resumed (foreground) state. The recommended
+     * approach is to call this method during your Activity's
+     * {@link Activity#onCreate} - see sample
+     * code below. This method does not immediately perform any I/O or blocking work,
+     * so is safe to call on your main thread.
+     *
+     * <p>{@link #setBeamPushUris} and {@link #setBeamPushUrisCallback}
+     * have priority over both {@link #setNdefPushMessage} and
+     * {@link #setNdefPushMessageCallback}.
+     *
+     * <p>If {@link #setBeamPushUris} is called with a null Uri array,
+     * and/or {@link #setBeamPushUrisCallback} is called with a null callback,
+     * then the Uri push will be completely disabled for the specified activity(s).
+     *
+     * <p>Code example:
+     * <pre>
+     * protected void onCreate(Bundle savedInstanceState) {
+     *     super.onCreate(savedInstanceState);
+     *     NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
+     *     if (nfcAdapter == null) return;  // NFC not available on this device
+     *     nfcAdapter.setBeamPushUris(new Uri[] {uri1, uri2}, this);
+     * }</pre>
+     * And that is it. Only one call per activity is necessary. The Android
+     * OS will automatically release its references to the Uri(s) and the
+     * Activity object when it is destroyed if you follow this pattern.
+     *
+     * <p>If your Activity wants to dynamically supply Uri(s),
+     * then set a callback using {@link #setBeamPushUrisCallback} instead
+     * of using this method.
+     *
+     * <p class="note">Do not pass in an Activity that has already been through
+     * {@link Activity#onDestroy}. This is guaranteed if you call this API
+     * during {@link Activity#onCreate}.
+     *
+     * <p class="note">If this device does not support alternate transports
+     * such as Bluetooth or WiFI, calling this method does nothing.
+     *
+     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+     *
+     * @param uris an array of Uri(s) to push over Android Beam
+     * @param activity activity for which the Uri(s) will be pushed
+     * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+     * @removed this feature is removed. File sharing can work using other technology like
+     * Bluetooth.
+     */
+    @java.lang.Deprecated
+    @UnsupportedAppUsage
+    public void setBeamPushUris(Uri[] uris, Activity activity) {
+        synchronized (sLock) {
+            if (!sHasNfcFeature) {
+                throw new UnsupportedOperationException();
+            }
+        }
+    }
+
+    /**
+     * Set a callback that will dynamically generate one or more {@link Uri}s
+     * to send using Android Beam (TM). Every Uri the callback provides
+     * must have either scheme 'file' or scheme 'content'.
+     *
+     * <p>For the data provided through this callback, Android Beam tries to
+     * switch to alternate transports such as Bluetooth to achieve a fast
+     * transfer speed. Hence this method is very suitable
+     * for transferring large files such as pictures or songs.
+     *
+     * <p>The receiving side will store the content of each Uri in
+     * a file and present a notification to the user to open the file
+     * with a {@link android.content.Intent} with action
+     * {@link android.content.Intent#ACTION_VIEW}.
+     * If multiple URIs are sent, the {@link android.content.Intent} will refer
+     * to the first of the stored files.
+     *
+     * <p>This method may be called at any time before {@link Activity#onDestroy},
+     * but the URI(s) are only made available for Android Beam when the
+     * specified activity(s) are in resumed (foreground) state. The recommended
+     * approach is to call this method during your Activity's
+     * {@link Activity#onCreate} - see sample
+     * code below. This method does not immediately perform any I/O or blocking work,
+     * so is safe to call on your main thread.
+     *
+     * <p>{@link #setBeamPushUris} and {@link #setBeamPushUrisCallback}
+     * have priority over both {@link #setNdefPushMessage} and
+     * {@link #setNdefPushMessageCallback}.
+     *
+     * <p>If {@link #setBeamPushUris} is called with a null Uri array,
+     * and/or {@link #setBeamPushUrisCallback} is called with a null callback,
+     * then the Uri push will be completely disabled for the specified activity(s).
+     *
+     * <p>Code example:
+     * <pre>
+     * protected void onCreate(Bundle savedInstanceState) {
+     *     super.onCreate(savedInstanceState);
+     *     NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
+     *     if (nfcAdapter == null) return;  // NFC not available on this device
+     *     nfcAdapter.setBeamPushUrisCallback(callback, this);
+     * }</pre>
+     * And that is it. Only one call per activity is necessary. The Android
+     * OS will automatically release its references to the Uri(s) and the
+     * Activity object when it is destroyed if you follow this pattern.
+     *
+     * <p class="note">Do not pass in an Activity that has already been through
+     * {@link Activity#onDestroy}. This is guaranteed if you call this API
+     * during {@link Activity#onCreate}.
+     *
+     * <p class="note">If this device does not support alternate transports
+     * such as Bluetooth or WiFI, calling this method does nothing.
+     *
+     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+     *
+     * @param callback callback, or null to disable
+     * @param activity activity for which the Uri(s) will be pushed
+     * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+     * @removed this feature is removed. File sharing can work using other technology like
+     * Bluetooth.
+     */
+    @java.lang.Deprecated
+    @UnsupportedAppUsage
+    public void setBeamPushUrisCallback(CreateBeamUrisCallback callback, Activity activity) {
+        synchronized (sLock) {
+            if (!sHasNfcFeature) {
+                throw new UnsupportedOperationException();
+            }
+        }
+    }
+
+    /**
+     * Set a static {@link NdefMessage} to send using Android Beam (TM).
+     *
+     * <p>This method may be called at any time before {@link Activity#onDestroy},
+     * but the NDEF message is only made available for NDEF push when the
+     * specified activity(s) are in resumed (foreground) state. The recommended
+     * approach is to call this method during your Activity's
+     * {@link Activity#onCreate} - see sample
+     * code below. This method does not immediately perform any I/O or blocking work,
+     * so is safe to call on your main thread.
+     *
+     * <p>Only one NDEF message can be pushed by the currently resumed activity.
+     * If both {@link #setNdefPushMessage} and
+     * {@link #setNdefPushMessageCallback} are set, then
+     * the callback will take priority.
+     *
+     * <p>If neither {@link #setNdefPushMessage} or
+     * {@link #setNdefPushMessageCallback} have been called for your activity, then
+     * the Android OS may choose to send a default NDEF message on your behalf,
+     * such as a URI for your application.
+     *
+     * <p>If {@link #setNdefPushMessage} is called with a null NDEF message,
+     * and/or {@link #setNdefPushMessageCallback} is called with a null callback,
+     * then NDEF push will be completely disabled for the specified activity(s).
+     * This also disables any default NDEF message the Android OS would have
+     * otherwise sent on your behalf for those activity(s).
+     *
+     * <p>If you want to prevent the Android OS from sending default NDEF
+     * messages completely (for all activities), you can include a
+     * {@code <meta-data>} element inside the {@code <application>}
+     * element of your AndroidManifest.xml file, like this:
+     * <pre>
+     * &lt;application ...>
+     *     &lt;meta-data android:name="android.nfc.disable_beam_default"
+     *         android:value="true" />
+     * &lt;/application></pre>
+     *
+     * <p>The API allows for multiple activities to be specified at a time,
+     * but it is strongly recommended to just register one at a time,
+     * and to do so during the activity's {@link Activity#onCreate}. For example:
+     * <pre>
+     * protected void onCreate(Bundle savedInstanceState) {
+     *     super.onCreate(savedInstanceState);
+     *     NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
+     *     if (nfcAdapter == null) return;  // NFC not available on this device
+     *     nfcAdapter.setNdefPushMessage(ndefMessage, this);
+     * }</pre>
+     * And that is it. Only one call per activity is necessary. The Android
+     * OS will automatically release its references to the NDEF message and the
+     * Activity object when it is destroyed if you follow this pattern.
+     *
+     * <p>If your Activity wants to dynamically generate an NDEF message,
+     * then set a callback using {@link #setNdefPushMessageCallback} instead
+     * of a static message.
+     *
+     * <p class="note">Do not pass in an Activity that has already been through
+     * {@link Activity#onDestroy}. This is guaranteed if you call this API
+     * during {@link Activity#onCreate}.
+     *
+     * <p class="note">For sending large content such as pictures and songs,
+     * consider using {@link #setBeamPushUris}, which switches to alternate transports
+     * such as Bluetooth to achieve a fast transfer rate.
+     *
+     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+     *
+     * @param message NDEF message to push over NFC, or null to disable
+     * @param activity activity for which the NDEF message will be pushed
+     * @param activities optional additional activities, however we strongly recommend
+     *        to only register one at a time, and to do so in that activity's
+     *        {@link Activity#onCreate}
+     * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+     * @removed this feature is removed. File sharing can work using other technology like
+     * Bluetooth.
+     */
+    @java.lang.Deprecated
+    @UnsupportedAppUsage
+    public void setNdefPushMessage(NdefMessage message, Activity activity,
+            Activity ... activities) {
+        synchronized (sLock) {
+            if (!sHasNfcFeature) {
+                throw new UnsupportedOperationException();
+            }
+        }
+    }
+
+    /**
+     * @hide
+     * @removed
+     */
+    @SystemApi
+    @UnsupportedAppUsage
+    public void setNdefPushMessage(NdefMessage message, Activity activity, int flags) {
+        synchronized (sLock) {
+            if (!sHasNfcFeature) {
+                throw new UnsupportedOperationException();
+            }
+        }
+    }
+
+    /**
+     * Set a callback that dynamically generates NDEF messages to send using Android Beam (TM).
+     *
+     * <p>This method may be called at any time before {@link Activity#onDestroy},
+     * but the NDEF message callback can only occur when the
+     * specified activity(s) are in resumed (foreground) state. The recommended
+     * approach is to call this method during your Activity's
+     * {@link Activity#onCreate} - see sample
+     * code below. This method does not immediately perform any I/O or blocking work,
+     * so is safe to call on your main thread.
+     *
+     * <p>Only one NDEF message can be pushed by the currently resumed activity.
+     * If both {@link #setNdefPushMessage} and
+     * {@link #setNdefPushMessageCallback} are set, then
+     * the callback will take priority.
+     *
+     * <p>If neither {@link #setNdefPushMessage} or
+     * {@link #setNdefPushMessageCallback} have been called for your activity, then
+     * the Android OS may choose to send a default NDEF message on your behalf,
+     * such as a URI for your application.
+     *
+     * <p>If {@link #setNdefPushMessage} is called with a null NDEF message,
+     * and/or {@link #setNdefPushMessageCallback} is called with a null callback,
+     * then NDEF push will be completely disabled for the specified activity(s).
+     * This also disables any default NDEF message the Android OS would have
+     * otherwise sent on your behalf for those activity(s).
+     *
+     * <p>If you want to prevent the Android OS from sending default NDEF
+     * messages completely (for all activities), you can include a
+     * {@code <meta-data>} element inside the {@code <application>}
+     * element of your AndroidManifest.xml file, like this:
+     * <pre>
+     * &lt;application ...>
+     *     &lt;meta-data android:name="android.nfc.disable_beam_default"
+     *         android:value="true" />
+     * &lt;/application></pre>
+     *
+     * <p>The API allows for multiple activities to be specified at a time,
+     * but it is strongly recommended to just register one at a time,
+     * and to do so during the activity's {@link Activity#onCreate}. For example:
+     * <pre>
+     * protected void onCreate(Bundle savedInstanceState) {
+     *     super.onCreate(savedInstanceState);
+     *     NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
+     *     if (nfcAdapter == null) return;  // NFC not available on this device
+     *     nfcAdapter.setNdefPushMessageCallback(callback, this);
+     * }</pre>
+     * And that is it. Only one call per activity is necessary. The Android
+     * OS will automatically release its references to the callback and the
+     * Activity object when it is destroyed if you follow this pattern.
+     *
+     * <p class="note">Do not pass in an Activity that has already been through
+     * {@link Activity#onDestroy}. This is guaranteed if you call this API
+     * during {@link Activity#onCreate}.
+     * <p class="note">For sending large content such as pictures and songs,
+     * consider using {@link #setBeamPushUris}, which switches to alternate transports
+     * such as Bluetooth to achieve a fast transfer rate.
+     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+     *
+     * @param callback callback, or null to disable
+     * @param activity activity for which the NDEF message will be pushed
+     * @param activities optional additional activities, however we strongly recommend
+     *        to only register one at a time, and to do so in that activity's
+     *        {@link Activity#onCreate}
+     * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+     * @removed this feature is removed. File sharing can work using other technology like
+     * Bluetooth.
+     */
+    @java.lang.Deprecated
+    @UnsupportedAppUsage
+    public void setNdefPushMessageCallback(CreateNdefMessageCallback callback, Activity activity,
+            Activity ... activities) {
+        synchronized (sLock) {
+            if (!sHasNfcFeature) {
+                throw new UnsupportedOperationException();
+            }
+        }
+    }
+
+    /**
+     * Set a callback on successful Android Beam (TM).
+     *
+     * <p>This method may be called at any time before {@link Activity#onDestroy},
+     * but the callback can only occur when the
+     * specified activity(s) are in resumed (foreground) state. The recommended
+     * approach is to call this method during your Activity's
+     * {@link Activity#onCreate} - see sample
+     * code below. This method does not immediately perform any I/O or blocking work,
+     * so is safe to call on your main thread.
+     *
+     * <p>The API allows for multiple activities to be specified at a time,
+     * but it is strongly recommended to just register one at a time,
+     * and to do so during the activity's {@link Activity#onCreate}. For example:
+     * <pre>
+     * protected void onCreate(Bundle savedInstanceState) {
+     *     super.onCreate(savedInstanceState);
+     *     NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
+     *     if (nfcAdapter == null) return;  // NFC not available on this device
+     *     nfcAdapter.setOnNdefPushCompleteCallback(callback, this);
+     * }</pre>
+     * And that is it. Only one call per activity is necessary. The Android
+     * OS will automatically release its references to the callback and the
+     * Activity object when it is destroyed if you follow this pattern.
+     *
+     * <p class="note">Do not pass in an Activity that has already been through
+     * {@link Activity#onDestroy}. This is guaranteed if you call this API
+     * during {@link Activity#onCreate}.
+     *
+     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+     *
+     * @param callback callback, or null to disable
+     * @param activity activity for which the NDEF message will be pushed
+     * @param activities optional additional activities, however we strongly recommend
+     *        to only register one at a time, and to do so in that activity's
+     *        {@link Activity#onCreate}
+     * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+     * @removed this feature is removed. File sharing can work using other technology like
+     * Bluetooth.
+     */
+    @java.lang.Deprecated
+    @UnsupportedAppUsage
+    public void setOnNdefPushCompleteCallback(OnNdefPushCompleteCallback callback,
+            Activity activity, Activity ... activities) {
+        synchronized (sLock) {
+            if (!sHasNfcFeature) {
+                throw new UnsupportedOperationException();
+            }
+        }
+    }
+
+    /**
+     * Enable foreground dispatch to the given Activity.
+     *
+     * <p>This will give priority to the foreground activity when
+     * dispatching a discovered {@link Tag} to an application.
+     *
+     * <p>If any IntentFilters are provided to this method they are used to match dispatch Intents
+     * for both the {@link NfcAdapter#ACTION_NDEF_DISCOVERED} and
+     * {@link NfcAdapter#ACTION_TAG_DISCOVERED}. Since {@link NfcAdapter#ACTION_TECH_DISCOVERED}
+     * relies on meta data outside of the IntentFilter matching for that dispatch Intent is handled
+     * by passing in the tech lists separately. Each first level entry in the tech list represents
+     * an array of technologies that must all be present to match. If any of the first level sets
+     * match then the dispatch is routed through the given PendingIntent. In other words, the second
+     * level is ANDed together and the first level entries are ORed together.
+     *
+     * <p>If you pass {@code null} for both the {@code filters} and {@code techLists} parameters
+     * that acts a wild card and will cause the foreground activity to receive all tags via the
+     * {@link NfcAdapter#ACTION_TAG_DISCOVERED} intent.
+     *
+     * <p>This method must be called from the main thread, and only when the activity is in the
+     * foreground (resumed). Also, activities must call {@link #disableForegroundDispatch} before
+     * the completion of their {@link Activity#onPause} callback to disable foreground dispatch
+     * after it has been enabled.
+     *
+     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+     *
+     * @param activity the Activity to dispatch to
+     * @param intent the PendingIntent to start for the dispatch
+     * @param filters the IntentFilters to override dispatching for, or null to always dispatch
+     * @param techLists the tech lists used to perform matching for dispatching of the
+     *      {@link NfcAdapter#ACTION_TECH_DISCOVERED} intent
+     * @throws IllegalStateException if the Activity is not currently in the foreground
+     * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+     */
+    public void enableForegroundDispatch(Activity activity, PendingIntent intent,
+            IntentFilter[] filters, String[][] techLists) {
+        synchronized (sLock) {
+            if (!sHasNfcFeature) {
+                throw new UnsupportedOperationException();
+            }
+        }
+        if (activity == null || intent == null) {
+            throw new NullPointerException();
+        }
+        try {
+            TechListParcel parcel = null;
+            if (techLists != null && techLists.length > 0) {
+                parcel = new TechListParcel(techLists);
+            }
+            sService.setForegroundDispatch(intent, filters, parcel);
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+        }
+    }
+
+    /**
+     * Disable foreground dispatch to the given activity.
+     *
+     * <p>After calling {@link #enableForegroundDispatch}, an activity
+     * must call this method before its {@link Activity#onPause} callback
+     * completes.
+     *
+     * <p>This method must be called from the main thread.
+     *
+     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+     *
+     * @param activity the Activity to disable dispatch to
+     * @throws IllegalStateException if the Activity has already been paused
+     * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+     */
+    public void disableForegroundDispatch(Activity activity) {
+        synchronized (sLock) {
+            if (!sHasNfcFeature) {
+                throw new UnsupportedOperationException();
+            }
+        }
+        try {
+            sService.setForegroundDispatch(null, null, null);
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+        }
+    }
+
+    /**
+     * Limit the NFC controller to reader mode while this Activity is in the foreground.
+     *
+     * <p>In this mode the NFC controller will only act as an NFC tag reader/writer,
+     * thus disabling any peer-to-peer (Android Beam) and card-emulation modes of
+     * the NFC adapter on this device.
+     *
+     * <p>Use {@link #FLAG_READER_SKIP_NDEF_CHECK} to prevent the platform from
+     * performing any NDEF checks in reader mode. Note that this will prevent the
+     * {@link Ndef} tag technology from being enumerated on the tag, and that
+     * NDEF-based tag dispatch will not be functional.
+     *
+     * <p>For interacting with tags that are emulated on another Android device
+     * using Android's host-based card-emulation, the recommended flags are
+     * {@link #FLAG_READER_NFC_A} and {@link #FLAG_READER_SKIP_NDEF_CHECK}.
+     *
+     * @param activity the Activity that requests the adapter to be in reader mode
+     * @param callback the callback to be called when a tag is discovered
+     * @param flags Flags indicating poll technologies and other optional parameters
+     * @param extras Additional extras for configuring reader mode.
+     * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+     */
+    public void enableReaderMode(Activity activity, ReaderCallback callback, int flags,
+            Bundle extras) {
+        synchronized (sLock) {
+            if (!sHasNfcFeature) {
+                throw new UnsupportedOperationException();
+            }
+        }
+        mNfcActivityManager.enableReaderMode(activity, callback, flags, extras);
+    }
+
+    /**
+     * Restore the NFC adapter to normal mode of operation: supporting
+     * peer-to-peer (Android Beam), card emulation, and polling for
+     * all supported tag technologies.
+     *
+     * @param activity the Activity that currently has reader mode enabled
+     * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+     */
+    public void disableReaderMode(Activity activity) {
+        synchronized (sLock) {
+            if (!sHasNfcFeature) {
+                throw new UnsupportedOperationException();
+            }
+        }
+        mNfcActivityManager.disableReaderMode(activity);
+    }
+
+    // Flags arguments to NFC adapter to enable/disable NFC
+    private static final int DISABLE_POLLING_FLAGS = 0x1000;
+    private static final int ENABLE_POLLING_FLAGS = 0x0000;
+
+    /**
+     * Privileged API to enable disable reader polling.
+     * Note: Use with caution! The app is responsible for ensuring that the polling state is
+     * returned to normal.
+     *
+     * @see #enableReaderMode(Activity, ReaderCallback, int, Bundle)  for more detailed
+     * documentation.
+     *
+     * @param enablePolling whether to enable or disable polling.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    @SuppressLint("VisiblySynchronized")
+    public void setReaderMode(boolean enablePolling) {
+        synchronized (sLock) {
+            if (!sHasNfcFeature) {
+                throw new UnsupportedOperationException();
+            }
+        }
+        Binder token = new Binder();
+        int flags = enablePolling ? ENABLE_POLLING_FLAGS : DISABLE_POLLING_FLAGS;
+        try {
+            NfcAdapter.sService.setReaderMode(token, null, flags, null);
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+        }
+    }
+
+    /**
+     * Set the NFC controller to enable specific poll/listen technologies,
+     * as specified in parameters, while this Activity is in the foreground.
+     *
+     * Use {@link #FLAG_READER_KEEP} to keep current polling technology.
+     * Use {@link #FLAG_LISTEN_KEEP} to keep current listenig technology.
+     * Use {@link #FLAG_READER_DISABLE} to disable polling.
+     * Use {@link #FLAG_LISTEN_DISABLE} to disable listening.
+     * Also refer to {@link #resetDiscoveryTechnology(Activity)} to restore these changes.
+     * </p>
+     * The pollTechnology, listenTechnology parameters can be one or several of below list.
+     * <pre>
+     *                    Poll                    Listen
+     *  Passive A         0x01   (NFC_A)           0x01  (NFC_PASSIVE_A)
+     *  Passive B         0x02   (NFC_B)           0x02  (NFC_PASSIVE_B)
+     *  Passive F         0x04   (NFC_F)           0x04  (NFC_PASSIVE_F)
+     *  ISO 15693         0x08   (NFC_V)             -
+     *  Kovio             0x10   (NFC_BARCODE)       -
+     * </pre>
+     * <p>Example usage in an Activity that requires to disable poll,
+     * keep current listen technologies:
+     * <pre>
+     * protected void onResume() {
+     *     mNfcAdapter = NfcAdapter.getDefaultAdapter(getApplicationContext());
+     *     mNfcAdapter.setDiscoveryTechnology(this,
+     *         NfcAdapter.FLAG_READER_DISABLE, NfcAdapter.FLAG_LISTEN_KEEP);
+     * }</pre></p>
+     * @param activity The Activity that requests NFC controller to enable specific technologies.
+     * @param pollTechnology Flags indicating poll technologies.
+     * @param listenTechnology Flags indicating listen technologies.
+     * @throws UnsupportedOperationException if FEATURE_NFC,
+     * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF are unavailable.
+     */
+
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH)
+    public void setDiscoveryTechnology(@NonNull Activity activity,
+            @PollTechnology int pollTechnology, @ListenTechnology int listenTechnology) {
+        if (listenTechnology == FLAG_LISTEN_DISABLE) {
+            synchronized (sLock) {
+                if (!sHasNfcFeature) {
+                    throw new UnsupportedOperationException();
+                }
+            }
+            mNfcActivityManager.enableReaderMode(activity, null, pollTechnology, null);
+            return;
+        }
+        if (pollTechnology == FLAG_READER_DISABLE) {
+            synchronized (sLock) {
+                if (!sHasCeFeature) {
+                    throw new UnsupportedOperationException();
+                }
+            }
+        } else {
+            synchronized (sLock) {
+                if (!sHasNfcFeature || !sHasCeFeature) {
+                    throw new UnsupportedOperationException();
+                }
+            }
+        }
+        mNfcActivityManager.setDiscoveryTech(activity, pollTechnology, listenTechnology);
+    }
+
+    /**
+     * Restore the poll/listen technologies of NFC controller,
+     * which were changed by {@link #setDiscoveryTechnology(Activity , int , int)}
+     *
+     * @param activity The Activity that requests to changed technologies.
+     */
+
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH)
+    public void resetDiscoveryTechnology(@NonNull Activity activity) {
+        mNfcActivityManager.resetDiscoveryTech(activity);
+    }
+
+    /**
+     * Manually invoke Android Beam to share data.
+     *
+     * <p>The Android Beam animation is normally only shown when two NFC-capable
+     * devices come into range.
+     * By calling this method, an Activity can invoke the Beam animation directly
+     * even if no other NFC device is in range yet. The Beam animation will then
+     * prompt the user to tap another NFC-capable device to complete the data
+     * transfer.
+     *
+     * <p>The main advantage of using this method is that it avoids the need for the
+     * user to tap the screen to complete the transfer, as this method already
+     * establishes the direction of the transfer and the consent of the user to
+     * share data. Callers are responsible for making sure that the user has
+     * consented to sharing data on NFC tap.
+     *
+     * <p>Note that to use this method, the passed in Activity must have already
+     * set data to share over Beam by using method calls such as
+     * {@link #setNdefPushMessageCallback} or
+     * {@link #setBeamPushUrisCallback}.
+     *
+     * @param activity the current foreground Activity that has registered data to share
+     * @return whether the Beam animation was successfully invoked
+     * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+     * @removed this feature is removed. File sharing can work using other technology like
+     * Bluetooth.
+     */
+    @java.lang.Deprecated
+    @UnsupportedAppUsage
+    public boolean invokeBeam(Activity activity) {
+        synchronized (sLock) {
+            if (!sHasNfcFeature) {
+                throw new UnsupportedOperationException();
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Enable NDEF message push over NFC while this Activity is in the foreground.
+     *
+     * <p>You must explicitly call this method every time the activity is
+     * resumed, and you must call {@link #disableForegroundNdefPush} before
+     * your activity completes {@link Activity#onPause}.
+     *
+     * <p>Strongly recommend to use the new {@link #setNdefPushMessage}
+     * instead: it automatically hooks into your activity life-cycle,
+     * so you do not need to call enable/disable in your onResume/onPause.
+     *
+     * <p>For NDEF push to function properly the other NFC device must
+     * support either NFC Forum's SNEP (Simple Ndef Exchange Protocol), or
+     * Android's "com.android.npp" (Ndef Push Protocol). This was optional
+     * on Gingerbread level Android NFC devices, but SNEP is mandatory on
+     * Ice-Cream-Sandwich and beyond.
+     *
+     * <p>This method must be called from the main thread.
+     *
+     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+     *
+     * @param activity foreground activity
+     * @param message a NDEF Message to push over NFC
+     * @throws UnsupportedOperationException if FEATURE_NFC is unavailable
+     * @removed this feature is removed. File sharing can work using other technology like
+     * Bluetooth.
+     */
+    @Deprecated
+    @UnsupportedAppUsage
+    public void enableForegroundNdefPush(Activity activity, NdefMessage message) {
+        synchronized (sLock) {
+            if (!sHasNfcFeature) {
+                throw new UnsupportedOperationException();
+            }
+        }
+    }
+
+    /**
+     * Disable NDEF message push over P2P.
+     *
+     * <p>After calling {@link #enableForegroundNdefPush}, an activity
+     * must call this method before its {@link Activity#onPause} callback
+     * completes.
+     *
+     * <p>Strongly recommend to use the new {@link #setNdefPushMessage}
+     * instead: it automatically hooks into your activity life-cycle,
+     * so you do not need to call enable/disable in your onResume/onPause.
+     *
+     * <p>This method must be called from the main thread.
+     *
+     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+     *
+     * @param activity the Foreground activity
+     * @throws UnsupportedOperationException if FEATURE_NFC is unavailable
+     * @removed this feature is removed. File sharing can work using other technology like
+     * Bluetooth.
+     */
+    @Deprecated
+    @UnsupportedAppUsage
+    public void disableForegroundNdefPush(Activity activity) {
+        synchronized (sLock) {
+            if (!sHasNfcFeature) {
+                throw new UnsupportedOperationException();
+            }
+        }
+    }
+
+    /**
+     * Sets Secure NFC feature.
+     * <p>This API is for the Settings application.
+     * @return True if successful
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+    public boolean enableSecureNfc(boolean enable) {
+        if (!sHasNfcFeature && !sHasCeFeature) {
+            throw new UnsupportedOperationException();
+        }
+        try {
+            return sService.setNfcSecure(enable);
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+            // Try one more time
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+                return false;
+            }
+            try {
+                return sService.setNfcSecure(enable);
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Checks if the device supports Secure NFC functionality.
+     *
+     * @return True if device supports Secure NFC, false otherwise
+     * @throws UnsupportedOperationException if FEATURE_NFC,
+     * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF,
+     * FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC and FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE
+     * are unavailable
+     */
+    public boolean isSecureNfcSupported() {
+        if (!sHasNfcFeature && !sHasCeFeature) {
+            throw new UnsupportedOperationException();
+        }
+        try {
+            return sService.deviceSupportsNfcSecure();
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+            // Try one more time
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+                return false;
+            }
+            try {
+                return sService.deviceSupportsNfcSecure();
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Returns information regarding Nfc antennas on the device
+     * such as their relative positioning on the device.
+     *
+     * @return Information on the nfc antenna(s) on the device.
+     * @throws UnsupportedOperationException if FEATURE_NFC,
+     * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF,
+     * FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC and FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE
+     * are unavailable
+     */
+    @Nullable
+    public NfcAntennaInfo getNfcAntennaInfo() {
+        if (!sHasNfcFeature && !sHasCeFeature) {
+            throw new UnsupportedOperationException();
+        }
+        try {
+            return sService.getNfcAntennaInfo();
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+            // Try one more time
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+                return null;
+            }
+            try {
+                return sService.getNfcAntennaInfo();
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+            }
+            return null;
+        }
+    }
+
+    /**
+     * Checks Secure NFC feature is enabled.
+     *
+     * @return True if Secure NFC is enabled, false otherwise
+     * @throws UnsupportedOperationException if FEATURE_NFC,
+     * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF,
+     * FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC and FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE
+     * are unavailable
+     * @throws UnsupportedOperationException if device doesn't support
+     *         Secure NFC functionality. {@link #isSecureNfcSupported}
+     */
+    public boolean isSecureNfcEnabled() {
+        if (!sHasNfcFeature && !sHasCeFeature) {
+            throw new UnsupportedOperationException();
+        }
+        try {
+            return sService.isNfcSecureEnabled();
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+            // Try one more time
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+                return false;
+            }
+            try {
+                return sService.isNfcSecureEnabled();
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Sets NFC Reader option feature.
+     * <p>This API is for the Settings application.
+     * @return True if successful
+     * @hide
+     */
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_READER_OPTION)
+    @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+    public boolean enableReaderOption(boolean enable) {
+        if (!sHasNfcFeature) {
+            throw new UnsupportedOperationException();
+        }
+        try {
+            return sService.enableReaderOption(enable);
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+            // Try one more time
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+                return false;
+            }
+            try {
+                return sService.enableReaderOption(enable);
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Checks if the device supports NFC Reader option functionality.
+     *
+     * @return True if device supports NFC Reader option, false otherwise
+     * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_READER_OPTION)
+    public boolean isReaderOptionSupported() {
+        if (!sHasNfcFeature) {
+            throw new UnsupportedOperationException();
+        }
+        try {
+            return sService.isReaderOptionSupported();
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+            // Try one more time
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+                return false;
+            }
+            try {
+                return sService.isReaderOptionSupported();
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Checks NFC Reader option feature is enabled.
+     *
+     * @return True if NFC Reader option  is enabled, false otherwise
+     * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+     * @throws UnsupportedOperationException if device doesn't support
+     *         NFC Reader option functionality. {@link #isReaderOptionSupported}
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_READER_OPTION)
+    public boolean isReaderOptionEnabled() {
+        if (!sHasNfcFeature) {
+            throw new UnsupportedOperationException();
+        }
+        try {
+            return sService.isReaderOptionEnabled();
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+            // Try one more time
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+                return false;
+            }
+            try {
+                return sService.isReaderOptionEnabled();
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Enable NDEF Push feature.
+     * <p>This API is for the Settings application.
+     * @hide
+     * @removed
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+    @UnsupportedAppUsage
+    public boolean enableNdefPush() {
+        return false;
+    }
+
+    /**
+     * Disable NDEF Push feature.
+     * <p>This API is for the Settings application.
+     * @hide
+     * @removed
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+    @UnsupportedAppUsage
+    public boolean disableNdefPush() {
+        return false;
+    }
+
+    /**
+     * Return true if the NDEF Push (Android Beam) feature is enabled.
+     * <p>This function will return true only if both NFC is enabled, and the
+     * NDEF Push feature is enabled.
+     * <p>Note that if NFC is enabled but NDEF Push is disabled then this
+     * device can still <i>receive</i> NDEF messages, it just cannot send them.
+     * <p>Applications cannot directly toggle the NDEF Push feature, but they
+     * can request Settings UI allowing the user to toggle NDEF Push using
+     * <code>startActivity(new Intent(Settings.ACTION_NFCSHARING_SETTINGS))</code>
+     * <p>Example usage in an Activity that requires NDEF Push:
+     * <p><pre>
+     * protected void onResume() {
+     *     super.onResume();
+     *     if (!nfcAdapter.isEnabled()) {
+     *         startActivity(new Intent(Settings.ACTION_NFC_SETTINGS));
+     *     } else if (!nfcAdapter.isNdefPushEnabled()) {
+     *         startActivity(new Intent(Settings.ACTION_NFCSHARING_SETTINGS));
+     *     }
+     * }</pre>
+     *
+     * @see android.provider.Settings#ACTION_NFCSHARING_SETTINGS
+     * @return true if NDEF Push feature is enabled
+     * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+     * @removed this feature is removed. File sharing can work using other technology like
+     * Bluetooth.
+     */
+    @java.lang.Deprecated
+    @UnsupportedAppUsage
+    public boolean isNdefPushEnabled() {
+        synchronized (sLock) {
+            if (!sHasNfcFeature) {
+                throw new UnsupportedOperationException();
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Signals that you are no longer interested in communicating with an NFC tag
+     * for as long as it remains in range.
+     *
+     * All future attempted communication to this tag will fail with {@link IOException}.
+     * The NFC controller will be put in a low-power polling mode, allowing the device
+     * to save power in cases where it's "attached" to a tag all the time (e.g. a tag in
+     * car dock).
+     *
+     * Additionally the debounceMs parameter allows you to specify for how long the tag needs
+     * to have gone out of range, before it will be dispatched again.
+     *
+     * Note: the NFC controller typically polls at a pretty slow interval (100 - 500 ms).
+     * This means that if the tag repeatedly goes in and out of range (for example, in
+     * case of a flaky connection), and the controller happens to poll every time the
+     * tag is out of range, it *will* re-dispatch the tag after debounceMs, despite the tag
+     * having been "in range" during the interval.
+     *
+     * Note 2: if a tag with another UID is detected after this API is called, its effect
+     * will be cancelled; if this tag shows up before the amount of time specified in
+     * debounceMs, it will be dispatched again.
+     *
+     * Note 3: some tags have a random UID, in which case this API won't work reliably.
+     *
+     * @param tag        the {@link android.nfc.Tag Tag} to ignore.
+     * @param debounceMs minimum amount of time the tag needs to be out of range before being
+     *                   dispatched again.
+     * @param tagRemovedListener listener to be called when the tag is removed from the field.
+     *                           Note that this will only be called if the tag has been out of range
+     *                           for at least debounceMs, or if another tag came into range before
+     *                           debounceMs. May be null in case you don't want a callback.
+     * @param handler the {@link android.os.Handler Handler} that will be used for delivering
+     *                the callback. if the handler is null, then the thread used for delivering
+     *                the callback is unspecified.
+     * @return false if the tag couldn't be found (or has already gone out of range), true otherwise
+     */
+    public boolean ignore(final Tag tag, int debounceMs,
+                          final OnTagRemovedListener tagRemovedListener, final Handler handler) {
+        ITagRemovedCallback.Stub iListener = null;
+        if (tagRemovedListener != null) {
+            iListener = new ITagRemovedCallback.Stub() {
+                @Override
+                public void onTagRemoved() throws RemoteException {
+                    if (handler != null) {
+                        handler.post(new Runnable() {
+                            @Override
+                            public void run() {
+                                tagRemovedListener.onTagRemoved();
+                            }
+                        });
+                    } else {
+                        tagRemovedListener.onTagRemoved();
+                    }
+                    synchronized (mLock) {
+                        mTagRemovedListener = null;
+                    }
+                }
+            };
+        }
+        synchronized (mLock) {
+            mTagRemovedListener = iListener;
+        }
+        try {
+            return sService.ignore(tag.getServiceHandle(), debounceMs, iListener);
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Inject a mock NFC tag.<p>
+     * Used for testing purposes.
+     * <p class="note">Requires the
+     * {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} permission.
+     * @hide
+     */
+    public void dispatch(Tag tag) {
+        if (tag == null) {
+            throw new NullPointerException("tag cannot be null");
+        }
+        try {
+            sService.dispatch(tag);
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+        }
+    }
+
+    /**
+     * Registers a new NFC unlock handler with the NFC service.
+     *
+     * <p />NFC unlock handlers are intended to unlock the keyguard in the presence of a trusted
+     * NFC device. The handler should return true if it successfully authenticates the user and
+     * unlocks the keyguard.
+     *
+     * <p /> The parameter {@code tagTechnologies} determines which Tag technologies will be polled for
+     * at the lockscreen. Polling for less tag technologies reduces latency, and so it is
+     * strongly recommended to only provide the Tag technologies that the handler is expected to
+     * receive. There must be at least one tag technology provided, otherwise the unlock handler
+     * is ignored.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+    public boolean addNfcUnlockHandler(final NfcUnlockHandler unlockHandler,
+                                       String[] tagTechnologies) {
+        synchronized (sLock) {
+            if (!sHasNfcFeature) {
+                throw new UnsupportedOperationException();
+            }
+        }
+        // If there are no tag technologies, don't bother adding unlock handler
+        if (tagTechnologies.length == 0) {
+            return false;
+        }
+
+        try {
+            synchronized (mLock) {
+                if (mNfcUnlockHandlers.containsKey(unlockHandler)) {
+                    // update the tag technologies
+                    sService.removeNfcUnlockHandler(mNfcUnlockHandlers.get(unlockHandler));
+                    mNfcUnlockHandlers.remove(unlockHandler);
+                }
+
+                INfcUnlockHandler.Stub iHandler = new INfcUnlockHandler.Stub() {
+                    @Override
+                    public boolean onUnlockAttempted(Tag tag) throws RemoteException {
+                        return unlockHandler.onUnlockAttempted(tag);
+                    }
+                };
+
+                sService.addNfcUnlockHandler(iHandler,
+                        Tag.getTechCodesFromStrings(tagTechnologies));
+                mNfcUnlockHandlers.put(unlockHandler, iHandler);
+            }
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+            return false;
+        } catch (IllegalArgumentException e) {
+            Log.e(TAG, "Unable to register LockscreenDispatch", e);
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Removes a previously registered unlock handler. Also removes the tag technologies
+     * associated with the removed unlock handler.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+    public boolean removeNfcUnlockHandler(NfcUnlockHandler unlockHandler) {
+        synchronized (sLock) {
+            if (!sHasNfcFeature) {
+                throw new UnsupportedOperationException();
+            }
+        }
+        try {
+            synchronized (mLock) {
+                if (mNfcUnlockHandlers.containsKey(unlockHandler)) {
+                    sService.removeNfcUnlockHandler(mNfcUnlockHandlers.remove(unlockHandler));
+                }
+
+                return true;
+            }
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+            return false;
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public INfcAdapterExtras getNfcAdapterExtrasInterface() {
+        if (mContext == null) {
+            throw new UnsupportedOperationException("You need a context on NfcAdapter to use the "
+                    + " NFC extras APIs");
+        }
+        try {
+            return sService.getNfcAdapterExtrasInterface(mContext.getPackageName());
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+            // Try one more time
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+                return null;
+            }
+            try {
+                return sService.getNfcAdapterExtrasInterface(mContext.getPackageName());
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+            }
+            return null;
+        }
+    }
+
+    void enforceResumed(Activity activity) {
+        if (!activity.isResumed()) {
+            throw new IllegalStateException("API cannot be called while activity is paused");
+        }
+    }
+
+    int getSdkVersion() {
+        if (mContext == null) {
+            return android.os.Build.VERSION_CODES.GINGERBREAD; // best guess
+        } else {
+            return mContext.getApplicationInfo().targetSdkVersion;
+        }
+    }
+
+    /**
+     * Sets NFC controller always on feature.
+     * <p>This API is for the NFCC internal state management. It allows to discriminate
+     * the controller function from the NFC function by keeping the NFC controller on without
+     * any NFC RF enabled if necessary.
+     * <p>This call is asynchronous. Register a listener {@link #ControllerAlwaysOnListener}
+     * by {@link #registerControllerAlwaysOnListener} to find out when the operation is
+     * complete.
+     * <p>If this returns true, then either NFCC always on state has been set based on the value,
+     * or a {@link ControllerAlwaysOnListener#onControllerAlwaysOnChanged(boolean)} will be invoked
+     * to indicate the state change.
+     * If this returns false, then there is some problem that prevents an attempt to turn NFCC
+     * always on.
+     * @param value if true the NFCC will be kept on (with no RF enabled if NFC adapter is
+     * disabled), if false the NFCC will follow completely the Nfc adapter state.
+     * @throws UnsupportedOperationException if FEATURE_NFC,
+     * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF,
+     * FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC and FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE
+     * are unavailable
+     * @return void
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON)
+    public boolean setControllerAlwaysOn(boolean value) {
+        if (!sHasNfcFeature && !sHasCeFeature) {
+            throw new UnsupportedOperationException();
+        }
+        try {
+            return sService.setControllerAlwaysOn(value);
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+            // Try one more time
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+                return false;
+            }
+            try {
+                return sService.setControllerAlwaysOn(value);
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Checks NFC controller always on feature is enabled.
+     *
+     * @return True if NFC controller always on is enabled, false otherwise
+     * @throws UnsupportedOperationException if FEATURE_NFC,
+     * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF,
+     * FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC and FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE
+     * are unavailable
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON)
+    public boolean isControllerAlwaysOn() {
+        try {
+            return sService.isControllerAlwaysOn();
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+            // Try one more time
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+                return false;
+            }
+            try {
+                return sService.isControllerAlwaysOn();
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Checks if the device supports NFC controller always on functionality.
+     *
+     * @return True if device supports NFC controller always on, false otherwise
+     * @throws UnsupportedOperationException if FEATURE_NFC,
+     * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF,
+     * FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC and FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE
+     * are unavailable
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON)
+    public boolean isControllerAlwaysOnSupported() {
+        if (!sHasNfcFeature && !sHasCeFeature) {
+            throw new UnsupportedOperationException();
+        }
+        try {
+            return sService.isControllerAlwaysOnSupported();
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+            // Try one more time
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+                return false;
+            }
+            try {
+                return sService.isControllerAlwaysOnSupported();
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Register a {@link ControllerAlwaysOnListener} to listen for NFC controller always on
+     * state changes
+     * <p>The provided listener will be invoked by the given {@link Executor}.
+     *
+     * @param executor an {@link Executor} to execute given listener
+     * @param listener user implementation of the {@link ControllerAlwaysOnListener}
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON)
+    public void registerControllerAlwaysOnListener(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull ControllerAlwaysOnListener listener) {
+        mControllerAlwaysOnListener.register(executor, listener);
+    }
+
+    /**
+     * Unregister the specified {@link ControllerAlwaysOnListener}
+     * <p>The same {@link ControllerAlwaysOnListener} object used when calling
+     * {@link #registerControllerAlwaysOnListener(Executor, ControllerAlwaysOnListener)}
+     * must be used.
+     *
+     * <p>Listeners are automatically unregistered when application process goes away
+     *
+     * @param listener user implementation of the {@link ControllerAlwaysOnListener}
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON)
+    public void unregisterControllerAlwaysOnListener(
+            @NonNull ControllerAlwaysOnListener listener) {
+        mControllerAlwaysOnListener.unregister(listener);
+    }
+
+
+    /**
+     * Sets whether we dispatch NFC Tag intents to the package.
+     *
+     * <p>{@link #ACTION_NDEF_DISCOVERED}, {@link #ACTION_TECH_DISCOVERED} or
+     * {@link #ACTION_TAG_DISCOVERED} will not be dispatched to an Activity if its package is
+     * disallowed.
+     * <p>An app is added to the preference list with the allowed flag set to {@code true}
+     * when a Tag intent is dispatched to the package for the first time. This API is called
+     * by settings to note that the user wants to change this default preference.
+     *
+     * @param userId the user to whom this package name will belong to
+     * @param pkg the full name (i.e. com.google.android.tag) of the package that will be added to
+     * the preference list
+     * @param allow {@code true} to allow dispatching Tag intents to the package's activity,
+     * {@code false} otherwise
+     * @return the {@link #TagIntentAppPreferenceResult} value
+     * @throws UnsupportedOperationException if {@link isTagIntentAppPreferenceSupported} returns
+     * {@code false}
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+    @TagIntentAppPreferenceResult
+    public int setTagIntentAppPreferenceForUser(@UserIdInt int userId,
+                @NonNull String pkg, boolean allow) {
+        Objects.requireNonNull(pkg, "pkg cannot be null");
+        if (!isTagIntentAppPreferenceSupported()) {
+            Log.e(TAG, "TagIntentAppPreference is not supported");
+            throw new UnsupportedOperationException();
+        }
+        try {
+            return sService.setTagIntentAppPreferenceForUser(userId, pkg, allow);
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+            // Try one more time
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+            }
+            try {
+                return sService.setTagIntentAppPreferenceForUser(userId, pkg, allow);
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+            }
+            return TAG_INTENT_APP_PREF_RESULT_UNAVAILABLE;
+        }
+    }
+
+
+    /**
+     * Get the Tag dispatch preference list of the UserId.
+     *
+     * <p>This returns a mapping of package names for this user id to whether we dispatch Tag
+     * intents to the package. {@link #ACTION_NDEF_DISCOVERED}, {@link #ACTION_TECH_DISCOVERED} or
+     * {@link #ACTION_TAG_DISCOVERED} will not be dispatched to an Activity if its package is
+     * mapped to {@code false}.
+     * <p>There are three different possible cases:
+     * <p>A package not being in the preference list.
+     * It does not contain any Tag intent filters or the user never triggers a Tag detection that
+     * matches the intent filter of the package.
+     * <p>A package being mapped to {@code true}.
+     * When a package has been launched by a tag detection for the first time, the package name is
+     * put to the map and by default mapped to {@code true}. The package will receive Tag intents as
+     * usual.
+     * <p>A package being mapped to {@code false}.
+     * The user chooses to disable this package and it will not receive any Tag intents anymore.
+     *
+     * @param userId the user to whom this preference list will belong to
+     * @return a map of the UserId which indicates the mapping from package name to
+     * boolean(allow status), otherwise return an empty map
+     * @throws UnsupportedOperationException if {@link isTagIntentAppPreferenceSupported} returns
+     * {@code false}
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+    @NonNull
+    public Map<String, Boolean> getTagIntentAppPreferenceForUser(@UserIdInt int userId) {
+        if (!isTagIntentAppPreferenceSupported()) {
+            Log.e(TAG, "TagIntentAppPreference is not supported");
+            throw new UnsupportedOperationException();
+        }
+        try {
+            Map<String, Boolean> result = (Map<String, Boolean>) sService
+                     .getTagIntentAppPreferenceForUser(userId);
+            return result;
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+            // Try one more time
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+                return Collections.emptyMap();
+            }
+            try {
+                Map<String, Boolean> result = (Map<String, Boolean>) sService
+                        .getTagIntentAppPreferenceForUser(userId);
+                return result;
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+            }
+            return Collections.emptyMap();
+        }
+    }
+
+    /**
+     * Checks if the device supports Tag application preference.
+     *
+     * @return {@code true} if the device supports Tag application preference, {@code false}
+     * otherwise
+     * @throws UnsupportedOperationException if FEATURE_NFC is unavailable
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+    public boolean isTagIntentAppPreferenceSupported() {
+        if (!sHasNfcFeature) {
+            throw new UnsupportedOperationException();
+        }
+        try {
+            return sService.isTagIntentAppPreferenceSupported();
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+            // Try one more time
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+                return false;
+            }
+            try {
+                return sService.isTagIntentAppPreferenceSupported();
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Sets NFC charging feature.
+     * <p>This API is for the Settings application.
+     * @return True if successful
+     * @hide
+     */
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_CHARGING)
+    @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+    public boolean enableWlc(boolean enable) {
+        if (!sHasNfcWlcFeature) {
+            throw new UnsupportedOperationException();
+        }
+        try {
+            return sService.enableWlc(enable);
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+            // Try one more time
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+                return false;
+            }
+            try {
+                return sService.enableWlc(enable);
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Checks NFC charging feature is enabled.
+     *
+     * @return True if NFC charging is enabled, false otherwise
+     * @throws UnsupportedOperationException if FEATURE_NFC_CHARGING
+     * is unavailable
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_CHARGING)
+    public boolean isWlcEnabled() {
+        if (!sHasNfcWlcFeature) {
+            throw new UnsupportedOperationException();
+        }
+        try {
+            return sService.isWlcEnabled();
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+            // Try one more time
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+                return false;
+            }
+            try {
+                return sService.isWlcEnabled();
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+            }
+            return false;
+        }
+    }
+
+    /**
+     * A listener to be invoked when NFC controller always on state changes.
+     * <p>Register your {@code ControllerAlwaysOnListener} implementation with {@link
+     * NfcAdapter#registerWlcStateListener} and disable it with {@link
+     * NfcAdapter#unregisterWlcStateListenerListener}.
+     * @see #registerWlcStateListener
+     * @hide
+     */
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_CHARGING)
+    public interface WlcStateListener {
+        /**
+         * Called on NFC WLC state changes
+         */
+        void onWlcStateChanged(@NonNull WlcLDeviceInfo wlcLDeviceInfo);
+    }
+
+    /**
+     * Register a {@link WlcStateListener} to listen for NFC WLC state changes
+     * <p>The provided listener will be invoked by the given {@link Executor}.
+     *
+     * @param executor an {@link Executor} to execute given listener
+     * @param listener user implementation of the {@link WlcStateListener}
+     * @throws UnsupportedOperationException if FEATURE_NFC_CHARGING
+     * is unavailable
+     *
+     * @hide
+     */
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_CHARGING)
+    public void registerWlcStateListener(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull WlcStateListener listener) {
+        if (!sHasNfcWlcFeature) {
+            throw new UnsupportedOperationException();
+        }
+        mNfcWlcStateListener.register(executor, listener);
+    }
+
+    /**
+     * Unregister the specified {@link WlcStateListener}
+     * <p>The same {@link WlcStateListener} object used when calling
+     * {@link #registerWlcStateListener(Executor, WlcStateListener)}
+     * must be used.
+     *
+     * <p>Listeners are automatically unregistered when application process goes away
+     *
+     * @param listener user implementation of the {@link WlcStateListener}a
+     * @throws UnsupportedOperationException if FEATURE_NFC_CHARGING
+     * is unavailable
+     *
+     * @hide
+     */
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_CHARGING)
+    public void unregisterWlcStateListener(
+            @NonNull WlcStateListener listener) {
+        if (!sHasNfcWlcFeature) {
+            throw new UnsupportedOperationException();
+        }
+        mNfcWlcStateListener.unregister(listener);
+    }
+
+    /**
+     * Returns information on the NFC charging listener device
+     *
+     * @return Information on the NFC charging listener device
+     * @throws UnsupportedOperationException if FEATURE_NFC_CHARGING
+     * is unavailable
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_CHARGING)
+    @Nullable
+    public WlcLDeviceInfo getWlcLDeviceInfo() {
+        if (!sHasNfcWlcFeature) {
+            throw new UnsupportedOperationException();
+        }
+        try {
+            return sService.getWlcLDeviceInfo();
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+            // Try one more time
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+                return null;
+            }
+            try {
+                return sService.getWlcLDeviceInfo();
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+            }
+            return null;
+        }
+    }
+}
diff --git a/nfc/java/android/nfc/NfcAntennaInfo.aidl b/nfc/java/android/nfc/NfcAntennaInfo.aidl
new file mode 100644
index 0000000..d5e79fc
--- /dev/null
+++ b/nfc/java/android/nfc/NfcAntennaInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc;
+
+parcelable NfcAntennaInfo;
diff --git a/nfc/java/android/nfc/NfcAntennaInfo.java b/nfc/java/android/nfc/NfcAntennaInfo.java
new file mode 100644
index 0000000..b002ca2
--- /dev/null
+++ b/nfc/java/android/nfc/NfcAntennaInfo.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * Contains information on all available Nfc
+ * antennas on an Android device as well as information
+ * on the device itself in relation positioning of the
+ * antennas.
+ */
+public final class NfcAntennaInfo implements Parcelable {
+    // Width of the device in millimeters.
+    private final int mDeviceWidth;
+    // Height of the device in millimeters.
+    private final int mDeviceHeight;
+    // Whether the device is foldable.
+    private final boolean mDeviceFoldable;
+    // All available Nfc Antennas on the device.
+    private final List<AvailableNfcAntenna> mAvailableNfcAntennas;
+
+    public NfcAntennaInfo(int deviceWidth, int deviceHeight, boolean deviceFoldable,
+            @NonNull List<AvailableNfcAntenna> availableNfcAntennas) {
+        this.mDeviceWidth = deviceWidth;
+        this.mDeviceHeight = deviceHeight;
+        this.mDeviceFoldable = deviceFoldable;
+        this.mAvailableNfcAntennas = availableNfcAntennas;
+    }
+
+    /**
+     * Width of the device in millimeters.
+     */
+    public int getDeviceWidth() {
+        return mDeviceWidth;
+    }
+
+    /**
+     * Height of the device in millimeters.
+     */
+    public int getDeviceHeight() {
+        return mDeviceHeight;
+    }
+
+    /**
+     * Whether the device is foldable. When the device is foldable,
+     * the 0, 0 is considered to be bottom-left when the device is unfolded and
+     * the screens are facing the user. For non-foldable devices 0, 0
+     * is bottom-left when the user is facing the screen.
+     */
+    public boolean isDeviceFoldable() {
+        return mDeviceFoldable;
+    }
+
+    /**
+     * Get all NFC antennas that exist on the device.
+     */
+    @NonNull
+    public List<AvailableNfcAntenna> getAvailableNfcAntennas() {
+        return mAvailableNfcAntennas;
+    }
+
+    private NfcAntennaInfo(Parcel in) {
+        this.mDeviceWidth = in.readInt();
+        this.mDeviceHeight = in.readInt();
+        this.mDeviceFoldable = in.readByte() != 0;
+        this.mAvailableNfcAntennas = new ArrayList<>();
+        in.readTypedList(this.mAvailableNfcAntennas,
+                AvailableNfcAntenna.CREATOR);
+    }
+
+    public static final @NonNull Parcelable.Creator<NfcAntennaInfo> CREATOR =
+            new Parcelable.Creator<NfcAntennaInfo>() {
+        @Override
+        public NfcAntennaInfo createFromParcel(Parcel in) {
+            return new NfcAntennaInfo(in);
+        }
+
+        @Override
+        public NfcAntennaInfo[] newArray(int size) {
+            return new NfcAntennaInfo[size];
+        }
+    };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mDeviceWidth);
+        dest.writeInt(mDeviceHeight);
+        dest.writeByte((byte) (mDeviceFoldable ? 1 : 0));
+        dest.writeTypedList(mAvailableNfcAntennas, 0);
+    }
+}
diff --git a/nfc/java/android/nfc/NfcControllerAlwaysOnListener.java b/nfc/java/android/nfc/NfcControllerAlwaysOnListener.java
new file mode 100644
index 0000000..6ae58fd
--- /dev/null
+++ b/nfc/java/android/nfc/NfcControllerAlwaysOnListener.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2021 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 android.nfc;
+
+import android.annotation.NonNull;
+import android.nfc.NfcAdapter.ControllerAlwaysOnListener;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.Executor;
+
+/**
+ * @hide
+ */
+public class NfcControllerAlwaysOnListener extends INfcControllerAlwaysOnListener.Stub {
+    private static final String TAG = NfcControllerAlwaysOnListener.class.getSimpleName();
+
+    private final INfcAdapter mAdapter;
+
+    private final Map<ControllerAlwaysOnListener, Executor> mListenerMap = new HashMap<>();
+
+    private boolean mCurrentState = false;
+    private boolean mIsRegistered = false;
+
+    public NfcControllerAlwaysOnListener(@NonNull INfcAdapter adapter) {
+        mAdapter = adapter;
+    }
+
+    /**
+     * Register a {@link ControllerAlwaysOnListener} with this
+     * {@link NfcControllerAlwaysOnListener}
+     *
+     * @param executor an {@link Executor} to execute given listener
+     * @param listener user implementation of the {@link ControllerAlwaysOnListener}
+     */
+    public void register(@NonNull Executor executor,
+            @NonNull ControllerAlwaysOnListener listener) {
+        try {
+            if (!mAdapter.isControllerAlwaysOnSupported()) {
+                return;
+            }
+        } catch (RemoteException e) {
+            Log.w(TAG, "Failed to register");
+            return;
+        }
+        synchronized (this) {
+            if (mListenerMap.containsKey(listener)) {
+                return;
+            }
+
+            mListenerMap.put(listener, executor);
+            if (!mIsRegistered) {
+                try {
+                    mAdapter.registerControllerAlwaysOnListener(this);
+                    mIsRegistered = true;
+                } catch (RemoteException e) {
+                    Log.w(TAG, "Failed to register");
+                }
+            }
+        }
+    }
+
+    /**
+     * Unregister the specified {@link ControllerAlwaysOnListener}
+     *
+     * @param listener user implementation of the {@link ControllerAlwaysOnListener}
+     */
+    public void unregister(@NonNull ControllerAlwaysOnListener listener) {
+        try {
+            if (!mAdapter.isControllerAlwaysOnSupported()) {
+                return;
+            }
+        } catch (RemoteException e) {
+            Log.w(TAG, "Failed to unregister");
+            return;
+        }
+        synchronized (this) {
+            if (!mListenerMap.containsKey(listener)) {
+                return;
+            }
+
+            mListenerMap.remove(listener);
+
+            if (mListenerMap.isEmpty() && mIsRegistered) {
+                try {
+                    mAdapter.unregisterControllerAlwaysOnListener(this);
+                } catch (RemoteException e) {
+                    Log.w(TAG, "Failed to unregister");
+                }
+                mIsRegistered = false;
+            }
+        }
+    }
+
+    private void sendCurrentState(@NonNull ControllerAlwaysOnListener listener) {
+        synchronized (this) {
+            Executor executor = mListenerMap.get(listener);
+
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                executor.execute(() -> listener.onControllerAlwaysOnChanged(
+                        mCurrentState));
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+    }
+
+    @Override
+    public void onControllerAlwaysOnChanged(boolean isEnabled) {
+        synchronized (this) {
+            mCurrentState = isEnabled;
+            for (ControllerAlwaysOnListener cb : mListenerMap.keySet()) {
+                sendCurrentState(cb);
+            }
+        }
+    }
+}
+
diff --git a/nfc/java/android/nfc/NfcEvent.java b/nfc/java/android/nfc/NfcEvent.java
new file mode 100644
index 0000000..aff4f52
--- /dev/null
+++ b/nfc/java/android/nfc/NfcEvent.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc;
+
+/**
+ * Wraps information associated with any NFC event.
+ *
+ * <p>Immutable object, with direct access to the (final) fields.
+ *
+ * <p>An {@link NfcEvent} object is usually included in callbacks from
+ * {@link NfcAdapter}. Check the documentation of the callback to see
+ * which fields may be set.
+ *
+ * <p>This wrapper object is used (instead of parameters
+ * in the callback) because it allows new fields to be added without breaking
+ * API compatibility.
+ *
+ * @see NfcAdapter.OnNdefPushCompleteCallback#onNdefPushComplete
+ * @see NfcAdapter.CreateNdefMessageCallback#createNdefMessage
+ */
+public final class NfcEvent {
+    /**
+     * The {@link NfcAdapter} associated with the NFC event.
+     */
+    public final NfcAdapter nfcAdapter;
+
+    /**
+     * The major LLCP version number of the peer associated with the NFC event.
+     */
+    public final int peerLlcpMajorVersion;
+
+    /**
+     * The minor LLCP version number of the peer associated with the NFC event.
+     */
+    public final int peerLlcpMinorVersion;
+
+    NfcEvent(NfcAdapter nfcAdapter, byte peerLlcpVersion) {
+        this.nfcAdapter = nfcAdapter;
+        this.peerLlcpMajorVersion = (peerLlcpVersion & 0xF0) >> 4;
+        this.peerLlcpMinorVersion = peerLlcpVersion & 0x0F;
+    }
+}
diff --git a/nfc/java/android/nfc/NfcFrameworkInitializer.java b/nfc/java/android/nfc/NfcFrameworkInitializer.java
new file mode 100644
index 0000000..1ab8a1e
--- /dev/null
+++ b/nfc/java/android/nfc/NfcFrameworkInitializer.java
@@ -0,0 +1,71 @@
+/*
+ * 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 android.nfc;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.app.SystemServiceRegistry;
+import android.content.Context;
+
+/**
+ * Class for performing registration for Nfc service.
+ *
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public class NfcFrameworkInitializer {
+    private NfcFrameworkInitializer() {}
+
+    private static volatile NfcServiceManager sNfcServiceManager;
+
+    /**
+     * Sets an instance of {@link NfcServiceManager} that allows
+     * the nfc mainline module to register/obtain nfc binder services. This is called
+     * by the platform during the system initialization.
+     *
+     * @param nfcServiceManager instance of {@link NfcServiceManager} that allows
+     * the nfc mainline module to register/obtain nfcd binder services.
+     */
+    public static void setNfcServiceManager(
+            @NonNull NfcServiceManager nfcServiceManager) {
+        if (sNfcServiceManager != null) {
+            throw new IllegalStateException("setNfcServiceManager called twice!");
+        }
+
+        if (nfcServiceManager == null) {
+            throw new IllegalArgumentException("nfcServiceManager must not be null");
+        }
+
+        sNfcServiceManager = nfcServiceManager;
+    }
+
+    /** @hide */
+    public static NfcServiceManager getNfcServiceManager() {
+        return sNfcServiceManager;
+    }
+
+    /**
+     * Called by {@link SystemServiceRegistry}'s static initializer and registers NFC service
+     * to {@link Context}, so that {@link Context#getSystemService} can return them.
+     *
+     * @throws IllegalStateException if this is called from anywhere besides
+     * {@link SystemServiceRegistry}
+     */
+    public static void registerServiceWrappers() {
+        SystemServiceRegistry.registerContextAwareService(Context.NFC_SERVICE,
+                NfcManager.class, context -> new NfcManager(context));
+    }
+}
diff --git a/nfc/java/android/nfc/NfcManager.java b/nfc/java/android/nfc/NfcManager.java
new file mode 100644
index 0000000..644e312
--- /dev/null
+++ b/nfc/java/android/nfc/NfcManager.java
@@ -0,0 +1,73 @@
+/*
+ * 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 android.nfc;
+
+import android.annotation.SystemService;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
+import android.os.Build;
+
+/**
+ * High level manager used to obtain an instance of an {@link NfcAdapter}.
+ * <p>
+ * Use {@link android.content.Context#getSystemService(java.lang.String)}
+ * with {@link Context#NFC_SERVICE} to create an {@link NfcManager},
+ * then call {@link #getDefaultAdapter} to obtain the {@link NfcAdapter}.
+ * <p>
+ * Alternately, you can just call the static helper
+ * {@link NfcAdapter#getDefaultAdapter(android.content.Context)}.
+ *
+ * <div class="special reference">
+ * <h3>Developer Guides</h3>
+ * <p>For more information about using NFC, read the
+ * <a href="{@docRoot}guide/topics/nfc/index.html">Near Field Communication</a> developer guide.</p>
+ * </div>
+ *
+ * @see NfcAdapter#getDefaultAdapter(android.content.Context)
+ */
+@SystemService(Context.NFC_SERVICE)
+public final class NfcManager {
+    private final NfcAdapter mAdapter;
+
+    /**
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    public NfcManager(Context context) {
+        NfcAdapter adapter;
+        context = context.getApplicationContext();
+        if (context == null) {
+            throw new IllegalArgumentException(
+                    "context not associated with any application (using a mock context?)");
+        }
+        try {
+            adapter = NfcAdapter.getNfcAdapter(context);
+        } catch (UnsupportedOperationException e) {
+            adapter = null;
+        }
+        mAdapter = adapter;
+    }
+
+    /**
+     * Get the default NFC Adapter for this device.
+     *
+     * @return the default NFC Adapter
+     */
+    public NfcAdapter getDefaultAdapter() {
+        return mAdapter;
+    }
+}
diff --git a/nfc/java/android/nfc/NfcServiceManager.java b/nfc/java/android/nfc/NfcServiceManager.java
new file mode 100644
index 0000000..5582f11
--- /dev/null
+++ b/nfc/java/android/nfc/NfcServiceManager.java
@@ -0,0 +1,126 @@
+/*
+ * 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.
+ */
+
+
+/**********************************************************************
+ * This file is not a part of the NFC mainline modure                 *
+ * *******************************************************************/
+
+package android.nfc;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.SystemApi.Client;
+import android.content.Context;
+import android.os.IBinder;
+import android.os.ServiceManager;
+
+/**
+ * Provides a way to register and obtain the system service binder objects managed by the nfc
+ * service.
+ *
+ * @hide
+ */
+@SystemApi(client = Client.MODULE_LIBRARIES)
+public class NfcServiceManager {
+
+    /**
+     * @hide
+     */
+    public NfcServiceManager() {
+    }
+
+    /**
+     * A class that exposes the methods to register and obtain each system service.
+     */
+    public static final class ServiceRegisterer {
+        private final String mServiceName;
+
+        /**
+         * @hide
+         */
+        public ServiceRegisterer(String serviceName) {
+            mServiceName = serviceName;
+        }
+
+        /**
+         * Register a system server binding object for a service.
+         */
+        public void register(@NonNull IBinder service) {
+            ServiceManager.addService(mServiceName, service);
+        }
+
+        /**
+         * Get the system server binding object for a service.
+         *
+         * <p>This blocks until the service instance is ready,
+         * or a timeout happens, in which case it returns null.
+         */
+        @Nullable
+        public IBinder get() {
+            return ServiceManager.getService(mServiceName);
+        }
+
+        /**
+         * Get the system server binding object for a service.
+         *
+         * <p>This blocks until the service instance is ready,
+         * or a timeout happens, in which case it throws {@link ServiceNotFoundException}.
+         */
+        @NonNull
+        public IBinder getOrThrow() throws ServiceNotFoundException {
+            try {
+                return ServiceManager.getServiceOrThrow(mServiceName);
+            } catch (ServiceManager.ServiceNotFoundException e) {
+                throw new ServiceNotFoundException(mServiceName);
+            }
+        }
+
+        /**
+         * Get the system server binding object for a service. If the specified service is
+         * not available, it returns null.
+         */
+        @Nullable
+        public IBinder tryGet() {
+            return ServiceManager.checkService(mServiceName);
+        }
+    }
+
+    /**
+     * See {@link ServiceRegisterer#getOrThrow}.
+     *
+     */
+    public static class ServiceNotFoundException extends ServiceManager.ServiceNotFoundException {
+        /**
+         * Constructor.
+         *
+         * @param name the name of the binder service that cannot be found.
+         *
+         */
+        public ServiceNotFoundException(@NonNull String name) {
+            super(name);
+        }
+    }
+
+    /**
+     * Returns {@link ServiceRegisterer} for the "nfc" service.
+     */
+    @NonNull
+    public ServiceRegisterer getNfcManagerServiceRegisterer() {
+        return new ServiceRegisterer(Context.NFC_SERVICE);
+    }
+}
diff --git a/nfc/java/android/nfc/NfcWlcStateListener.java b/nfc/java/android/nfc/NfcWlcStateListener.java
new file mode 100644
index 0000000..8d79310
--- /dev/null
+++ b/nfc/java/android/nfc/NfcWlcStateListener.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 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 android.nfc;
+
+import android.annotation.NonNull;
+import android.nfc.NfcAdapter.WlcStateListener;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.Executor;
+
+/**
+ * @hide
+ */
+public class NfcWlcStateListener extends INfcWlcStateListener.Stub {
+    private static final String TAG = NfcWlcStateListener.class.getSimpleName();
+
+    private final INfcAdapter mAdapter;
+
+    private final Map<WlcStateListener, Executor> mListenerMap = new HashMap<>();
+
+    private WlcLDeviceInfo mCurrentState = null;
+    private boolean mIsRegistered = false;
+
+    public NfcWlcStateListener(@NonNull INfcAdapter adapter) {
+        mAdapter = adapter;
+    }
+
+    /**
+     * Register a {@link WlcStateListener} with this
+     * {@link WlcStateListener}
+     *
+     * @param executor an {@link Executor} to execute given listener
+     * @param listener user implementation of the {@link WlcStateListener}
+     */
+    public void register(@NonNull Executor executor, @NonNull WlcStateListener listener) {
+        synchronized (this) {
+            if (mListenerMap.containsKey(listener)) {
+                return;
+            }
+
+            mListenerMap.put(listener, executor);
+
+            if (!mIsRegistered) {
+                try {
+                    mAdapter.registerWlcStateListener(this);
+                    mIsRegistered = true;
+                } catch (RemoteException e) {
+                    Log.w(TAG, "Failed to register");
+                }
+            }
+        }
+    }
+
+    /**
+     * Unregister the specified {@link WlcStateListener}
+     *
+     * @param listener user implementation of the {@link WlcStateListener}
+     */
+    public void unregister(@NonNull WlcStateListener listener) {
+        synchronized (this) {
+            if (!mListenerMap.containsKey(listener)) {
+                return;
+            }
+
+            mListenerMap.remove(listener);
+
+            if (mListenerMap.isEmpty() && mIsRegistered) {
+                try {
+                    mAdapter.unregisterWlcStateListener(this);
+                } catch (RemoteException e) {
+                    Log.w(TAG, "Failed to unregister");
+                }
+                mIsRegistered = false;
+            }
+        }
+    }
+
+    private void sendCurrentState(@NonNull WlcStateListener listener) {
+        synchronized (this) {
+            Executor executor = mListenerMap.get(listener);
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                executor.execute(() -> listener.onWlcStateChanged(
+                        mCurrentState));
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+    }
+
+    @Override
+    public void onWlcStateChanged(@NonNull WlcLDeviceInfo wlcLDeviceInfo) {
+        synchronized (this) {
+            mCurrentState = wlcLDeviceInfo;
+
+            for (WlcStateListener cb : mListenerMap.keySet()) {
+                sendCurrentState(cb);
+            }
+        }
+    }
+}
+
diff --git a/nfc/java/android/nfc/Tag.aidl b/nfc/java/android/nfc/Tag.aidl
new file mode 100644
index 0000000..312261e
--- /dev/null
+++ b/nfc/java/android/nfc/Tag.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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 android.nfc;
+
+parcelable Tag;
\ No newline at end of file
diff --git a/nfc/java/android/nfc/Tag.java b/nfc/java/android/nfc/Tag.java
new file mode 100644
index 0000000..500038f
--- /dev/null
+++ b/nfc/java/android/nfc/Tag.java
@@ -0,0 +1,508 @@
+/*
+ * 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 android.nfc;
+
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
+import android.nfc.tech.IsoDep;
+import android.nfc.tech.MifareClassic;
+import android.nfc.tech.MifareUltralight;
+import android.nfc.tech.Ndef;
+import android.nfc.tech.NdefFormatable;
+import android.nfc.tech.NfcA;
+import android.nfc.tech.NfcB;
+import android.nfc.tech.NfcBarcode;
+import android.nfc.tech.NfcF;
+import android.nfc.tech.NfcV;
+import android.nfc.tech.TagTechnology;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashMap;
+
+/**
+ * Represents an NFC tag that has been discovered.
+ * <p>
+ * {@link Tag} is an immutable object that represents the state of a NFC tag at
+ * the time of discovery. It can be used as a handle to {@link TagTechnology} classes
+ * to perform advanced operations, or directly queried for its ID via {@link #getId} and the
+ * set of technologies it contains via {@link #getTechList}. Arrays passed to and
+ * returned by this class are <em>not</em> cloned, so be careful not to modify them.
+ * <p>
+ * A new tag object is created every time a tag is discovered (comes into range), even
+ * if it is the same physical tag. If a tag is removed and then returned into range, then
+ * only the most recent tag object can be successfully used to create a {@link TagTechnology}.
+ *
+ * <h3>Tag Dispatch</h3>
+ * When a tag is discovered, a {@link Tag} object is created and passed to a
+ * single activity via the {@link NfcAdapter#EXTRA_TAG} extra in an
+ * {@link android.content.Intent} via {@link Context#startActivity}. A four stage dispatch is used
+ * to select the
+ * most appropriate activity to handle the tag. The Android OS executes each stage in order,
+ * and completes dispatch as soon as a single matching activity is found. If there are multiple
+ * matching activities found at any one stage then the Android activity chooser dialog is shown
+ * to allow the user to select the activity to receive the tag.
+ *
+ * <p>The Tag dispatch mechanism was designed to give a high probability of dispatching
+ * a tag to the correct activity without showing the user an activity chooser dialog.
+ * This is important for NFC interactions because they are very transient -- if a user has to
+ * move the Android device to choose an application then the connection will likely be broken.
+ *
+ * <h4>1. Foreground activity dispatch</h4>
+ * A foreground activity that has called
+ * {@link NfcAdapter#enableForegroundDispatch NfcAdapter.enableForegroundDispatch()} is
+ * given priority. See the documentation on
+ * {@link NfcAdapter#enableForegroundDispatch NfcAdapter.enableForegroundDispatch()} for
+ * its usage.
+ * <h4>2. NDEF data dispatch</h4>
+ * If the tag contains NDEF data the system inspects the first {@link NdefRecord} in the first
+ * {@link NdefMessage}. If the record is a URI, SmartPoster, or MIME data
+ * {@link Context#startActivity} is called with {@link NfcAdapter#ACTION_NDEF_DISCOVERED}. For URI
+ * and SmartPoster records the URI is put into the intent's data field. For MIME records the MIME
+ * type is put in the intent's type field. This allows activities to register to be launched only
+ * when data they know how to handle is present on a tag. This is the preferred method of handling
+ * data on a tag since NDEF data can be stored on many types of tags and doesn't depend on a
+ * specific tag technology. 
+ * See {@link NfcAdapter#ACTION_NDEF_DISCOVERED} for more detail. If the tag does not contain
+ * NDEF data, or if no activity is registered
+ * for {@link NfcAdapter#ACTION_NDEF_DISCOVERED} with a matching data URI or MIME type then dispatch
+ * moves to stage 3.
+ * <h4>3. Tag Technology dispatch</h4>
+ * {@link Context#startActivity} is called with {@link NfcAdapter#ACTION_TECH_DISCOVERED} to
+ * dispatch the tag to an activity that can handle the technologies present on the tag.
+ * Technologies are defined as sub-classes of {@link TagTechnology}, see the package
+ * {@link android.nfc.tech}. The Android OS looks for an activity that can handle one or
+ * more technologies in the tag. See {@link NfcAdapter#ACTION_TECH_DISCOVERED} for more detail.
+ * <h4>4. Fall-back dispatch</h4>
+ * If no activity has been matched then {@link Context#startActivity} is called with
+ * {@link NfcAdapter#ACTION_TAG_DISCOVERED}. This is intended as a fall-back mechanism.
+ * See {@link NfcAdapter#ACTION_TAG_DISCOVERED}.
+ *
+ * <h3>NFC Tag Background</h3>
+ * An NFC tag is a passive NFC device, powered by the NFC field of this Android device while
+ * it is in range. Tag's can come in many forms, such as stickers, cards, key fobs, or
+ * even embedded in a more sophisticated device.
+ * <p>
+ * Tags can have a wide range of capabilities. Simple tags just offer read/write semantics,
+ * and contain some one time
+ * programmable areas to make read-only. More complex tags offer math operations
+ * and per-sector access control and authentication. The most sophisticated tags
+ * contain operating environments allowing complex interactions with the
+ * code executing on the tag. Use {@link TagTechnology} classes to access a broad
+ * range of capabilities available in NFC tags.
+ * <p>
+ */
+public final class Tag implements Parcelable {
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    final byte[] mId;
+    final int[] mTechList;
+    final String[] mTechStringList;
+    final Bundle[] mTechExtras;
+    final int mServiceHandle;  // for use by NFC service, 0 indicates a mock
+    final long mCookie;        // for accessibility checking
+    final INfcTag mTagService; // interface to NFC service, will be null if mock tag
+
+    int mConnectedTechnology;
+
+    /**
+     * Hidden constructor to be used by NFC service and internal classes.
+     * @hide
+     */
+    public Tag(byte[] id, int[] techList, Bundle[] techListExtras, int serviceHandle,
+            long cookie, INfcTag tagService) {
+        if (techList == null) {
+            throw new IllegalArgumentException("rawTargets cannot be null");
+        }
+        mId = id;
+        mTechList = Arrays.copyOf(techList, techList.length);
+        mTechStringList = generateTechStringList(techList);
+        // Ensure mTechExtras is as long as mTechList
+        mTechExtras = Arrays.copyOf(techListExtras, techList.length);
+        mServiceHandle = serviceHandle;
+        mCookie = cookie;
+        mTagService = tagService;
+        mConnectedTechnology = -1;
+
+        if (tagService == null) {
+            return;
+        }
+    }
+
+    /**
+     * Construct a mock Tag.
+     * <p>This is an application constructed tag, so NfcAdapter methods on this Tag may fail
+     * with {@link IllegalArgumentException} since it does not represent a physical Tag.
+     * <p>This constructor might be useful for mock testing.
+     * @param id The tag identifier, can be null
+     * @param techList must not be null
+     * @return freshly constructed tag
+     * @hide
+     */
+    public static Tag createMockTag(byte[] id, int[] techList, Bundle[] techListExtras,
+            long cookie) {
+        // set serviceHandle to 0 and tagService to null to indicate mock tag
+        return new Tag(id, techList, techListExtras, 0, cookie, null);
+    }
+
+    private String[] generateTechStringList(int[] techList) {
+        final int size = techList.length;
+        String[] strings = new String[size];
+        for (int i = 0; i < size; i++) {
+            switch (techList[i]) {
+                case TagTechnology.ISO_DEP:
+                    strings[i] = IsoDep.class.getName();
+                    break;
+                case TagTechnology.MIFARE_CLASSIC:
+                    strings[i] = MifareClassic.class.getName();
+                    break;
+                case TagTechnology.MIFARE_ULTRALIGHT:
+                    strings[i] = MifareUltralight.class.getName();
+                    break;
+                case TagTechnology.NDEF:
+                    strings[i] = Ndef.class.getName();
+                    break;
+                case TagTechnology.NDEF_FORMATABLE:
+                    strings[i] = NdefFormatable.class.getName();
+                    break;
+                case TagTechnology.NFC_A:
+                    strings[i] = NfcA.class.getName();
+                    break;
+                case TagTechnology.NFC_B:
+                    strings[i] = NfcB.class.getName();
+                    break;
+                case TagTechnology.NFC_F:
+                    strings[i] = NfcF.class.getName();
+                    break;
+                case TagTechnology.NFC_V:
+                    strings[i] = NfcV.class.getName();
+                    break;
+                case TagTechnology.NFC_BARCODE:
+                    strings[i] = NfcBarcode.class.getName();
+                    break;
+                default:
+                    throw new IllegalArgumentException("Unknown tech type " + techList[i]);
+            }
+        }
+        return strings;
+    }
+
+    static int[] getTechCodesFromStrings(String[] techStringList) throws IllegalArgumentException {
+        if (techStringList == null) {
+            throw new IllegalArgumentException("List cannot be null");
+        }
+        int[] techIntList = new int[techStringList.length];
+        HashMap<String, Integer> stringToCodeMap = getTechStringToCodeMap();
+        for (int i = 0; i < techStringList.length; i++) {
+            Integer code = stringToCodeMap.get(techStringList[i]);
+
+            if (code == null) {
+                throw new IllegalArgumentException("Unknown tech type " + techStringList[i]);
+            }
+
+            techIntList[i] = code.intValue();
+        }
+        return techIntList;
+    }
+
+    private static HashMap<String, Integer> getTechStringToCodeMap() {
+        HashMap<String, Integer> techStringToCodeMap = new HashMap<String, Integer>();
+
+        techStringToCodeMap.put(IsoDep.class.getName(), TagTechnology.ISO_DEP);
+        techStringToCodeMap.put(MifareClassic.class.getName(), TagTechnology.MIFARE_CLASSIC);
+        techStringToCodeMap.put(MifareUltralight.class.getName(), TagTechnology.MIFARE_ULTRALIGHT);
+        techStringToCodeMap.put(Ndef.class.getName(), TagTechnology.NDEF);
+        techStringToCodeMap.put(NdefFormatable.class.getName(), TagTechnology.NDEF_FORMATABLE);
+        techStringToCodeMap.put(NfcA.class.getName(), TagTechnology.NFC_A);
+        techStringToCodeMap.put(NfcB.class.getName(), TagTechnology.NFC_B);
+        techStringToCodeMap.put(NfcF.class.getName(), TagTechnology.NFC_F);
+        techStringToCodeMap.put(NfcV.class.getName(), TagTechnology.NFC_V);
+        techStringToCodeMap.put(NfcBarcode.class.getName(), TagTechnology.NFC_BARCODE);
+
+        return techStringToCodeMap;
+    }
+
+    /**
+     * For use by NfcService only.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public int getServiceHandle() {
+        return mServiceHandle;
+    }
+
+    /**
+     * For use by NfcService only.
+     * @hide
+     */
+    public int[] getTechCodeList() {
+        return mTechList;
+    }
+
+    /**
+     * Get the Tag Identifier (if it has one).
+     * <p>The tag identifier is a low level serial number, used for anti-collision
+     * and identification.
+     * <p> Most tags have a stable unique identifier
+     * (UID), but some tags will generate a random ID every time they are discovered
+     * (RID), and there are some tags with no ID at all (the byte array will be zero-sized).
+     * <p> The size and format of an ID is specific to the RF technology used by the tag.
+     * <p> This function retrieves the ID as determined at discovery time, and does not
+     * perform any further RF communication or block.
+     * @return ID as byte array, never null
+     */
+    public byte[] getId() {
+        return mId;
+    }
+
+    /**
+     * Get the technologies available in this tag, as fully qualified class names.
+     * <p>
+     * A technology is an implementation of the {@link TagTechnology} interface,
+     * and can be instantiated by calling the static <code>get(Tag)</code>
+     * method on the implementation with this Tag. The {@link TagTechnology}
+     * object can then be used to perform advanced, technology-specific operations on a tag.
+     * <p>
+     * Android defines a mandatory set of technologies that must be correctly
+     * enumerated by all Android NFC devices, and an optional
+     * set of proprietary technologies.
+     * See {@link TagTechnology} for more details.
+     * <p>
+     * The ordering of the returned array is undefined and should not be relied upon.
+     * @return an array of fully-qualified {@link TagTechnology} class-names.
+     */
+    public String[] getTechList() {
+        return mTechStringList;
+    }
+
+    /**
+     * Rediscover the technologies available on this tag.
+     * <p>
+     * The technologies that are available on a tag may change due to
+     * operations being performed on a tag. For example, formatting a
+     * tag as NDEF adds the {@link Ndef} technology. The {@link rediscover}
+     * method reenumerates the available technologies on the tag
+     * and returns a new {@link Tag} object containing these technologies.
+     * <p>
+     * You may not be connected to any of this {@link Tag}'s technologies
+     * when calling this method.
+     * This method guarantees that you will be returned the same Tag
+     * if it is still in the field.
+     * <p>May cause RF activity and may block. Must not be called
+     * from the main application thread. A blocked call will be canceled with
+     * {@link IOException} by calling {@link #close} from another thread.
+     * <p>Does not remove power from the RF field, so a tag having a random
+     * ID should not change its ID.
+     * @return the rediscovered tag object.
+     * @throws IOException if the tag cannot be rediscovered
+     * @hide
+     */
+    // TODO See if we need TagLostException
+    // TODO Unhide for ICS
+    // TODO Update documentation to make sure it matches with the final
+    //      implementation.
+    public Tag rediscover() throws IOException {
+        if (getConnectedTechnology() != -1) {
+            throw new IllegalStateException("Close connection to the technology first!");
+        }
+
+        if (mTagService == null) {
+            throw new IOException("Mock tags don't support this operation.");
+        }
+        try {
+            Tag newTag = mTagService.rediscover(getServiceHandle());
+            if (newTag != null) {
+                return newTag;
+            } else {
+                throw new IOException("Failed to rediscover tag");
+            }
+        } catch (RemoteException e) {
+            throw new IOException("NFC service dead");
+        }
+    }
+
+
+    /** @hide */
+    public boolean hasTech(int techType) {
+        for (int tech : mTechList) {
+            if (tech == techType) return true;
+        }
+        return false;
+    }
+
+    /** @hide */
+    public Bundle getTechExtras(int tech) {
+        int pos = -1;
+        for (int idx = 0; idx < mTechList.length; idx++) {
+          if (mTechList[idx] == tech) {
+              pos = idx;
+              break;
+          }
+        }
+        if (pos < 0) {
+            return null;
+        }
+
+        return mTechExtras[pos];
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage
+    public INfcTag getTagService() {
+        if (mTagService == null) {
+            return null;
+        }
+
+        try {
+            if (!mTagService.isTagUpToDate(mCookie)) {
+                String id_str = "";
+                for (int i = 0; i < mId.length; i++) {
+                    id_str = id_str + String.format("%02X ", mId[i]);
+                }
+                String msg = "Permission Denial: Tag ( ID: " + id_str + ") is out of date";
+                throw new SecurityException(msg);
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+        return mTagService;
+    }
+
+    /**
+     * Human-readable description of the tag, for debugging.
+     */
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder("TAG: Tech [");
+        String[] techList = getTechList();
+        int length = techList.length;
+        for (int i = 0; i < length; i++) {
+            sb.append(techList[i]);
+            if (i < length - 1) {
+                sb.append(", ");
+            }
+        }
+        sb.append("]");
+        return sb.toString();
+    }
+
+    /*package*/ static byte[] readBytesWithNull(Parcel in) {
+        int len = in.readInt();
+        byte[] result = null;
+        if (len >= 0) {
+            result = new byte[len];
+            in.readByteArray(result);
+        }
+        return result;
+    }
+
+    /*package*/ static void writeBytesWithNull(Parcel out, byte[] b) {
+        if (b == null) {
+            out.writeInt(-1);
+            return;
+        }
+        out.writeInt(b.length);
+        out.writeByteArray(b);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        // Null mTagService means this is a mock tag
+        int isMock = (mTagService == null)?1:0;
+
+        writeBytesWithNull(dest, mId);
+        dest.writeInt(mTechList.length);
+        dest.writeIntArray(mTechList);
+        dest.writeTypedArray(mTechExtras, 0);
+        dest.writeInt(mServiceHandle);
+        dest.writeLong(mCookie);
+        dest.writeInt(isMock);
+        if (isMock == 0) {
+            dest.writeStrongBinder(mTagService.asBinder());
+        }
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<Tag> CREATOR =
+            new Parcelable.Creator<Tag>() {
+        @Override
+        public Tag createFromParcel(Parcel in) {
+            INfcTag tagService;
+
+            // Tag fields
+            byte[] id = Tag.readBytesWithNull(in);
+            int[] techList = new int[in.readInt()];
+            in.readIntArray(techList);
+            Bundle[] techExtras = in.createTypedArray(Bundle.CREATOR);
+            int serviceHandle = in.readInt();
+            long cookie = in.readLong();
+            int isMock = in.readInt();
+            if (isMock == 0) {
+                tagService = INfcTag.Stub.asInterface(in.readStrongBinder());
+            }
+            else {
+                tagService = null;
+            }
+
+            return new Tag(id, techList, techExtras, serviceHandle, cookie, tagService);
+        }
+
+        @Override
+        public Tag[] newArray(int size) {
+            return new Tag[size];
+        }
+    };
+
+    /**
+     * For internal use only.
+     *
+     * @hide
+     */
+    public synchronized boolean setConnectedTechnology(int technology) {
+        if (mConnectedTechnology != -1) {
+            return false;
+        }
+        mConnectedTechnology = technology;
+        return true;
+    }
+
+    /**
+     * For internal use only.
+     *
+     * @hide
+     */
+    public int getConnectedTechnology() {
+        return mConnectedTechnology;
+    }
+
+    /**
+     * For internal use only.
+     *
+     * @hide
+     */
+    public void setTechnologyDisconnected() {
+        mConnectedTechnology = -1;
+    }
+}
diff --git a/nfc/java/android/nfc/TagLostException.java b/nfc/java/android/nfc/TagLostException.java
new file mode 100644
index 0000000..1981d7c
--- /dev/null
+++ b/nfc/java/android/nfc/TagLostException.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2011, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc;
+
+import java.io.IOException;
+
+public class TagLostException extends IOException {
+    public TagLostException() {
+        super();
+    }
+
+    public TagLostException(String message) {
+        super(message);
+    }
+}
diff --git a/nfc/java/android/nfc/TechListParcel.aidl b/nfc/java/android/nfc/TechListParcel.aidl
new file mode 100644
index 0000000..92e646f
--- /dev/null
+++ b/nfc/java/android/nfc/TechListParcel.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc;
+
+parcelable TechListParcel;
\ No newline at end of file
diff --git a/nfc/java/android/nfc/TechListParcel.java b/nfc/java/android/nfc/TechListParcel.java
new file mode 100644
index 0000000..9f01559
--- /dev/null
+++ b/nfc/java/android/nfc/TechListParcel.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.nfc;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/** @hide */
+public class TechListParcel implements Parcelable {
+
+    private String[][] mTechLists;
+
+    public TechListParcel(String[]... strings) {
+        mTechLists = strings;
+    }
+
+    public String[][] getTechLists() {
+        return mTechLists;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        int count = mTechLists.length;
+        dest.writeInt(count);
+        for (int i = 0; i < count; i++) {
+            String[] techList = mTechLists[i];
+            dest.writeStringArray(techList);
+        }
+    }
+
+    public static final @android.annotation.NonNull Creator<TechListParcel> CREATOR = new Creator<TechListParcel>() {
+        @Override
+        public TechListParcel createFromParcel(Parcel source) {
+            int count = source.readInt();
+            String[][] techLists = new String[count][];
+            for (int i = 0; i < count; i++) {
+                techLists[i] = source.createStringArray();
+            }
+            return new TechListParcel(techLists);
+        }
+
+        @Override
+        public TechListParcel[] newArray(int size) {
+            return new TechListParcel[size];
+        }
+    };
+}
diff --git a/nfc/java/android/nfc/TransceiveResult.aidl b/nfc/java/android/nfc/TransceiveResult.aidl
new file mode 100644
index 0000000..98f92ee
--- /dev/null
+++ b/nfc/java/android/nfc/TransceiveResult.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc;
+
+parcelable TransceiveResult;
diff --git a/nfc/java/android/nfc/TransceiveResult.java b/nfc/java/android/nfc/TransceiveResult.java
new file mode 100644
index 0000000..7992094
--- /dev/null
+++ b/nfc/java/android/nfc/TransceiveResult.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2011, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.io.IOException;
+
+/**
+ * Class used to pipe transceive result from the NFC service.
+ *
+ * @hide
+ */
+public final class TransceiveResult implements Parcelable {
+    public static final int RESULT_SUCCESS = 0;
+    public static final int RESULT_FAILURE = 1;
+    public static final int RESULT_TAGLOST = 2;
+    public static final int RESULT_EXCEEDED_LENGTH = 3;
+
+    final int mResult;
+    final byte[] mResponseData;
+
+    public TransceiveResult(final int result, final byte[] data) {
+        mResult = result;
+        mResponseData = data;
+    }
+
+    public byte[] getResponseOrThrow() throws IOException {
+        switch (mResult) {
+            case RESULT_SUCCESS:
+                return mResponseData;
+            case RESULT_TAGLOST:
+                throw new TagLostException("Tag was lost.");
+            case RESULT_EXCEEDED_LENGTH:
+                throw new IOException("Transceive length exceeds supported maximum");
+            default:
+                throw new IOException("Transceive failed");
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mResult);
+        if (mResult == RESULT_SUCCESS) {
+            dest.writeInt(mResponseData.length);
+            dest.writeByteArray(mResponseData);
+        }
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<TransceiveResult> CREATOR =
+            new Parcelable.Creator<TransceiveResult>() {
+        @Override
+        public TransceiveResult createFromParcel(Parcel in) {
+            int result = in.readInt();
+            byte[] responseData;
+
+            if (result == RESULT_SUCCESS) {
+                int responseLength = in.readInt();
+                responseData = new byte[responseLength];
+                in.readByteArray(responseData);
+            } else {
+                responseData = null;
+            }
+            return new TransceiveResult(result, responseData);
+        }
+
+        @Override
+        public TransceiveResult[] newArray(int size) {
+            return new TransceiveResult[size];
+        }
+    };
+
+}
diff --git a/nfc/java/android/nfc/WlcLDeviceInfo.aidl b/nfc/java/android/nfc/WlcLDeviceInfo.aidl
new file mode 100644
index 0000000..33143fe
--- /dev/null
+++ b/nfc/java/android/nfc/WlcLDeviceInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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 android.nfc;
+
+parcelable WlcLDeviceInfo;
diff --git a/nfc/java/android/nfc/WlcLDeviceInfo.java b/nfc/java/android/nfc/WlcLDeviceInfo.java
new file mode 100644
index 0000000..016431e
--- /dev/null
+++ b/nfc/java/android/nfc/WlcLDeviceInfo.java
@@ -0,0 +1,107 @@
+/*
+ * 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 android.nfc;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Contains information of the nfc wireless charging listener device information.
+ */
+@FlaggedApi(Flags.FLAG_ENABLE_NFC_CHARGING)
+public final class WlcLDeviceInfo implements Parcelable {
+    public static final int DISCONNECTED = 1;
+
+    public static final int CONNECTED_CHARGING = 2;
+
+    public static final int CONNECTED_DISCHARGING = 3;
+
+    private double mProductId;
+    private double mTemperature;
+    private double mBatteryLevel;
+    private int mState;
+
+    public WlcLDeviceInfo(double productId, double temperature, double batteryLevel, int state) {
+        this.mProductId = productId;
+        this.mTemperature = temperature;
+        this.mBatteryLevel = batteryLevel;
+        this.mState = state;
+    }
+
+    /**
+     * ProductId of the WLC listener device.
+     */
+    public double getProductId() {
+        return mProductId;
+    }
+
+    /**
+     * Temperature of the WLC listener device.
+     */
+    public double getTemperature() {
+        return mTemperature;
+    }
+
+    /**
+     * BatteryLevel of the WLC listener device.
+     */
+    public double getBatteryLevel() {
+        return mBatteryLevel;
+    }
+
+    /**
+     * State of the WLC listener device.
+     */
+    public int getState() {
+        return mState;
+    }
+
+    private WlcLDeviceInfo(Parcel in) {
+        this.mProductId = in.readDouble();
+        this.mTemperature = in.readDouble();
+        this.mBatteryLevel = in.readDouble();
+        this.mState = in.readInt();
+    }
+
+    public static final @NonNull Parcelable.Creator<WlcLDeviceInfo> CREATOR =
+            new Parcelable.Creator<WlcLDeviceInfo>() {
+                @Override
+                public WlcLDeviceInfo createFromParcel(Parcel in) {
+                    return new WlcLDeviceInfo(in);
+                }
+
+                @Override
+                public WlcLDeviceInfo[] newArray(int size) {
+                    return new WlcLDeviceInfo[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeDouble(mProductId);
+        dest.writeDouble(mTemperature);
+        dest.writeDouble(mBatteryLevel);
+        dest.writeDouble(mState);
+    }
+}
diff --git a/nfc/java/android/nfc/cardemulation/AidGroup.aidl b/nfc/java/android/nfc/cardemulation/AidGroup.aidl
new file mode 100644
index 0000000..56d6fa5
--- /dev/null
+++ b/nfc/java/android/nfc/cardemulation/AidGroup.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc.cardemulation;
+
+parcelable AidGroup;
diff --git a/nfc/java/android/nfc/cardemulation/AidGroup.java b/nfc/java/android/nfc/cardemulation/AidGroup.java
new file mode 100644
index 0000000..ae3e333
--- /dev/null
+++ b/nfc/java/android/nfc/cardemulation/AidGroup.java
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc.cardemulation;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.nfc.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+import android.util.proto.ProtoOutputStream;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.regex.Pattern;
+
+/**********************************************************************
+ * This file is not a part of the NFC mainline module                 *
+ * *******************************************************************/
+
+/**
+ * The AidGroup class represents a group of Application Identifiers (AIDs).
+ *
+ * <p>The format of AIDs is defined in the ISO/IEC 7816-4 specification. This class
+ * requires the AIDs to be input as a hexadecimal string, with an even amount of
+ * hexadecimal characters, e.g. "F014811481".
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+public final class AidGroup implements Parcelable {
+    /**
+     * The maximum number of AIDs that can be present in any one group.
+     */
+    private static final int MAX_NUM_AIDS = 256;
+
+    private static final String TAG = "AidGroup";
+
+
+    private final List<String> mAids;
+    private final String mCategory;
+    @SuppressWarnings("unused") // Unused as of now, but part of the XML input.
+    private final String mDescription;
+
+    /**
+     * Creates a new AidGroup object.
+     *
+     * @param aids list of AIDs present in the group
+     * @param category category of this group, e.g. {@link CardEmulation#CATEGORY_PAYMENT}
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    public AidGroup(@NonNull List<String> aids, @Nullable String category) {
+        if (aids == null || aids.size() == 0) {
+            throw new IllegalArgumentException("No AIDS in AID group.");
+        }
+        if (aids.size() > MAX_NUM_AIDS) {
+            throw new IllegalArgumentException("Too many AIDs in AID group.");
+        }
+        for (String aid : aids) {
+            if (!isValidAid(aid)) {
+                throw new IllegalArgumentException("AID " + aid + " is not a valid AID.");
+            }
+        }
+        if (isValidCategory(category)) {
+            this.mCategory = category;
+        } else {
+            this.mCategory = CardEmulation.CATEGORY_OTHER;
+        }
+        this.mAids = new ArrayList<String>(aids.size());
+        for (String aid : aids) {
+            this.mAids.add(aid.toUpperCase(Locale.US));
+        }
+        this.mDescription = null;
+    }
+
+    /**
+     * Creates a new AidGroup object.
+     *
+     * @param category category of this group, e.g. {@link CardEmulation#CATEGORY_PAYMENT}
+     * @param description description of this group
+     */
+    AidGroup(@NonNull String category, @NonNull String description) {
+        this.mAids = new ArrayList<String>();
+        this.mCategory = category;
+        this.mDescription = description;
+    }
+
+    /**
+     * Returns the category of this group.
+     * @return the category of this AID group
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    @NonNull
+    public String getCategory() {
+        return mCategory;
+    }
+
+    /**
+     * Returns the list of AIDs in this group.
+     *
+     * @return the list of AIDs in this group
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    @NonNull
+    public List<String> getAids() {
+        return mAids;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder out = new StringBuilder("Category: " + mCategory
+                + ", AIDs:");
+        for (String aid : mAids) {
+            out.append(aid);
+            out.append(", ");
+        }
+        return out.toString();
+    }
+
+    /**
+     * Dump debugging info as AidGroupProto.
+     *
+     * If the output belongs to a sub message, the caller is responsible for wrapping this function
+     * between {@link ProtoOutputStream#start(long)} and {@link ProtoOutputStream#end(long)}.
+     *
+     * @param proto the ProtoOutputStream to write to
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    public void dump(@NonNull ProtoOutputStream proto) {
+        proto.write(AidGroupProto.CATEGORY, mCategory);
+        for (String aid : mAids) {
+            proto.write(AidGroupProto.AIDS, aid);
+        }
+    }
+
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeString8(mCategory);
+        dest.writeInt(mAids.size());
+        if (mAids.size() > 0) {
+            dest.writeStringList(mAids);
+        }
+    }
+
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    public static final @NonNull Parcelable.Creator<AidGroup> CREATOR =
+            new Parcelable.Creator<AidGroup>() {
+
+        @Override
+        public AidGroup createFromParcel(Parcel source) {
+            String category = source.readString8();
+            int listSize = source.readInt();
+            ArrayList<String> aidList = new ArrayList<String>();
+            if (listSize > 0) {
+                source.readStringList(aidList);
+            }
+            return new AidGroup(aidList, category);
+        }
+
+        @Override
+        public AidGroup[] newArray(int size) {
+            return new AidGroup[size];
+        }
+    };
+
+    /**
+     * Create an instance of AID group from XML file.
+     *
+     * @param parser input xml parser stream
+     * @throws XmlPullParserException If an error occurs parsing the element.
+     * @throws IOException If an error occurs reading the element.
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    @Nullable
+    public static AidGroup createFromXml(@NonNull XmlPullParser parser)
+            throws XmlPullParserException, IOException {
+        String category = null;
+        ArrayList<String> aids = new ArrayList<String>();
+        AidGroup group = null;
+        boolean inGroup = false;
+
+        int eventType = parser.getEventType();
+        int minDepth = parser.getDepth();
+        while (eventType != XmlPullParser.END_DOCUMENT && parser.getDepth() >= minDepth) {
+            String tagName = parser.getName();
+            if (eventType == XmlPullParser.START_TAG) {
+                if (tagName.equals("aid")) {
+                    if (inGroup) {
+                        String aid = parser.getAttributeValue(null, "value");
+                        if (aid != null) {
+                            aids.add(aid.toUpperCase());
+                        }
+                    } else {
+                        Log.d(TAG, "Ignoring <aid> tag while not in group");
+                    }
+                } else if (tagName.equals("aid-group")) {
+                    category = parser.getAttributeValue(null, "category");
+                    if (category == null) {
+                        Log.e(TAG, "<aid-group> tag without valid category");
+                        return null;
+                    }
+                    inGroup = true;
+                } else {
+                    Log.d(TAG, "Ignoring unexpected tag: " + tagName);
+                }
+            } else if (eventType == XmlPullParser.END_TAG) {
+                if (tagName.equals("aid-group") && inGroup && aids.size() > 0) {
+                    group = new AidGroup(aids, category);
+                    break;
+                }
+            }
+            eventType = parser.next();
+        }
+        return group;
+    }
+
+    /**
+     * Serialize instance of AID group to XML file.
+     * @param out XML serializer stream
+     * @throws IOException If an error occurs reading the element.
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    public void writeAsXml(@NonNull XmlSerializer out) throws IOException {
+        out.startTag(null, "aid-group");
+        out.attribute(null, "category", mCategory);
+        for (String aid : mAids) {
+            out.startTag(null, "aid");
+            out.attribute(null, "value", aid);
+            out.endTag(null, "aid");
+        }
+        out.endTag(null, "aid-group");
+    }
+
+    private static boolean isValidCategory(String category) {
+        return CardEmulation.CATEGORY_PAYMENT.equals(category) ||
+                CardEmulation.CATEGORY_OTHER.equals(category);
+    }
+
+    private static final Pattern AID_PATTERN = Pattern.compile("[0-9A-Fa-f]{10,32}\\*?\\#?");
+    /**
+     * Copied over from {@link CardEmulation#isValidAid(String)}
+     * @hide
+     */
+    private static boolean isValidAid(String aid) {
+        if (aid == null)
+            return false;
+
+        // If a prefix/subset AID, the total length must be odd (even # of AID chars + '*')
+        if ((aid.endsWith("*") || aid.endsWith("#")) && ((aid.length() % 2) == 0)) {
+            Log.e(TAG, "AID " + aid + " is not a valid AID.");
+            return false;
+        }
+
+        // If not a prefix/subset AID, the total length must be even (even # of AID chars)
+        if ((!(aid.endsWith("*") || aid.endsWith("#"))) && ((aid.length() % 2) != 0)) {
+            Log.e(TAG, "AID " + aid + " is not a valid AID.");
+            return false;
+        }
+
+        // Verify hex characters
+        if (!AID_PATTERN.matcher(aid).matches()) {
+            Log.e(TAG, "AID " + aid + " is not a valid AID.");
+            return false;
+        }
+
+        return true;
+    }
+}
diff --git a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.aidl b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.aidl
new file mode 100644
index 0000000..a62fdd6
--- /dev/null
+++ b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc.cardemulation;
+
+parcelable ApduServiceInfo;
diff --git a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
new file mode 100644
index 0000000..41dee3a
--- /dev/null
+++ b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
@@ -0,0 +1,912 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**********************************************************************
+ * This file is not a part of the NFC mainline module                 *
+ * *******************************************************************/
+
+package android.nfc.cardemulation;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ComponentName;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
+import android.content.res.Resources.NotFoundException;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.graphics.drawable.Drawable;
+import android.nfc.Flags;
+import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Xml;
+import android.util.proto.ProtoOutputStream;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+/**
+ * Class holding APDU service info.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+public final class ApduServiceInfo implements Parcelable {
+    private static final String TAG = "ApduServiceInfo";
+
+    /**
+     * The service that implements this
+     */
+    private final ResolveInfo mService;
+
+    /**
+     * Description of the service
+     */
+    private final String mDescription;
+
+    /**
+     * Whether this service represents AIDs running on the host CPU
+     */
+    private final boolean mOnHost;
+
+    /**
+     * Offhost reader name.
+     * eg: SIM, eSE etc
+     */
+    private String mOffHostName;
+
+    /**
+     * Offhost reader name from manifest file.
+     * Used for resetOffHostSecureElement()
+     */
+    private final String mStaticOffHostName;
+
+    /**
+     * Mapping from category to static AID group
+     */
+    private final HashMap<String, AidGroup> mStaticAidGroups;
+
+    /**
+     * Mapping from category to dynamic AID group
+     */
+    private final HashMap<String, AidGroup> mDynamicAidGroups;
+
+    /**
+     * Whether this service should only be started when the device is unlocked.
+     */
+    private final boolean mRequiresDeviceUnlock;
+
+    /**
+     * Whether this service should only be started when the device is screen on.
+     */
+    private final boolean mRequiresDeviceScreenOn;
+
+    /**
+     * The id of the service banner specified in XML.
+     */
+    private final int mBannerResourceId;
+
+    /**
+     * The uid of the package the service belongs to
+     */
+    private final int mUid;
+
+    /**
+     * Settings Activity for this service
+     */
+    private final String mSettingsActivityName;
+
+    /**
+     * State of the service for CATEGORY_OTHER selection
+     */
+    private boolean mCategoryOtherServiceEnabled;
+
+    /**
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public ApduServiceInfo(ResolveInfo info, boolean onHost, String description,
+            ArrayList<AidGroup> staticAidGroups, ArrayList<AidGroup> dynamicAidGroups,
+            boolean requiresUnlock, int bannerResource, int uid,
+            String settingsActivityName, String offHost, String staticOffHost) {
+        this(info, onHost, description, staticAidGroups, dynamicAidGroups,
+                requiresUnlock, bannerResource, uid, settingsActivityName,
+                offHost, staticOffHost, false);
+    }
+
+    /**
+     * @hide
+     */
+    public ApduServiceInfo(ResolveInfo info, boolean onHost, String description,
+            ArrayList<AidGroup> staticAidGroups, ArrayList<AidGroup> dynamicAidGroups,
+            boolean requiresUnlock, int bannerResource, int uid,
+            String settingsActivityName, String offHost, String staticOffHost,
+            boolean isEnabled) {
+        this(info, onHost, description, staticAidGroups, dynamicAidGroups,
+                requiresUnlock, onHost ? true : false, bannerResource, uid,
+                settingsActivityName, offHost, staticOffHost, isEnabled);
+    }
+
+    /**
+     * @hide
+     */
+    public ApduServiceInfo(ResolveInfo info, boolean onHost, String description,
+            List<AidGroup> staticAidGroups, List<AidGroup> dynamicAidGroups,
+            boolean requiresUnlock, boolean requiresScreenOn, int bannerResource, int uid,
+            String settingsActivityName, String offHost, String staticOffHost, boolean isEnabled) {
+        this.mService = info;
+        this.mDescription = description;
+        this.mStaticAidGroups = new HashMap<String, AidGroup>();
+        this.mDynamicAidGroups = new HashMap<String, AidGroup>();
+        this.mOffHostName = offHost;
+        this.mStaticOffHostName = staticOffHost;
+        this.mOnHost = onHost;
+        this.mRequiresDeviceUnlock = requiresUnlock;
+        this.mRequiresDeviceScreenOn = requiresScreenOn;
+        for (AidGroup aidGroup : staticAidGroups) {
+            this.mStaticAidGroups.put(aidGroup.getCategory(), aidGroup);
+        }
+        for (AidGroup aidGroup : dynamicAidGroups) {
+            this.mDynamicAidGroups.put(aidGroup.getCategory(), aidGroup);
+        }
+        this.mBannerResourceId = bannerResource;
+        this.mUid = uid;
+        this.mSettingsActivityName = settingsActivityName;
+        this.mCategoryOtherServiceEnabled = isEnabled;
+
+    }
+
+    /**
+     * Creates a new ApduServiceInfo object.
+     *
+     * @param pm packageManager instance
+     * @param info app component info
+     * @param onHost whether service is on host or not (secure element)
+     * @throws XmlPullParserException If an error occurs parsing the element.
+     * @throws IOException If an error occurs reading the element.
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    public ApduServiceInfo(@NonNull PackageManager pm, @NonNull ResolveInfo info, boolean onHost)
+            throws XmlPullParserException, IOException {
+        ServiceInfo si = info.serviceInfo;
+        XmlResourceParser parser = null;
+        try {
+            if (onHost) {
+                parser = si.loadXmlMetaData(pm, HostApduService.SERVICE_META_DATA);
+                if (parser == null) {
+                    throw new XmlPullParserException("No " + HostApduService.SERVICE_META_DATA +
+                            " meta-data");
+                }
+            } else {
+                parser = si.loadXmlMetaData(pm, OffHostApduService.SERVICE_META_DATA);
+                if (parser == null) {
+                    throw new XmlPullParserException("No " + OffHostApduService.SERVICE_META_DATA +
+                            " meta-data");
+                }
+            }
+
+            int eventType = parser.getEventType();
+            while (eventType != XmlPullParser.START_TAG && eventType != XmlPullParser.END_DOCUMENT) {
+                eventType = parser.next();
+            }
+
+            String tagName = parser.getName();
+            if (onHost && !"host-apdu-service".equals(tagName)) {
+                throw new XmlPullParserException(
+                        "Meta-data does not start with <host-apdu-service> tag");
+            } else if (!onHost && !"offhost-apdu-service".equals(tagName)) {
+                throw new XmlPullParserException(
+                        "Meta-data does not start with <offhost-apdu-service> tag");
+            }
+
+            Resources res = pm.getResourcesForApplication(si.applicationInfo);
+            AttributeSet attrs = Xml.asAttributeSet(parser);
+            if (onHost) {
+                TypedArray sa = res.obtainAttributes(attrs,
+                        com.android.internal.R.styleable.HostApduService);
+                mService = info;
+                mDescription = sa.getString(
+                        com.android.internal.R.styleable.HostApduService_description);
+                mRequiresDeviceUnlock = sa.getBoolean(
+                        com.android.internal.R.styleable.HostApduService_requireDeviceUnlock,
+                        false);
+                mRequiresDeviceScreenOn = sa.getBoolean(
+                        com.android.internal.R.styleable.HostApduService_requireDeviceScreenOn,
+                        true);
+                mBannerResourceId = sa.getResourceId(
+                        com.android.internal.R.styleable.HostApduService_apduServiceBanner, -1);
+                mSettingsActivityName = sa.getString(
+                        com.android.internal.R.styleable.HostApduService_settingsActivity);
+                mOffHostName = null;
+                mStaticOffHostName = mOffHostName;
+                sa.recycle();
+            } else {
+                TypedArray sa = res.obtainAttributes(attrs,
+                        com.android.internal.R.styleable.OffHostApduService);
+                mService = info;
+                mDescription = sa.getString(
+                        com.android.internal.R.styleable.OffHostApduService_description);
+                mRequiresDeviceUnlock = sa.getBoolean(
+                        com.android.internal.R.styleable.OffHostApduService_requireDeviceUnlock,
+                        false);
+                mRequiresDeviceScreenOn = sa.getBoolean(
+                        com.android.internal.R.styleable.OffHostApduService_requireDeviceScreenOn,
+                        false);
+                mBannerResourceId = sa.getResourceId(
+                        com.android.internal.R.styleable.OffHostApduService_apduServiceBanner, -1);
+                mSettingsActivityName = sa.getString(
+                        com.android.internal.R.styleable.HostApduService_settingsActivity);
+                mOffHostName = sa.getString(
+                        com.android.internal.R.styleable.OffHostApduService_secureElementName);
+                if (mOffHostName != null) {
+                    if (mOffHostName.equals("eSE")) {
+                        mOffHostName = "eSE1";
+                    } else if (mOffHostName.equals("SIM")) {
+                        mOffHostName = "SIM1";
+                    }
+                }
+                mStaticOffHostName = mOffHostName;
+                sa.recycle();
+            }
+
+            mStaticAidGroups = new HashMap<String, AidGroup>();
+            mDynamicAidGroups = new HashMap<String, AidGroup>();
+            mOnHost = onHost;
+
+            final int depth = parser.getDepth();
+            AidGroup currentGroup = null;
+
+            // Parsed values for the current AID group
+            while (((eventType = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
+                    && eventType != XmlPullParser.END_DOCUMENT) {
+                tagName = parser.getName();
+                if (eventType == XmlPullParser.START_TAG && "aid-group".equals(tagName) &&
+                        currentGroup == null) {
+                    final TypedArray groupAttrs = res.obtainAttributes(attrs,
+                            com.android.internal.R.styleable.AidGroup);
+                    // Get category of AID group
+                    String groupCategory = groupAttrs.getString(
+                            com.android.internal.R.styleable.AidGroup_category);
+                    String groupDescription = groupAttrs.getString(
+                            com.android.internal.R.styleable.AidGroup_description);
+                    if (!CardEmulation.CATEGORY_PAYMENT.equals(groupCategory)) {
+                        groupCategory = CardEmulation.CATEGORY_OTHER;
+                    }
+                    currentGroup = mStaticAidGroups.get(groupCategory);
+                    if (currentGroup != null) {
+                        if (!CardEmulation.CATEGORY_OTHER.equals(groupCategory)) {
+                            Log.e(TAG, "Not allowing multiple aid-groups in the " +
+                                    groupCategory + " category");
+                            currentGroup = null;
+                        }
+                    } else {
+                        currentGroup = new AidGroup(groupCategory, groupDescription);
+                    }
+                    groupAttrs.recycle();
+                } else if (eventType == XmlPullParser.END_TAG && "aid-group".equals(tagName) &&
+                        currentGroup != null) {
+                    if (currentGroup.getAids().size() > 0) {
+                        if (!mStaticAidGroups.containsKey(currentGroup.getCategory())) {
+                            mStaticAidGroups.put(currentGroup.getCategory(), currentGroup);
+                        }
+                    } else {
+                        Log.e(TAG, "Not adding <aid-group> with empty or invalid AIDs");
+                    }
+                    currentGroup = null;
+                } else if (eventType == XmlPullParser.START_TAG && "aid-filter".equals(tagName) &&
+                        currentGroup != null) {
+                    final TypedArray a = res.obtainAttributes(attrs,
+                            com.android.internal.R.styleable.AidFilter);
+                    String aid = a.getString(com.android.internal.R.styleable.AidFilter_name).
+                            toUpperCase();
+                    if (isValidAid(aid) && !currentGroup.getAids().contains(aid)) {
+                        currentGroup.getAids().add(aid);
+                    } else {
+                        Log.e(TAG, "Ignoring invalid or duplicate aid: " + aid);
+                    }
+                    a.recycle();
+                } else if (eventType == XmlPullParser.START_TAG &&
+                        "aid-prefix-filter".equals(tagName) && currentGroup != null) {
+                    final TypedArray a = res.obtainAttributes(attrs,
+                            com.android.internal.R.styleable.AidFilter);
+                    String aid = a.getString(com.android.internal.R.styleable.AidFilter_name).
+                            toUpperCase();
+                    // Add wildcard char to indicate prefix
+                    aid = aid.concat("*");
+                    if (isValidAid(aid) && !currentGroup.getAids().contains(aid)) {
+                        currentGroup.getAids().add(aid);
+                    } else {
+                        Log.e(TAG, "Ignoring invalid or duplicate aid: " + aid);
+                    }
+                    a.recycle();
+                } else if (eventType == XmlPullParser.START_TAG &&
+                        tagName.equals("aid-suffix-filter") && currentGroup != null) {
+                    final TypedArray a = res.obtainAttributes(attrs,
+                            com.android.internal.R.styleable.AidFilter);
+                    String aid = a.getString(com.android.internal.R.styleable.AidFilter_name).
+                            toUpperCase();
+                    // Add wildcard char to indicate suffix
+                    aid = aid.concat("#");
+                    if (isValidAid(aid) && !currentGroup.getAids().contains(aid)) {
+                        currentGroup.getAids().add(aid);
+                    } else {
+                        Log.e(TAG, "Ignoring invalid or duplicate aid: " + aid);
+                    }
+                    a.recycle();
+                }
+            }
+        } catch (NameNotFoundException e) {
+            throw new XmlPullParserException("Unable to create context for: " + si.packageName);
+        } finally {
+            if (parser != null) parser.close();
+        }
+        // Set uid
+        mUid = si.applicationInfo.uid;
+
+        mCategoryOtherServiceEnabled = true;    // support other category
+
+    }
+
+    /**
+     * Returns the app component corresponding to this APDU service.
+     *
+     * @return app component for this service
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    @NonNull
+    public ComponentName getComponent() {
+        return new ComponentName(mService.serviceInfo.packageName,
+                mService.serviceInfo.name);
+    }
+
+    /**
+     * Returns the offhost secure element name (if the service is offhost).
+     *
+     * @return offhost secure element name for offhost services
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    @Nullable
+    public String getOffHostSecureElement() {
+        return mOffHostName;
+    }
+
+    /**
+     * Returns a consolidated list of AIDs from the AID groups
+     * registered by this service. Note that if a service has both
+     * a static (manifest-based) AID group for a category and a dynamic
+     * AID group, only the dynamically registered AIDs will be returned
+     * for that category.
+     * @return List of AIDs registered by the service
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    @NonNull
+    public List<String> getAids() {
+        final ArrayList<String> aids = new ArrayList<String>();
+        for (AidGroup group : getAidGroups()) {
+            aids.addAll(group.getAids());
+        }
+        return aids;
+    }
+
+    /**
+     * Returns a consolidated list of AIDs with prefixes from the AID groups
+     * registered by this service. Note that if a service has both
+     * a static (manifest-based) AID group for a category and a dynamic
+     * AID group, only the dynamically registered AIDs will be returned
+     * for that category.
+     * @return List of prefix AIDs registered by the service
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    @NonNull
+    public List<String> getPrefixAids() {
+        final ArrayList<String> prefixAids = new ArrayList<String>();
+        for (AidGroup group : getAidGroups()) {
+            for (String aid : group.getAids()) {
+                if (aid.endsWith("*")) {
+                    prefixAids.add(aid);
+                }
+            }
+        }
+        return prefixAids;
+    }
+
+    /**
+     * Returns a consolidated list of AIDs with subsets from the AID groups
+     * registered by this service. Note that if a service has both
+     * a static (manifest-based) AID group for a category and a dynamic
+     * AID group, only the dynamically registered AIDs will be returned
+     * for that category.
+     * @return List of prefix AIDs registered by the service
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    @NonNull
+    public List<String> getSubsetAids() {
+        final ArrayList<String> subsetAids = new ArrayList<String>();
+        for (AidGroup group : getAidGroups()) {
+            for (String aid : group.getAids()) {
+                if (aid.endsWith("#")) {
+                    subsetAids.add(aid);
+                }
+            }
+        }
+        return subsetAids;
+    }
+
+    /**
+     * Returns the registered AID group for this category.
+     *
+     * @param category category name
+     * @return {@link AidGroup} instance for the provided category
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    @NonNull
+    public AidGroup getDynamicAidGroupForCategory(@NonNull String category) {
+        return mDynamicAidGroups.get(category);
+    }
+
+    /**
+     * Removes the registered AID group for this category.
+     *
+     * @param category category name
+     * @return {@code true} if an AID group existed
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    @NonNull
+    public boolean removeDynamicAidGroupForCategory(@NonNull String category) {
+        return (mDynamicAidGroups.remove(category) != null);
+    }
+
+    /**
+     * Returns a consolidated list of AID groups
+     * registered by this service. Note that if a service has both
+     * a static (manifest-based) AID group for a category and a dynamic
+     * AID group, only the dynamically registered AID group will be returned
+     * for that category.
+     * @return List of AIDs registered by the service
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    @NonNull
+    public List<AidGroup> getAidGroups() {
+        final ArrayList<AidGroup> groups = new ArrayList<AidGroup>();
+        for (Map.Entry<String, AidGroup> entry : mDynamicAidGroups.entrySet()) {
+            groups.add(entry.getValue());
+        }
+        for (Map.Entry<String, AidGroup> entry : mStaticAidGroups.entrySet()) {
+            if (!mDynamicAidGroups.containsKey(entry.getKey())) {
+                // Consolidate AID groups - don't return static ones
+                // if a dynamic group exists for the category.
+                groups.add(entry.getValue());
+            }
+        }
+        return groups;
+    }
+
+    /**
+     * Returns the category to which this service has attributed the AID that is passed in,
+     * or null if we don't know this AID.
+     * @param aid AID to lookup for
+     * @return category name corresponding to this AID
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    @NonNull
+    public String getCategoryForAid(@NonNull String aid) {
+        List<AidGroup> groups = getAidGroups();
+        for (AidGroup group : groups) {
+            if (group.getAids().contains(aid.toUpperCase())) {
+                return group.getCategory();
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns whether there is any AID group for this category.
+     * @param category category name
+     * @return {@code true} if an AID group exists
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    public boolean hasCategory(@NonNull String category) {
+        return (mStaticAidGroups.containsKey(category) || mDynamicAidGroups.containsKey(category));
+    }
+
+    /**
+     * Returns whether the service is on host or not.
+     * @return true if the service is on host (not secure element)
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    public boolean isOnHost() {
+        return mOnHost;
+    }
+
+    /**
+     * Returns whether the service requires device unlock.
+     * @return whether the service requires device unlock
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    public boolean requiresUnlock() {
+        return mRequiresDeviceUnlock;
+    }
+
+    /**
+     * Returns whether this service should only be started when the device is screen on.
+     * @return whether the service requires screen on
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    public boolean requiresScreenOn() {
+        return mRequiresDeviceScreenOn;
+    }
+
+    /**
+     * Returns description of service.
+     * @return user readable description of service
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    @NonNull
+    public String getDescription() {
+        return mDescription;
+    }
+
+    /**
+     * Returns uid of service.
+     * @return uid of the service
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    public int getUid() {
+        return mUid;
+    }
+
+    /**
+     * Add or replace an AID group to this service.
+     * @param aidGroup instance of aid group to set or replace
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    public void setDynamicAidGroup(@NonNull AidGroup aidGroup) {
+        mDynamicAidGroups.put(aidGroup.getCategory(), aidGroup);
+    }
+
+    /**
+     * Sets the off host Secure Element.
+     * @param  offHost  Secure Element to set. Only accept strings with prefix SIM or prefix eSE.
+     *                  Ref: GSMA TS.26 - NFC Handset Requirements
+     *                  TS26_NFC_REQ_069: For UICC, Secure Element Name SHALL be SIM[smartcard slot]
+     *                                    (e.g. SIM/SIM1, SIM2… SIMn).
+     *                  TS26_NFC_REQ_070: For embedded SE, Secure Element Name SHALL be eSE[number]
+     *                                    (e.g. eSE/eSE1, eSE2, etc.).
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    public void setOffHostSecureElement(@NonNull String offHost) {
+        mOffHostName = offHost;
+    }
+
+    /**
+     * Resets the off host Secure Element to statically defined
+     * by the service in the manifest file.
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    public void resetOffHostSecureElement() {
+        mOffHostName = mStaticOffHostName;
+    }
+
+    /**
+     * Load label for this service.
+     * @param pm packagemanager instance
+     * @return label name corresponding to service
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    @NonNull
+    public CharSequence loadLabel(@NonNull PackageManager pm) {
+        return mService.loadLabel(pm);
+    }
+
+    /**
+     * Load application label for this service.
+     * @param pm packagemanager instance
+     * @return app label name corresponding to service
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    @NonNull
+    public CharSequence loadAppLabel(@NonNull PackageManager pm) {
+        try {
+            return pm.getApplicationLabel(pm.getApplicationInfo(
+                    mService.resolvePackageName, PackageManager.GET_META_DATA));
+        } catch (PackageManager.NameNotFoundException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Load application icon for this service.
+     * @param pm packagemanager instance
+     * @return app icon corresponding to service
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    @NonNull
+    public Drawable loadIcon(@NonNull PackageManager pm) {
+        return mService.loadIcon(pm);
+    }
+
+    /**
+     * Load application banner for this service.
+     * @param pm packagemanager instance
+     * @return app banner corresponding to service
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    @NonNull
+    public Drawable loadBanner(@NonNull PackageManager pm) {
+        Resources res;
+        try {
+            res = pm.getResourcesForApplication(mService.serviceInfo.packageName);
+            Drawable banner = res.getDrawable(mBannerResourceId);
+            return banner;
+        } catch (NotFoundException e) {
+            Log.e(TAG, "Could not load banner.");
+            return null;
+        } catch (NameNotFoundException e) {
+            Log.e(TAG, "Could not load banner.");
+            return null;
+        }
+    }
+
+    /**
+     * Load activity name for this service.
+     * @return activity name for this service
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    @NonNull
+    public String getSettingsActivityName() { return mSettingsActivityName; }
+
+    @Override
+    public String toString() {
+        StringBuilder out = new StringBuilder("ApduService: ");
+        out.append(getComponent());
+        out.append(", UID: " + mUid);
+        out.append(", description: " + mDescription);
+        out.append(", Static AID Groups: ");
+        for (AidGroup aidGroup : mStaticAidGroups.values()) {
+            out.append(aidGroup.toString());
+        }
+        out.append(", Dynamic AID Groups: ");
+        for (AidGroup aidGroup : mDynamicAidGroups.values()) {
+            out.append(aidGroup.toString());
+        }
+        return out.toString();
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (this == o) return true;
+        if (!(o instanceof ApduServiceInfo)) return false;
+        ApduServiceInfo thatService = (ApduServiceInfo) o;
+
+        return thatService.getComponent().equals(this.getComponent())
+                && thatService.getUid() == this.getUid();
+    }
+
+    @Override
+    public int hashCode() {
+        return getComponent().hashCode();
+    }
+
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        mService.writeToParcel(dest, flags);
+        dest.writeString(mDescription);
+        dest.writeInt(mOnHost ? 1 : 0);
+        dest.writeString(mOffHostName);
+        dest.writeString(mStaticOffHostName);
+        dest.writeInt(mStaticAidGroups.size());
+        if (mStaticAidGroups.size() > 0) {
+            dest.writeTypedList(new ArrayList<AidGroup>(mStaticAidGroups.values()));
+        }
+        dest.writeInt(mDynamicAidGroups.size());
+        if (mDynamicAidGroups.size() > 0) {
+            dest.writeTypedList(new ArrayList<AidGroup>(mDynamicAidGroups.values()));
+        }
+        dest.writeInt(mRequiresDeviceUnlock ? 1 : 0);
+        dest.writeInt(mRequiresDeviceScreenOn ? 1 : 0);
+        dest.writeInt(mBannerResourceId);
+        dest.writeInt(mUid);
+        dest.writeString(mSettingsActivityName);
+
+        dest.writeInt(mCategoryOtherServiceEnabled ? 1 : 0);
+    };
+
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    public static final @NonNull Parcelable.Creator<ApduServiceInfo> CREATOR =
+            new Parcelable.Creator<ApduServiceInfo>() {
+                @Override
+                public ApduServiceInfo createFromParcel(Parcel source) {
+                    ResolveInfo info = ResolveInfo.CREATOR.createFromParcel(source);
+                    String description = source.readString();
+                    boolean onHost = source.readInt() != 0;
+                    String offHostName = source.readString();
+                    String staticOffHostName = source.readString();
+                    ArrayList<AidGroup> staticAidGroups = new ArrayList<AidGroup>();
+                    int numStaticGroups = source.readInt();
+                    if (numStaticGroups > 0) {
+                        source.readTypedList(staticAidGroups, AidGroup.CREATOR);
+                    }
+                    ArrayList<AidGroup> dynamicAidGroups = new ArrayList<AidGroup>();
+                    int numDynamicGroups = source.readInt();
+                    if (numDynamicGroups > 0) {
+                        source.readTypedList(dynamicAidGroups, AidGroup.CREATOR);
+                    }
+                    boolean requiresUnlock = source.readInt() != 0;
+                    boolean requiresScreenOn = source.readInt() != 0;
+                    int bannerResource = source.readInt();
+                    int uid = source.readInt();
+                    String settingsActivityName = source.readString();
+                    boolean isEnabled = source.readInt() != 0;
+                    return new ApduServiceInfo(info, onHost, description, staticAidGroups,
+                            dynamicAidGroups, requiresUnlock, requiresScreenOn, bannerResource, uid,
+                            settingsActivityName, offHostName, staticOffHostName,
+                            isEnabled);
+                }
+
+                @Override
+                public ApduServiceInfo[] newArray(int size) {
+                    return new ApduServiceInfo[size];
+                }
+            };
+
+    /**
+     * Dump contents for debugging.
+     * @param fd parcelfiledescriptor instance
+     * @param pw printwriter instance
+     * @param args args for dumping
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    public void dump(@NonNull ParcelFileDescriptor fd, @NonNull PrintWriter pw,
+                     @NonNull String[] args) {
+        pw.println("    " + getComponent()
+                + " (Description: " + getDescription() + ")"
+                + " (UID: " + getUid() + ")");
+        if (mOnHost) {
+            pw.println("    On Host Service");
+        } else {
+            pw.println("    Off-host Service");
+            pw.println("        " + "Current off-host SE:" + mOffHostName
+                    + " static off-host SE:" + mStaticOffHostName);
+        }
+        pw.println("    Static AID groups:");
+        for (AidGroup group : mStaticAidGroups.values()) {
+            pw.println("        Category: " + group.getCategory()
+                    + "(enabled: " + mCategoryOtherServiceEnabled + ")");
+            for (String aid : group.getAids()) {
+                pw.println("            AID: " + aid);
+            }
+        }
+        pw.println("    Dynamic AID groups:");
+        for (AidGroup group : mDynamicAidGroups.values()) {
+            pw.println("        Category: " + group.getCategory()
+                    + "(enabled: " + mCategoryOtherServiceEnabled + ")");
+            for (String aid : group.getAids()) {
+                pw.println("            AID: " + aid);
+            }
+        }
+        pw.println("    Settings Activity: " + mSettingsActivityName);
+        pw.println("    Requires Device Unlock: " + mRequiresDeviceUnlock);
+        pw.println("    Requires Device ScreenOn: " + mRequiresDeviceScreenOn);
+    }
+
+
+    /**
+     * Enable or disable this CATEGORY_OTHER service.
+     *
+     * @param enabled true to indicate if user has enabled this service
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    public void setCategoryOtherServiceEnabled(boolean enabled) {
+        mCategoryOtherServiceEnabled = enabled;
+    }
+
+
+    /**
+     * Returns whether this CATEGORY_OTHER service is enabled or not.
+     *
+     * @return true to indicate if user has enabled this service
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    public boolean isCategoryOtherServiceEnabled() {
+        return mCategoryOtherServiceEnabled;
+    }
+
+    /**
+     * Dump debugging info as ApduServiceInfoProto.
+     *
+     * If the output belongs to a sub message, the caller is responsible for wrapping this function
+     * between {@link ProtoOutputStream#start(long)} and {@link ProtoOutputStream#end(long)}.
+     * See proto definition in frameworks/base/core/proto/android/nfc/apdu_service_info.proto
+     *
+     * @param proto the ProtoOutputStream to write to
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    public void dumpDebug(@NonNull ProtoOutputStream proto) {
+        getComponent().dumpDebug(proto, ApduServiceInfoProto.COMPONENT_NAME);
+        proto.write(ApduServiceInfoProto.DESCRIPTION, getDescription());
+        proto.write(ApduServiceInfoProto.ON_HOST, mOnHost);
+        if (!mOnHost) {
+            proto.write(ApduServiceInfoProto.OFF_HOST_NAME, mOffHostName);
+            proto.write(ApduServiceInfoProto.STATIC_OFF_HOST_NAME, mStaticOffHostName);
+        }
+        for (AidGroup group : mStaticAidGroups.values()) {
+            long token = proto.start(ApduServiceInfoProto.STATIC_AID_GROUPS);
+            group.dump(proto);
+            proto.end(token);
+        }
+        for (AidGroup group : mDynamicAidGroups.values()) {
+            long token = proto.start(ApduServiceInfoProto.STATIC_AID_GROUPS);
+            group.dump(proto);
+            proto.end(token);
+        }
+        proto.write(ApduServiceInfoProto.SETTINGS_ACTIVITY_NAME, mSettingsActivityName);
+    }
+
+    private static final Pattern AID_PATTERN = Pattern.compile("[0-9A-Fa-f]{10,32}\\*?\\#?");
+    /**
+     * Copied over from {@link CardEmulation#isValidAid(String)}
+     * @hide
+     */
+    private static boolean isValidAid(String aid) {
+        if (aid == null)
+            return false;
+
+        // If a prefix/subset AID, the total length must be odd (even # of AID chars + '*')
+        if ((aid.endsWith("*") || aid.endsWith("#")) && ((aid.length() % 2) == 0)) {
+            Log.e(TAG, "AID " + aid + " is not a valid AID.");
+            return false;
+        }
+
+        // If not a prefix/subset AID, the total length must be even (even # of AID chars)
+        if ((!(aid.endsWith("*") || aid.endsWith("#"))) && ((aid.length() % 2) != 0)) {
+            Log.e(TAG, "AID " + aid + " is not a valid AID.");
+            return false;
+        }
+
+        // Verify hex characters
+        if (!AID_PATTERN.matcher(aid).matches()) {
+            Log.e(TAG, "AID " + aid + " is not a valid AID.");
+            return false;
+        }
+
+        return true;
+    }
+}
diff --git a/nfc/java/android/nfc/cardemulation/CardEmulation.java b/nfc/java/android/nfc/cardemulation/CardEmulation.java
new file mode 100644
index 0000000..81eab71
--- /dev/null
+++ b/nfc/java/android/nfc/cardemulation/CardEmulation.java
@@ -0,0 +1,1120 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc.cardemulation;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemApi;
+import android.annotation.UserIdInt;
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.nfc.Constants;
+import android.nfc.Flags;
+import android.nfc.INfcCardEmulation;
+import android.nfc.NfcAdapter;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
+import android.util.Log;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.regex.Pattern;
+
+/**
+ * This class can be used to query the state of
+ * NFC card emulation services.
+ *
+ * For a general introduction into NFC card emulation,
+ * please read the <a href="{@docRoot}guide/topics/connectivity/nfc/hce.html">
+ * NFC card emulation developer guide</a>.</p>
+ *
+ * <p class="note">Use of this class requires the
+ * {@link PackageManager#FEATURE_NFC_HOST_CARD_EMULATION} to be present
+ * on the device.
+ */
+public final class CardEmulation {
+    private static final Pattern AID_PATTERN = Pattern.compile("[0-9A-Fa-f]{10,32}\\*?\\#?");
+    static final String TAG = "CardEmulation";
+
+    /**
+     * Activity action: ask the user to change the default
+     * card emulation service for a certain category. This will
+     * show a dialog that asks the user whether they want to
+     * replace the current default service with the service
+     * identified with the ComponentName specified in
+     * {@link #EXTRA_SERVICE_COMPONENT}, for the category
+     * specified in {@link #EXTRA_CATEGORY}. There is an optional
+     * extra field using {@link Intent#EXTRA_USER} to specify
+     * the {@link UserHandle} of the user that owns the app.
+     *
+     * @deprecated Please use {@link android.app.role.RoleManager#createRequestRoleIntent(String)}
+     * with {@link android.app.role.RoleManager#ROLE_WALLET} parameter
+     * and {@link Activity#startActivityForResult(Intent, int)} instead.
+     */
+    @Deprecated
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_CHANGE_DEFAULT =
+            "android.nfc.cardemulation.action.ACTION_CHANGE_DEFAULT";
+
+    /**
+     * The category extra for {@link #ACTION_CHANGE_DEFAULT}.
+     *
+     * @see #ACTION_CHANGE_DEFAULT
+     */
+    public static final String EXTRA_CATEGORY = "category";
+
+    /**
+     * The service {@link ComponentName} object passed in as an
+     * extra for {@link #ACTION_CHANGE_DEFAULT}.
+     *
+     * @see #ACTION_CHANGE_DEFAULT
+     */
+    public static final String EXTRA_SERVICE_COMPONENT = "component";
+
+    /**
+     * Category used for NFC payment services.
+     */
+    public static final String CATEGORY_PAYMENT = "payment";
+
+    /**
+     * Category that can be used for all other card emulation
+     * services.
+     */
+    public static final String CATEGORY_OTHER = "other";
+
+    /**
+     * Return value for {@link #getSelectionModeForCategory(String)}.
+     *
+     * <p>In this mode, the user has set a default service for this
+     *    category.
+     *
+     * <p>When using ISO-DEP card emulation with {@link HostApduService}
+     *    or {@link OffHostApduService}, if a remote NFC device selects
+     *    any of the Application IDs (AIDs)
+     *    that the default service has registered in this category,
+     *    that service will automatically be bound to to handle
+     *    the transaction.
+     */
+    public static final int SELECTION_MODE_PREFER_DEFAULT = 0;
+
+    /**
+     * Return value for {@link #getSelectionModeForCategory(String)}.
+     *
+     * <p>In this mode, when using ISO-DEP card emulation with {@link HostApduService}
+     *    or {@link OffHostApduService}, whenever an Application ID (AID) of this category
+     *    is selected, the user is asked which service they want to use to handle
+     *    the transaction, even if there is only one matching service.
+     */
+    public static final int SELECTION_MODE_ALWAYS_ASK = 1;
+
+    /**
+     * Return value for {@link #getSelectionModeForCategory(String)}.
+     *
+     * <p>In this mode, when using ISO-DEP card emulation with {@link HostApduService}
+     *    or {@link OffHostApduService}, the user will only be asked to select a service
+     *    if the Application ID (AID) selected by the reader has been registered by multiple
+     *    services. If there is only one service that has registered for the AID,
+     *    that service will be invoked directly.
+     */
+    public static final int SELECTION_MODE_ASK_IF_CONFLICT = 2;
+
+    static boolean sIsInitialized = false;
+    static HashMap<Context, CardEmulation> sCardEmus = new HashMap<Context, CardEmulation>();
+    static INfcCardEmulation sService;
+
+    final Context mContext;
+
+    private CardEmulation(Context context, INfcCardEmulation service) {
+        mContext = context.getApplicationContext();
+        sService = service;
+    }
+
+    /**
+     * Helper to get an instance of this class.
+     *
+     * @param adapter A reference to an NfcAdapter object.
+     * @return
+     */
+    public static synchronized CardEmulation getInstance(NfcAdapter adapter) {
+        if (adapter == null) throw new NullPointerException("NfcAdapter is null");
+        Context context = adapter.getContext();
+        if (context == null) {
+            Log.e(TAG, "NfcAdapter context is null.");
+            throw new UnsupportedOperationException();
+        }
+        if (!sIsInitialized) {
+            PackageManager pm = context.getPackageManager();
+            if (pm == null) {
+                Log.e(TAG, "Cannot get PackageManager");
+                throw new UnsupportedOperationException();
+            }
+            if (!pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
+                Log.e(TAG, "This device does not support card emulation");
+                throw new UnsupportedOperationException();
+            }
+            sIsInitialized = true;
+        }
+        CardEmulation manager = sCardEmus.get(context);
+        if (manager == null) {
+            // Get card emu service
+            INfcCardEmulation service = adapter.getCardEmulationService();
+            if (service == null) {
+                Log.e(TAG, "This device does not implement the INfcCardEmulation interface.");
+                throw new UnsupportedOperationException();
+            }
+            manager = new CardEmulation(context, service);
+            sCardEmus.put(context, manager);
+        }
+        return manager;
+    }
+
+    /**
+     * Allows an application to query whether a service is currently
+     * the default service to handle a card emulation category.
+     *
+     * <p>Note that if {@link #getSelectionModeForCategory(String)}
+     * returns {@link #SELECTION_MODE_ALWAYS_ASK} or {@link #SELECTION_MODE_ASK_IF_CONFLICT},
+     * this method will always return false. That is because in these
+     * selection modes a default can't be set at the category level. For categories where
+     * the selection mode is {@link #SELECTION_MODE_ALWAYS_ASK} or
+     * {@link #SELECTION_MODE_ASK_IF_CONFLICT}, use
+     * {@link #isDefaultServiceForAid(ComponentName, String)} to determine whether a service
+     * is the default for a specific AID.
+     *
+     * @param service The ComponentName of the service
+     * @param category The category
+     * @return whether service is currently the default service for the category.
+     *
+     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+     */
+    public boolean isDefaultServiceForCategory(ComponentName service, String category) {
+        try {
+            return sService.isDefaultServiceForCategory(mContext.getUser().getIdentifier(),
+                    service, category);
+        } catch (RemoteException e) {
+            // Try one more time
+            recoverService();
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                return false;
+            }
+            try {
+                return sService.isDefaultServiceForCategory(mContext.getUser().getIdentifier(),
+                        service, category);
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                return false;
+            }
+        }
+    }
+
+    /**
+     *
+     * Allows an application to query whether a service is currently
+     * the default handler for a specified ISO7816-4 Application ID.
+     *
+     * @param service The ComponentName of the service
+     * @param aid The ISO7816-4 Application ID
+     * @return whether the service is the default handler for the specified AID
+     *
+     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+     */
+    public boolean isDefaultServiceForAid(ComponentName service, String aid) {
+        try {
+            return sService.isDefaultServiceForAid(mContext.getUser().getIdentifier(),
+                    service, aid);
+        } catch (RemoteException e) {
+            // Try one more time
+            recoverService();
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                return false;
+            }
+            try {
+                return sService.isDefaultServiceForAid(mContext.getUser().getIdentifier(),
+                        service, aid);
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to reach CardEmulationService.");
+                return false;
+            }
+        }
+    }
+
+    /**
+     * Returns whether the user has allowed AIDs registered in the
+     * specified category to be handled by a service that is preferred
+     * by the foreground application, instead of by a pre-configured default.
+     *
+     * Foreground applications can set such preferences using the
+     * {@link #setPreferredService(Activity, ComponentName)} method.
+     *
+     * @param category The category, e.g. {@link #CATEGORY_PAYMENT}
+     * @return whether AIDs in the category can be handled by a service
+     *         specified by the foreground app.
+     */
+    @SuppressWarnings("NonUserGetterCalled")
+    public boolean categoryAllowsForegroundPreference(String category) {
+        if (CATEGORY_PAYMENT.equals(category)) {
+            boolean preferForeground = false;
+            Context contextAsUser = mContext.createContextAsUser(
+                    UserHandle.of(UserHandle.myUserId()), 0);
+            try {
+                preferForeground = Settings.Secure.getInt(
+                        contextAsUser.getContentResolver(),
+                        Constants.SETTINGS_SECURE_NFC_PAYMENT_FOREGROUND) != 0;
+            } catch (SettingNotFoundException e) {
+            }
+            return preferForeground;
+        } else {
+            // Allowed for all other categories
+            return true;
+        }
+    }
+
+    /**
+     * Returns the service selection mode for the passed in category.
+     * Valid return values are:
+     * <p>{@link #SELECTION_MODE_PREFER_DEFAULT} the user has requested a default
+     *    service for this category, which will be preferred.
+     * <p>{@link #SELECTION_MODE_ALWAYS_ASK} the user has requested to be asked
+     *    every time what service they would like to use in this category.
+     * <p>{@link #SELECTION_MODE_ASK_IF_CONFLICT} the user will only be asked
+     *    to pick a service if there is a conflict.
+     * @param category The category, for example {@link #CATEGORY_PAYMENT}
+     * @return the selection mode for the passed in category
+     */
+    public int getSelectionModeForCategory(String category) {
+        if (CATEGORY_PAYMENT.equals(category)) {
+            boolean paymentRegistered = false;
+            try {
+                paymentRegistered = sService.isDefaultPaymentRegistered();
+            } catch (RemoteException e) {
+                recoverService();
+                if (sService == null) {
+                    Log.e(TAG, "Failed to recover CardEmulationService.");
+                    return SELECTION_MODE_ALWAYS_ASK;
+                }
+                try {
+                    paymentRegistered = sService.isDefaultPaymentRegistered();
+                } catch (RemoteException ee) {
+                    Log.e(TAG, "Failed to reach CardEmulationService.");
+                    return SELECTION_MODE_ALWAYS_ASK;
+                }
+            }
+            if (paymentRegistered) {
+                return SELECTION_MODE_PREFER_DEFAULT;
+            } else {
+                return SELECTION_MODE_ALWAYS_ASK;
+            }
+        } else {
+            return SELECTION_MODE_ASK_IF_CONFLICT;
+        }
+    }
+    /**
+     * Sets whether the system should default to observe mode or not when
+     * the service is in the foreground or the default payment service.
+     *
+     * @param service The component name of the service
+     * @param enable Whether the servic should default to observe mode or not
+     * @return whether the change was successful.
+     */
+    @FlaggedApi(Flags.FLAG_NFC_OBSERVE_MODE)
+    public boolean setServiceObserveModeDefault(@NonNull ComponentName service, boolean enable) {
+        try {
+            return sService.setServiceObserveModeDefault(mContext.getUser().getIdentifier(),
+                    service, enable);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to reach CardEmulationService.");
+        }
+        return  false;
+    }
+
+    /**
+     * Registers a list of AIDs for a specific category for the
+     * specified service.
+     *
+     * <p>If a list of AIDs for that category was previously
+     * registered for this service (either statically
+     * through the manifest, or dynamically by using this API),
+     * that list of AIDs will be replaced with this one.
+     *
+     * <p>Note that you can only register AIDs for a service that
+     * is running under the same UID as the caller of this API. Typically
+     * this means you need to call this from the same
+     * package as the service itself, though UIDs can also
+     * be shared between packages using shared UIDs.
+     *
+     * @param service The component name of the service
+     * @param category The category of AIDs to be registered
+     * @param aids A list containing the AIDs to be registered
+     * @return whether the registration was successful.
+     */
+    public boolean registerAidsForService(ComponentName service, String category,
+            List<String> aids) {
+        AidGroup aidGroup = new AidGroup(aids, category);
+        try {
+            return sService.registerAidGroupForService(mContext.getUser().getIdentifier(),
+                    service, aidGroup);
+        } catch (RemoteException e) {
+            // Try one more time
+            recoverService();
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                return false;
+            }
+            try {
+                return sService.registerAidGroupForService(mContext.getUser().getIdentifier(),
+                        service, aidGroup);
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to reach CardEmulationService.");
+                return false;
+            }
+        }
+    }
+
+    /**
+     * Unsets the off-host Secure Element for the given service.
+     *
+     * <p>Note that this will only remove Secure Element that was dynamically
+     * set using the {@link #setOffHostForService(ComponentName, String)}
+     * and resets it to a value that was statically assigned using manifest.
+     *
+     * <p>Note that you can only unset off-host SE for a service that
+     * is running under the same UID as the caller of this API. Typically
+     * this means you need to call this from the same
+     * package as the service itself, though UIDs can also
+     * be shared between packages using shared UIDs.
+     *
+     * @param service The component name of the service
+     * @return whether the registration was successful.
+     */
+    @RequiresPermission(android.Manifest.permission.NFC)
+    @NonNull
+    public boolean unsetOffHostForService(@NonNull ComponentName service) {
+        NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
+        if (adapter == null) {
+            return false;
+        }
+
+        try {
+            return sService.unsetOffHostForService(mContext.getUser().getIdentifier(), service);
+        } catch (RemoteException e) {
+            // Try one more time
+            recoverService();
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                return false;
+            }
+            try {
+                return sService.unsetOffHostForService(mContext.getUser().getIdentifier(), service);
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to reach CardEmulationService.");
+                return false;
+            }
+        }
+    }
+
+    /**
+     * Sets the off-host Secure Element for the given service.
+     *
+     * <p>If off-host SE was initially set (either statically
+     * through the manifest, or dynamically by using this API),
+     * it will be replaced with this one. All AIDs registered by
+     * this service will be re-routed to this Secure Element if
+     * successful. AIDs that was statically assigned using manifest
+     * will re-route to off-host SE that stated in manifest after NFC
+     * toggle.
+     *
+     * <p>Note that you can only set off-host SE for a service that
+     * is running under the same UID as the caller of this API. Typically
+     * this means you need to call this from the same
+     * package as the service itself, though UIDs can also
+     * be shared between packages using shared UIDs.
+     *
+     * <p>Registeration will be successful only if the Secure Element
+     * exists on the device.
+     *
+     * @param service The component name of the service
+     * @param offHostSecureElement Secure Element to register the AID to. Only accept strings with
+     *                             prefix SIM or prefix eSE.
+     *                             Ref: GSMA TS.26 - NFC Handset Requirements
+     *                             TS26_NFC_REQ_069: For UICC, Secure Element Name SHALL be
+     *                                               SIM[smartcard slot]
+     *                                               (e.g. SIM/SIM1, SIM2… SIMn).
+     *                             TS26_NFC_REQ_070: For embedded SE, Secure Element Name SHALL be
+     *                                               eSE[number]
+     *                                               (e.g. eSE/eSE1, eSE2, etc.).
+     * @return whether the registration was successful.
+     */
+    @RequiresPermission(android.Manifest.permission.NFC)
+    @NonNull
+    public boolean setOffHostForService(@NonNull ComponentName service,
+            @NonNull String offHostSecureElement) {
+        boolean validSecureElement = false;
+
+        NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
+        if (adapter == null || offHostSecureElement == null) {
+            return false;
+        }
+
+        List<String> validSE = adapter.getSupportedOffHostSecureElements();
+        if ((offHostSecureElement.startsWith("eSE") && !validSE.contains("eSE"))
+                || (offHostSecureElement.startsWith("SIM") && !validSE.contains("SIM"))) {
+            return false;
+        }
+
+        if (!offHostSecureElement.startsWith("eSE") && !offHostSecureElement.startsWith("SIM")) {
+            return false;
+        }
+
+        if (offHostSecureElement.equals("eSE")) {
+            offHostSecureElement = "eSE1";
+        } else if (offHostSecureElement.equals("SIM")) {
+            offHostSecureElement = "SIM1";
+        }
+
+        try {
+            return sService.setOffHostForService(mContext.getUser().getIdentifier(), service,
+                offHostSecureElement);
+        } catch (RemoteException e) {
+            // Try one more time
+            recoverService();
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                return false;
+            }
+            try {
+                return sService.setOffHostForService(mContext.getUser().getIdentifier(), service,
+                        offHostSecureElement);
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to reach CardEmulationService.");
+                return false;
+            }
+        }
+    }
+
+    /**
+     * Retrieves the currently registered AIDs for the specified
+     * category for a service.
+     *
+     * <p>Note that this will only return AIDs that were dynamically
+     * registered using {@link #registerAidsForService(ComponentName, String, List)}
+     * method. It will *not* return AIDs that were statically registered
+     * in the manifest.
+     *
+     * @param service The component name of the service
+     * @param category The category for which the AIDs were registered,
+     *                 e.g. {@link #CATEGORY_PAYMENT}
+     * @return The list of AIDs registered for this category, or null if it couldn't be found.
+     */
+    public List<String> getAidsForService(ComponentName service, String category) {
+        try {
+            AidGroup group =  sService.getAidGroupForService(mContext.getUser().getIdentifier(),
+                    service, category);
+            return (group != null ? group.getAids() : null);
+        } catch (RemoteException e) {
+            recoverService();
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                return null;
+            }
+            try {
+                AidGroup group = sService.getAidGroupForService(mContext.getUser().getIdentifier(),
+                        service, category);
+                return (group != null ? group.getAids() : null);
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                return null;
+            }
+        }
+    }
+
+    /**
+     * Removes a previously registered list of AIDs for the specified category for the
+     * service provided.
+     *
+     * <p>Note that this will only remove AIDs that were dynamically
+     * registered using the {@link #registerAidsForService(ComponentName, String, List)}
+     * method. It will *not* remove AIDs that were statically registered in
+     * the manifest. If dynamically registered AIDs are removed using
+     * this method, and a statically registered AID group for the same category
+     * exists in the manifest, the static AID group will become active again.
+     *
+     * @param service The component name of the service
+     * @param category The category of the AIDs to be removed, e.g. {@link #CATEGORY_PAYMENT}
+     * @return whether the group was successfully removed.
+     */
+    public boolean removeAidsForService(ComponentName service, String category) {
+        try {
+            return sService.removeAidGroupForService(mContext.getUser().getIdentifier(), service,
+                    category);
+        } catch (RemoteException e) {
+            // Try one more time
+            recoverService();
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                return false;
+            }
+            try {
+                return sService.removeAidGroupForService(mContext.getUser().getIdentifier(),
+                        service, category);
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to reach CardEmulationService.");
+                return false;
+            }
+        }
+    }
+
+    /**
+     * Allows a foreground application to specify which card emulation service
+     * should be preferred while a specific Activity is in the foreground.
+     *
+     * <p>The specified Activity must currently be in resumed state. A good
+     * paradigm is to call this method in your {@link Activity#onResume}, and to call
+     * {@link #unsetPreferredService(Activity)} in your {@link Activity#onPause}.
+     *
+     * <p>This method call will fail in two specific scenarios:
+     * <ul>
+     * <li> If the service registers one or more AIDs in the {@link #CATEGORY_PAYMENT}
+     * category, but the user has indicated that foreground apps are not allowed
+     * to override the default payment service.
+     * <li> If the service registers one or more AIDs in the {@link #CATEGORY_OTHER}
+     * category that are also handled by the default payment service, and the
+     * user has indicated that foreground apps are not allowed to override the
+     * default payment service.
+     * </ul>
+     *
+     * <p> Use {@link #categoryAllowsForegroundPreference(String)} to determine
+     * whether foreground apps can override the default payment service.
+     *
+     * <p>Note that this preference is not persisted by the OS, and hence must be
+     * called every time the Activity is resumed.
+     *
+     * @param activity The activity which prefers this service to be invoked
+     * @param service The service to be preferred while this activity is in the foreground
+     * @return whether the registration was successful
+     */
+    public boolean setPreferredService(Activity activity, ComponentName service) {
+        // Verify the activity is in the foreground before calling into NfcService
+        if (activity == null || service == null) {
+            throw new NullPointerException("activity or service or category is null");
+        }
+        try {
+            return sService.setPreferredService(service);
+        } catch (RemoteException e) {
+            // Try one more time
+            recoverService();
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                return false;
+            }
+            try {
+                return sService.setPreferredService(service);
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to reach CardEmulationService.");
+                return false;
+            }
+        }
+    }
+
+    /**
+     * Unsets the preferred service for the specified Activity.
+     *
+     * <p>Note that the specified Activity must still be in resumed
+     * state at the time of this call. A good place to call this method
+     * is in your {@link Activity#onPause} implementation.
+     *
+     * @param activity The activity which the service was registered for
+     * @return true when successful
+     */
+    public boolean unsetPreferredService(Activity activity) {
+        if (activity == null) {
+            throw new NullPointerException("activity is null");
+        }
+        try {
+            return sService.unsetPreferredService();
+        } catch (RemoteException e) {
+            // Try one more time
+            recoverService();
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                return false;
+            }
+            try {
+                return sService.unsetPreferredService();
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to reach CardEmulationService.");
+                return false;
+            }
+        }
+    }
+
+    /**
+     * Some devices may allow an application to register all
+     * AIDs that starts with a certain prefix, e.g.
+     * "A000000004*" to register all MasterCard AIDs.
+     *
+     * Use this method to determine whether this device
+     * supports registering AID prefixes.
+     *
+     * @return whether AID prefix registering is supported on this device.
+     */
+    public boolean supportsAidPrefixRegistration() {
+        try {
+            return sService.supportsAidPrefixRegistration();
+        } catch (RemoteException e) {
+            recoverService();
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                return false;
+            }
+            try {
+                return sService.supportsAidPrefixRegistration();
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to reach CardEmulationService.");
+                return false;
+            }
+        }
+    }
+
+    /**
+     * Retrieves the registered AIDs for the preferred payment service.
+     *
+     * @return The list of AIDs registered for this category, or null if it couldn't be found.
+     */
+    @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO)
+    @Nullable
+    public List<String> getAidsForPreferredPaymentService() {
+        try {
+            ApduServiceInfo serviceInfo = sService.getPreferredPaymentService(
+                    mContext.getUser().getIdentifier());
+            return (serviceInfo != null ? serviceInfo.getAids() : null);
+        } catch (RemoteException e) {
+            recoverService();
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                throw e.rethrowFromSystemServer();
+            }
+            try {
+                ApduServiceInfo serviceInfo =
+                        sService.getPreferredPaymentService(mContext.getUser().getIdentifier());
+                return (serviceInfo != null ? serviceInfo.getAids() : null);
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Retrieves the route destination for the preferred payment service.
+     *
+     * @return The route destination secure element name of the preferred payment service.
+     *         HCE payment: "Host"
+     *         OffHost payment: 1. String with prefix SIM or prefix eSE string.
+     *                             Ref: GSMA TS.26 - NFC Handset Requirements
+     *                             TS26_NFC_REQ_069: For UICC, Secure Element Name SHALL be
+     *                                               SIM[smartcard slot]
+     *                                               (e.g. SIM/SIM1, SIM2… SIMn).
+     *                             TS26_NFC_REQ_070: For embedded SE, Secure Element Name SHALL be
+     *                                               eSE[number]
+     *                                               (e.g. eSE/eSE1, eSE2, etc.).
+     *                          2. "OffHost" if the payment service does not specify secure element
+     *                             name.
+     */
+    @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO)
+    @Nullable
+    public String getRouteDestinationForPreferredPaymentService() {
+        try {
+            ApduServiceInfo serviceInfo = sService.getPreferredPaymentService(
+                    mContext.getUser().getIdentifier());
+            if (serviceInfo != null) {
+                if (!serviceInfo.isOnHost()) {
+                    return serviceInfo.getOffHostSecureElement() == null ?
+                            "OffHost" : serviceInfo.getOffHostSecureElement();
+                }
+                return "Host";
+            }
+            return null;
+        } catch (RemoteException e) {
+            recoverService();
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                throw e.rethrowFromSystemServer();
+            }
+            try {
+                ApduServiceInfo serviceInfo =
+                        sService.getPreferredPaymentService(mContext.getUser().getIdentifier());
+                if (serviceInfo != null) {
+                    if (!serviceInfo.isOnHost()) {
+                        return serviceInfo.getOffHostSecureElement() == null ?
+                                "Offhost" : serviceInfo.getOffHostSecureElement();
+                    }
+                    return "Host";
+                }
+                return null;
+
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Returns a user-visible description of the preferred payment service.
+     *
+     * @return the preferred payment service description
+     */
+    @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO)
+    @Nullable
+    public CharSequence getDescriptionForPreferredPaymentService() {
+        try {
+            ApduServiceInfo serviceInfo = sService.getPreferredPaymentService(
+                    mContext.getUser().getIdentifier());
+            return (serviceInfo != null ? serviceInfo.getDescription() : null);
+        } catch (RemoteException e) {
+            recoverService();
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                throw e.rethrowFromSystemServer();
+            }
+            try {
+                ApduServiceInfo serviceInfo =
+                        sService.getPreferredPaymentService(mContext.getUser().getIdentifier());
+                return (serviceInfo != null ? serviceInfo.getDescription() : null);
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public boolean setDefaultServiceForCategory(ComponentName service, String category) {
+        try {
+            return sService.setDefaultServiceForCategory(mContext.getUser().getIdentifier(),
+                    service, category);
+        } catch (RemoteException e) {
+            // Try one more time
+            recoverService();
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                return false;
+            }
+            try {
+                return sService.setDefaultServiceForCategory(mContext.getUser().getIdentifier(),
+                        service, category);
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to reach CardEmulationService.");
+                return false;
+            }
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public boolean setDefaultForNextTap(ComponentName service) {
+        try {
+            return sService.setDefaultForNextTap(mContext.getUser().getIdentifier(), service);
+        } catch (RemoteException e) {
+            // Try one more time
+            recoverService();
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                return false;
+            }
+            try {
+                return sService.setDefaultForNextTap(mContext.getUser().getIdentifier(), service);
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to reach CardEmulationService.");
+                return false;
+            }
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public boolean setDefaultForNextTap(int userId, ComponentName service) {
+        try {
+            return sService.setDefaultForNextTap(userId, service);
+        } catch (RemoteException e) {
+            // Try one more time
+            recoverService();
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                return false;
+            }
+            try {
+                return sService.setDefaultForNextTap(userId, service);
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to reach CardEmulationService.");
+                return false;
+            }
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public List<ApduServiceInfo> getServices(String category) {
+        try {
+            return sService.getServices(mContext.getUser().getIdentifier(), category);
+        } catch (RemoteException e) {
+            // Try one more time
+            recoverService();
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                return null;
+            }
+            try {
+                return sService.getServices(mContext.getUser().getIdentifier(), category);
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to reach CardEmulationService.");
+                return null;
+            }
+        }
+    }
+
+    /**
+     * Retrieves list of services registered of the provided category for the provided user.
+     *
+     * @param category Category string, one of {@link #CATEGORY_PAYMENT} or {@link #CATEGORY_OTHER}
+     * @param userId the user handle of the user whose information is being requested.
+     * @hide
+     */
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    @NonNull
+    public List<ApduServiceInfo> getServices(@NonNull String category, @UserIdInt int userId) {
+        try {
+            return sService.getServices(userId, category);
+        } catch (RemoteException e) {
+            // Try one more time
+            recoverService();
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                return null;
+            }
+            try {
+                return sService.getServices(userId, category);
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to reach CardEmulationService.");
+                return null;
+            }
+        }
+    }
+
+    /**
+     * A valid AID according to ISO/IEC 7816-4:
+     * <ul>
+     * <li>Has >= 5 bytes and <=16 bytes (>=10 hex chars and <= 32 hex chars)
+     * <li>Consist of only hex characters
+     * <li>Additionally, we allow an asterisk at the end, to indicate
+     *     a prefix
+     * <li>Additinally we allow an (#) at symbol at the end, to indicate
+     *     a subset
+     * </ul>
+     *
+     * @hide
+     */
+    public static boolean isValidAid(String aid) {
+        if (aid == null)
+            return false;
+
+        // If a prefix/subset AID, the total length must be odd (even # of AID chars + '*')
+        if ((aid.endsWith("*") || aid.endsWith("#")) && ((aid.length() % 2) == 0)) {
+            Log.e(TAG, "AID " + aid + " is not a valid AID.");
+            return false;
+        }
+
+        // If not a prefix/subset AID, the total length must be even (even # of AID chars)
+        if ((!(aid.endsWith("*") || aid.endsWith("#"))) && ((aid.length() % 2) != 0)) {
+            Log.e(TAG, "AID " + aid + " is not a valid AID.");
+            return false;
+        }
+
+        // Verify hex characters
+        if (!AID_PATTERN.matcher(aid).matches()) {
+            Log.e(TAG, "AID " + aid + " is not a valid AID.");
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Allows to set or unset preferred service (category other) to avoid  AID Collision.
+     *
+     * @param service The ComponentName of the service
+     * @param status  true to enable, false to disable
+     * @return set service for the category and true if service is already set return false.
+     *
+     * @hide
+     */
+    public boolean setServiceEnabledForCategoryOther(ComponentName service, boolean status) {
+        if (service == null) {
+            throw new NullPointerException("activity or service or category is null");
+        }
+        int userId = mContext.getUser().getIdentifier();
+
+        try {
+            return sService.setServiceEnabledForCategoryOther(userId, service, status);
+        } catch (RemoteException e) {
+            // Try one more time
+            recoverService();
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                return false;
+            }
+            try {
+                return sService.setServiceEnabledForCategoryOther(userId, service, status);
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to reach CardEmulationService.");
+                return false;
+            }
+        }
+    }
+
+     /**
+      * Setting NFC controller routing table, which includes Protocol Route and Technology Route,
+      * while this Activity is in the foreground.
+      *
+      * The parameter set to null can be used to keep current values for that entry.
+      * <p>
+      * Example usage in an Activity that requires to set proto route to "ESE" and keep tech route:
+      * <pre>
+      * protected void onResume() {
+      *     mNfcAdapter.overrideRoutingTable(this , "ESE" , null);
+      * }</pre>
+      * </p>
+      * Also activities must call this method when it goes to the background,
+      * with all parameters set to null.
+      * @param activity The Activity that requests NFC controller routing table to be changed.
+      * @param protocol ISO-DEP route destination, which can be "DH" or "UICC" or "ESE".
+      * @param technology Tech-A, Tech-B route destination, which can be "DH" or "UICC" or "ESE".
+      * @return true if operation is successful and false otherwise
+      *
+      * This is a high risk API and only included to support mainline effort
+      * @hide
+      */
+    public boolean overrideRoutingTable(Activity activity, String protocol, String technology) {
+        if (activity == null) {
+            throw new NullPointerException("activity or service or category is null");
+        }
+        if (!activity.isResumed()) {
+            throw new IllegalArgumentException("Activity must be resumed.");
+        }
+        try {
+            return sService.overrideRoutingTable(UserHandle.myUserId(), protocol, technology);
+        } catch (RemoteException e) {
+            // Try one more time
+            recoverService();
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                return false;
+            }
+            try {
+                return sService.overrideRoutingTable(UserHandle.myUserId(), protocol, technology);
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to reach CardEmulationService.");
+                return false;
+            }
+        }
+    }
+
+    /**
+     * Restore the NFC controller routing table,
+     * which was changed by {@link #overrideRoutingTable(Activity, String, String)}
+     *
+     * @param activity The Activity that requested NFC controller routing table to be changed.
+     * @return true if operation is successful and false otherwise
+     *
+     * @hide
+     */
+    public boolean recoverRoutingTable(Activity activity) {
+        if (activity == null) {
+            throw new NullPointerException("activity is null");
+        }
+        if (!activity.isResumed()) {
+            throw new IllegalArgumentException("Activity must be resumed.");
+        }
+        try {
+            return sService.recoverRoutingTable(UserHandle.myUserId());
+        } catch (RemoteException e) {
+            // Try one more time
+            recoverService();
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                return false;
+            }
+            try {
+                return sService.recoverRoutingTable(UserHandle.myUserId());
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to reach CardEmulationService.");
+                return false;
+            }
+        }
+    }
+
+    void recoverService() {
+        NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
+        sService = adapter.getCardEmulationService();
+    }
+
+    /**
+     * Returns the {@link Settings.Secure#NFC_PAYMENT_DEFAULT_COMPONENT} for the given user.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO)
+    @FlaggedApi(android.permission.flags.Flags.FLAG_WALLET_ROLE_ENABLED)
+    @Nullable
+    public ApduServiceInfo getPreferredPaymentService() {
+        try {
+            return sService.getPreferredPaymentService(mContext.getUser().getIdentifier());
+        } catch (RemoteException e) {
+            // Try one more time
+            recoverService();
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                return null;
+            }
+            try {
+                return sService.getPreferredPaymentService(mContext.getUser().getIdentifier());
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to reach CardEmulationService.");
+                return null;
+            }
+        }
+    }
+
+}
diff --git a/nfc/java/android/nfc/cardemulation/HostApduService.java b/nfc/java/android/nfc/cardemulation/HostApduService.java
new file mode 100644
index 0000000..7cd2533
--- /dev/null
+++ b/nfc/java/android/nfc/cardemulation/HostApduService.java
@@ -0,0 +1,519 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc.cardemulation;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.app.Service;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.nfc.NfcAdapter;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * <p>HostApduService is a convenience {@link Service} class that can be
+ * extended to emulate an NFC card inside an Android
+ * service component.
+ *
+ * <div class="special reference">
+ * <h3>Developer Guide</h3>
+ * For a general introduction to card emulation, see
+ * <a href="{@docRoot}guide/topics/connectivity/nfc/hce.html">
+ * Host-based Card Emulation</a>.</p>
+ * </div>
+ *
+ * <h3>NFC Protocols</h3>
+ * <p>Cards emulated by this class are based on the NFC-Forum ISO-DEP
+ * protocol (based on ISO/IEC 14443-4) and support processing
+ * command Application Protocol Data Units (APDUs) as
+ * defined in the ISO/IEC 7816-4 specification.
+ *
+ * <h3>Service selection</h3>
+ * <p>When a remote NFC device wants to talk to your
+ * service, it sends a so-called
+ * "SELECT AID" APDU as defined in the ISO/IEC 7816-4 specification.
+ * The AID is an application identifier defined in ISO/IEC 7816-4.
+ *
+ * <p>The registration procedure for AIDs is defined in the
+ * ISO/IEC 7816-5 specification. If you don't want to register an
+ * AID, you are free to use AIDs in the proprietary range:
+ * bits 8-5 of the first byte must each be set to '1'. For example,
+ * "0xF00102030405" is a proprietary AID. If you do use proprietary
+ * AIDs, it is recommended to choose an AID of at least 6 bytes,
+ * to reduce the risk of collisions with other applications that
+ * might be using proprietary AIDs as well.
+ *
+ * <h3>AID groups</h3>
+ * <p>In some cases, a service may need to register multiple AIDs
+ * to implement a certain application, and it needs to be sure
+ * that it is the default handler for all of these AIDs (as opposed
+ * to some AIDs in the group going to another service).
+ *
+ * <p>An AID group is a list of AIDs that should be considered as
+ * belonging together by the OS. For all AIDs in an AID group, the
+ * OS will guarantee one of the following:
+ * <ul>
+ * <li>All AIDs in the group are routed to this service
+ * <li>No AIDs in the group are routed to this service
+ * </ul>
+ * In other words, there is no in-between state, where some AIDs
+ * in the group can be routed to this service, and some to another.
+ * <h3>AID groups and categories</h3>
+ * <p>Each AID group can be associated with a category. This allows
+ * the Android OS to classify services, and it allows the user to
+ * set defaults at the category level instead of the AID level.
+ *
+ * <p>You can use
+ * {@link CardEmulation#isDefaultServiceForCategory(android.content.ComponentName, String)}
+ * to determine if your service is the default handler for a category.
+ *
+ * <p>In this version of the platform, the only known categories
+ * are {@link CardEmulation#CATEGORY_PAYMENT} and {@link CardEmulation#CATEGORY_OTHER}.
+ * AID groups without a category, or with a category that is not recognized
+ * by the current platform version, will automatically be
+ * grouped into the {@link CardEmulation#CATEGORY_OTHER} category.
+ * <h3>Service AID registration</h3>
+ * <p>To tell the platform which AIDs groups
+ * are requested by this service, a {@link #SERVICE_META_DATA}
+ * entry must be included in the declaration of the service. An
+ * example of a HostApduService manifest declaration is shown below:
+ * <pre> &lt;service android:name=".MyHostApduService" android:exported="true" android:permission="android.permission.BIND_NFC_SERVICE"&gt;
+ *     &lt;intent-filter&gt;
+ *         &lt;action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE"/&gt;
+ *     &lt;/intent-filter&gt;
+ *     &lt;meta-data android:name="android.nfc.cardemulation.host_apdu_ervice" android:resource="@xml/apduservice"/&gt;
+ * &lt;/service&gt;</pre>
+ *
+ * This meta-data tag points to an apduservice.xml file.
+ * An example of this file with a single AID group declaration is shown below:
+ * <pre>
+ * &lt;host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
+ *           android:description="@string/servicedesc" android:requireDeviceUnlock="false"&gt;
+ *       &lt;aid-group android:description="@string/aiddescription" android:category="other">
+ *           &lt;aid-filter android:name="F0010203040506"/&gt;
+ *           &lt;aid-filter android:name="F0394148148100"/&gt;
+ *       &lt;/aid-group&gt;
+ * &lt;/host-apdu-service&gt;
+ * </pre>
+ *
+ * <p>The {@link android.R.styleable#HostApduService &lt;host-apdu-service&gt;} is required
+ * to contain a
+ * {@link android.R.styleable#HostApduService_description &lt;android:description&gt;}
+ * attribute that contains a user-friendly description of the service that may be shown in UI.
+ * The
+ * {@link android.R.styleable#HostApduService_requireDeviceUnlock &lt;requireDeviceUnlock&gt;}
+ * attribute can be used to specify that the device must be unlocked before this service
+ * can be invoked to handle APDUs.
+ * <p>The {@link android.R.styleable#HostApduService &lt;host-apdu-service&gt;} must
+ * contain one or more {@link android.R.styleable#AidGroup &lt;aid-group&gt;} tags.
+ * Each {@link android.R.styleable#AidGroup &lt;aid-group&gt;} must contain one or
+ * more {@link android.R.styleable#AidFilter &lt;aid-filter&gt;} tags, each of which
+ * contains a single AID. The AID must be specified in hexadecimal format, and contain
+ * an even number of characters.
+ * <h3>AID conflict resolution</h3>
+ * Multiple HostApduServices may be installed on a single device, and the same AID
+ * can be registered by more than one service. The Android platform resolves AID
+ * conflicts depending on which category an AID belongs to. Each category may
+ * have a different conflict resolution policy. For example, for some categories
+ * the user may be able to select a default service in the Android settings UI.
+ * For other categories, to policy may be to always ask the user which service
+ * is to be invoked in case of conflict.
+ *
+ * To query the conflict resolution policy for a certain category, see
+ * {@link CardEmulation#getSelectionModeForCategory(String)}.
+ *
+ * <h3>Data exchange</h3>
+ * <p>Once the platform has resolved a "SELECT AID" command APDU to a specific
+ * service component, the "SELECT AID" command APDU and all subsequent
+ * command APDUs will be sent to that service through
+ * {@link #processCommandApdu(byte[], Bundle)}, until either:
+ * <ul>
+ * <li>The NFC link is broken</li>
+ * <li>A "SELECT AID" APDU is received which resolves to another service</li>
+ * </ul>
+ * These two scenarios are indicated by a call to {@link #onDeactivated(int)}.
+ *
+ * <p class="note">Use of this class requires the
+ * {@link PackageManager#FEATURE_NFC_HOST_CARD_EMULATION} to be present
+ * on the device.
+ *
+ */
+public abstract class HostApduService extends Service {
+    /**
+     * The {@link Intent} action that must be declared as handled by the service.
+     */
+    @SdkConstant(SdkConstantType.SERVICE_ACTION)
+    public static final String SERVICE_INTERFACE =
+            "android.nfc.cardemulation.action.HOST_APDU_SERVICE";
+
+    /**
+     * The name of the meta-data element that contains
+     * more information about this service.
+     */
+    public static final String SERVICE_META_DATA =
+            "android.nfc.cardemulation.host_apdu_service";
+
+    /**
+     * Reason for {@link #onDeactivated(int)}.
+     * Indicates deactivation was due to the NFC link
+     * being lost.
+     */
+    public static final int DEACTIVATION_LINK_LOSS = 0;
+
+    /**
+     * Reason for {@link #onDeactivated(int)}.
+     *
+     * <p>Indicates deactivation was due to a different AID
+     * being selected (which implicitly deselects the AID
+     * currently active on the logical channel).
+     *
+     * <p>Note that this next AID may still be resolved to this
+     * service, in which case {@link #processCommandApdu(byte[], Bundle)}
+     * will be called again.
+     */
+    public static final int DEACTIVATION_DESELECTED = 1;
+
+    static final String TAG = "ApduService";
+
+    /**
+     * MSG_COMMAND_APDU is sent by NfcService when
+     * a 7816-4 command APDU has been received.
+     *
+     * @hide
+     */
+    public static final int MSG_COMMAND_APDU = 0;
+
+    /**
+     * MSG_RESPONSE_APDU is sent to NfcService to send
+     * a response APDU back to the remote device.
+     *
+     * @hide
+     */
+    public static final int MSG_RESPONSE_APDU = 1;
+
+    /**
+     * MSG_DEACTIVATED is sent by NfcService when
+     * the current session is finished; either because
+     * another AID was selected that resolved to
+     * another service, or because the NFC link
+     * was deactivated.
+     *
+     * @hide
+     */
+    public static final int MSG_DEACTIVATED = 2;
+
+    /**
+     *
+     * @hide
+     */
+    public static final int MSG_UNHANDLED = 3;
+
+    /**
+     * @hide
+     */
+    public static final int MSG_POLLING_LOOP = 4;
+
+    /**
+     * @hide
+     */
+    public static final String KEY_DATA = "data";
+
+    /**
+     * POLLING_LOOP_TYPE_KEY is the Bundle key for the type of
+     * polling loop frame in the Bundle passed to {@link #processPollingFrames(List)}
+     */
+    @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+    public static final String POLLING_LOOP_TYPE_KEY = "android.nfc.cardemulation.TYPE";
+
+    /**
+     * POLLING_LOOP_TYPE_A is the value associated with the key
+     * POLLING_LOOP_TYPE  in the Bundle passed to {@link #processPollingFrames(List)}
+     * when the polling loop is for NFC-A.
+     */
+    @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+    public static final char POLLING_LOOP_TYPE_A = 'A';
+
+    /**
+     * POLLING_LOOP_TYPE_B is the value associated with the key
+     * POLLING_LOOP_TYPE  in the Bundle passed to {@link #processPollingFrames(List)}
+     * when the polling loop is for NFC-B.
+     */
+    @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+    public static final char POLLING_LOOP_TYPE_B = 'B';
+
+    /**
+     * POLLING_LOOP_TYPE_F is the value associated with the key
+     * POLLING_LOOP_TYPE  in the Bundle passed to {@link #processPollingFrames(List)}
+     * when the polling loop is for NFC-F.
+     */
+    @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+    public static final char POLLING_LOOP_TYPE_F = 'F';
+
+    /**
+     * POLLING_LOOP_TYPE_ON is the value associated with the key
+     * POLLING_LOOP_TYPE  in the Bundle passed to {@link #processPollingFrames(List)}
+     * when the polling loop turns on.
+     */
+    @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+    public static final char POLLING_LOOP_TYPE_ON = 'O';
+
+    /**
+     * POLLING_LOOP_TYPE_OFF is the value associated with the key
+     * POLLING_LOOP_TYPE  in the Bundle passed to {@link #processPollingFrames(List)}
+     * when the polling loop turns off.
+     */
+    @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+    public static final char POLLING_LOOP_TYPE_OFF = 'X';
+
+    /**
+     * POLLING_LOOP_TYPE_UNKNOWN is the value associated with the key
+     * POLLING_LOOP_TYPE  in the Bundle passed to {@link #processPollingFrames(List)}
+     * when the polling loop frame isn't recognized.
+     */
+    @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+    public static final char POLLING_LOOP_TYPE_UNKNOWN = 'U';
+
+    /**
+     * POLLING_LOOP_DATA is the Bundle key for the raw data of captured from
+     * the polling loop frame in the Bundle passed to {@link #processPollingFrames(List)}
+     * when the frame type isn't recognized.
+     */
+    @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+    public static final String POLLING_LOOP_DATA_KEY = "android.nfc.cardemulation.DATA";
+
+    /**
+     * POLLING_LOOP_GAIN_KEY is the Bundle key for the field strength of
+     * the polling loop frame in the Bundle passed to {@link #processPollingFrames(List)}
+     * when the frame type isn't recognized.
+     */
+    @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+    public static final String POLLING_LOOP_GAIN_KEY = "android.nfc.cardemulation.GAIN";
+
+    /**
+     * POLLING_LOOP_TIMESTAMP_KEY is the Bundle key for the timestamp of
+     * the polling loop frame in the Bundle passed to {@link #processPollingFrames(List)}
+     * when the frame type isn't recognized.
+     */
+    @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+    public static final String POLLING_LOOP_TIMESTAMP_KEY = "android.nfc.cardemulation.TIMESTAMP";
+
+    /**
+     * @hide
+     */
+    public static final String POLLING_LOOP_FRAMES_BUNDLE_KEY =
+            "android.nfc.cardemulation.POLLING_FRAMES";
+
+    /**
+     * Messenger interface to NfcService for sending responses.
+     * Only accessed on main thread by the message handler.
+     *
+     * @hide
+     */
+    Messenger mNfcService = null;
+
+    final Messenger mMessenger = new Messenger(new MsgHandler());
+
+    final class MsgHandler extends Handler {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+            case MSG_COMMAND_APDU:
+                Bundle dataBundle = msg.getData();
+                if (dataBundle == null) {
+                    return;
+                }
+                if (mNfcService == null) mNfcService = msg.replyTo;
+
+                byte[] apdu = dataBundle.getByteArray(KEY_DATA);
+                if (apdu != null) {
+                        HostApduService has = HostApduService.this;
+                    byte[] responseApdu = processCommandApdu(apdu, null);
+                    if (responseApdu != null) {
+                        if (mNfcService == null) {
+                            Log.e(TAG, "Response not sent; service was deactivated.");
+                            return;
+                        }
+                        Message responseMsg = Message.obtain(null, MSG_RESPONSE_APDU);
+                        Bundle responseBundle = new Bundle();
+                        responseBundle.putByteArray(KEY_DATA, responseApdu);
+                        responseMsg.setData(responseBundle);
+                        responseMsg.replyTo = mMessenger;
+                        try {
+                            mNfcService.send(responseMsg);
+                        } catch (RemoteException e) {
+                            Log.e("TAG", "Response not sent; RemoteException calling into " +
+                                    "NfcService.");
+                        }
+                    }
+                } else {
+                    Log.e(TAG, "Received MSG_COMMAND_APDU without data.");
+                }
+                break;
+            case MSG_RESPONSE_APDU:
+                if (mNfcService == null) {
+                    Log.e(TAG, "Response not sent; service was deactivated.");
+                    return;
+                }
+                try {
+                    msg.replyTo = mMessenger;
+                    mNfcService.send(msg);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "RemoteException calling into NfcService.");
+                }
+                break;
+            case MSG_DEACTIVATED:
+                // Make sure we won't call into NfcService again
+                mNfcService = null;
+                onDeactivated(msg.arg1);
+                break;
+            case MSG_UNHANDLED:
+                if (mNfcService == null) {
+                    Log.e(TAG, "notifyUnhandled not sent; service was deactivated.");
+                    return;
+                }
+                try {
+                    msg.replyTo = mMessenger;
+                    mNfcService.send(msg);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "RemoteException calling into NfcService.");
+                }
+                break;
+                case MSG_POLLING_LOOP:
+                    ArrayList<Bundle> frames =
+                            msg.getData().getParcelableArrayList(POLLING_LOOP_FRAMES_BUNDLE_KEY,
+                            Bundle.class);
+                    processPollingFrames(frames);
+                    break;
+            default:
+                super.handleMessage(msg);
+            }
+        }
+    }
+
+    @Override
+    public final IBinder onBind(Intent intent) {
+        return mMessenger.getBinder();
+    }
+
+    /**
+     * Sends a response APDU back to the remote device.
+     *
+     * <p>Note: this method may be called from any thread and will not block.
+     * @param responseApdu A byte-array containing the reponse APDU.
+     */
+    public final void sendResponseApdu(byte[] responseApdu) {
+        Message responseMsg = Message.obtain(null, MSG_RESPONSE_APDU);
+        Bundle dataBundle = new Bundle();
+        dataBundle.putByteArray(KEY_DATA, responseApdu);
+        responseMsg.setData(dataBundle);
+        try {
+            mMessenger.send(responseMsg);
+        } catch (RemoteException e) {
+            Log.e("TAG", "Local messenger has died.");
+        }
+    }
+
+    /**
+     * Calling this method allows the service to tell the OS
+     * that it won't be able to complete this transaction -
+     * for example, because it requires data connectivity
+     * that is not present at that moment.
+     *
+     * The OS may use this indication to give the user a list
+     * of alternative applications that can handle the last
+     * AID that was selected. If the user would select an
+     * application from the list, that action by itself
+     * will not cause the default to be changed; the selected
+     * application will be invoked for the next tap only.
+     *
+     * If there are no other applications that can handle
+     * this transaction, the OS will show an error dialog
+     * indicating your service could not complete the
+     * transaction.
+     *
+     * <p>Note: this method may be called anywhere between
+     *    the first {@link #processCommandApdu(byte[], Bundle)}
+     *    call and a {@link #onDeactivated(int)} call.
+     */
+    public final void notifyUnhandled() {
+        Message unhandledMsg = Message.obtain(null, MSG_UNHANDLED);
+        try {
+            mMessenger.send(unhandledMsg);
+        } catch (RemoteException e) {
+            Log.e("TAG", "Local messenger has died.");
+        }
+    }
+
+    /**
+     * This method is called when a polling frame has been received from a
+     * remote device. If the device is in observe mode, the service should
+     * call {@link NfcAdapter#allowTransaction()} once it is ready to proceed
+     * with the transaction. If the device is not in observe mode, the service
+     * can use this polling frame information to determine how to proceed if it
+     * subsequently has {@link #processCommandApdu(byte[], Bundle)} called. The
+     * service must override this method inorder to receive polling frames,
+     * otherwise the base implementation drops the frame.
+     *
+     * @param frame A description of the polling frame.
+     */
+    @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+    public void processPollingFrames(@NonNull List<Bundle> frame) {
+    }
+
+    /**
+     * <p>This method will be called when a command APDU has been received
+     * from a remote device. A response APDU can be provided directly
+     * by returning a byte-array in this method. Note that in general
+     * response APDUs must be sent as quickly as possible, given the fact
+     * that the user is likely holding their device over an NFC reader
+     * when this method is called.
+     *
+     * <p class="note">If there are multiple services that have registered for the same
+     * AIDs in their meta-data entry, you will only get called if the user has
+     * explicitly selected your service, either as a default or just for the next tap.
+     *
+     * <p class="note">This method is running on the main thread of your application.
+     * If you cannot return a response APDU immediately, return null
+     * and use the {@link #sendResponseApdu(byte[])} method later.
+     *
+     * @param commandApdu The APDU that was received from the remote device
+     * @param extras A bundle containing extra data. May be null.
+     * @return a byte-array containing the response APDU, or null if no
+     *         response APDU can be sent at this point.
+     */
+    public abstract byte[] processCommandApdu(byte[] commandApdu, Bundle extras);
+
+    /**
+     * This method will be called in two possible scenarios:
+     * <li>The NFC link has been deactivated or lost
+     * <li>A different AID has been selected and was resolved to a different
+     *     service component
+     * @param reason Either {@link #DEACTIVATION_LINK_LOSS} or {@link #DEACTIVATION_DESELECTED}
+     */
+    public abstract void onDeactivated(int reason);
+}
diff --git a/nfc/java/android/nfc/cardemulation/HostNfcFService.java b/nfc/java/android/nfc/cardemulation/HostNfcFService.java
new file mode 100644
index 0000000..65b5ca7
--- /dev/null
+++ b/nfc/java/android/nfc/cardemulation/HostNfcFService.java
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc.cardemulation;
+
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.app.Service;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * <p>HostNfcFService is a convenience {@link Service} class that can be
+ * extended to emulate an NFC-F card inside an Android service component.
+ *
+ * <h3>NFC Protocols</h3>
+ * <p>Cards emulated by this class are based on the NFC-Forum NFC-F
+ * protocol (based on the JIS-X 6319-4 specification.)</p>
+ *
+ * <h3>System Code and NFCID2 registration</h3>
+ * <p>A {@link HostNfcFService HostNfcFService service} can register
+ * exactly one System Code and one NFCID2. For details about the use of
+ * System Code and NFCID2, see the NFC Forum Digital specification.</p>
+ * <p>To statically register a System Code and NFCID2 with the service, a {@link #SERVICE_META_DATA}
+ * entry must be included in the declaration of the service.
+ *
+ * <p>All {@link HostNfcFService HostNfcFService} declarations in the manifest must require the
+ * {@link android.Manifest.permission#BIND_NFC_SERVICE} permission
+ * in their &lt;service&gt; tag, to ensure that only the platform can bind to your service.</p>
+ *
+ * <p>An example of a HostNfcFService manifest declaration is shown below:
+ *
+ * <pre> &lt;service android:name=".MyHostNfcFService" android:exported="true" android:permission="android.permission.BIND_NFC_SERVICE"&gt;
+ *     &lt;intent-filter&gt;
+ *         &lt;action android:name="android.nfc.cardemulation.action.HOST_NFCF_SERVICE"/&gt;
+ *     &lt;/intent-filter&gt;
+ *     &lt;meta-data android:name="android.nfc.cardemulation.host_nfcf_service" android:resource="@xml/nfcfservice"/&gt;
+ * &lt;/service&gt;</pre>
+ *
+ * This meta-data tag points to an nfcfservice.xml file.
+ * An example of this file with a System Code and NFCID2 declaration is shown below:
+ * <pre>
+ * &lt;host-nfcf-service xmlns:android="http://schemas.android.com/apk/res/android"
+ *           android:description="@string/servicedesc"&gt;
+ *       &lt;system-code-filter android:name="4000"/&gt;
+ *       &lt;nfcid2-filter android:name="02FE000000000000"/&gt;
+         &lt;t3tPmm-filter android:name="FFFFFFFFFFFFFFFF"/&gt;
+ * &lt;/host-nfcf-service&gt;
+ * </pre>
+ *
+ * <p>The {@link android.R.styleable#HostNfcFService &lt;host-nfcf-service&gt;} is required
+ * to contain a
+ * {@link android.R.styleable#HostApduService_description &lt;android:description&gt;}
+ * attribute that contains a user-friendly description of the service that may be shown in UI.
+ * <p>The {@link android.R.styleable#HostNfcFService &lt;host-nfcf-service&gt;} must
+ * contain:
+ * <ul>
+ * <li>Exactly one {@link android.R.styleable#SystemCodeFilter &lt;system-code-filter&gt;} tag.</li>
+ * <li>Exactly one {@link android.R.styleable#Nfcid2Filter &lt;nfcid2-filter&gt;} tag.</li>
+ * <li>Zero or one {@link android.R.styleable#T3tPmmFilter &lt;t3tPmm-filter&gt;} tag.</li>
+ * </ul>
+ * </p>
+ *
+ * <p>Alternatively, the System Code and NFCID2 can be dynamically registererd for a service
+ * by using the {@link NfcFCardEmulation#registerSystemCodeForService(ComponentName, String)} and
+ * {@link NfcFCardEmulation#setNfcid2ForService(ComponentName, String)} methods.
+ * </p>
+ *
+ * <h3>Service selection</h3>
+ * <p>When a remote NFC devices wants to communicate with your service, it
+ * sends a SENSF_REQ command to the NFC controller, requesting a System Code.
+ * If a {@link NfcFCardEmulation NfcFCardEmulation service} has registered
+ * this system code and has been enabled by the foreground application, the
+ * NFC controller will respond with the NFCID2 that is registered for this service.
+ * The reader can then continue data exchange with this service by using the NFCID2.</p>
+ *
+ * <h3>Data exchange</h3>
+ * <p>After service selection, all frames addressed to the NFCID2 of this service will
+ * be sent through {@link #processNfcFPacket(byte[], Bundle)}, until the NFC link is
+ * broken.<p>
+ *
+ * <p>When the NFC link is broken, {@link #onDeactivated(int)} will be called.</p>
+ */
+public abstract class HostNfcFService extends Service {
+    /**
+     * The {@link Intent} action that must be declared as handled by the service.
+     */
+    @SdkConstant(SdkConstantType.SERVICE_ACTION)
+    public static final String SERVICE_INTERFACE =
+            "android.nfc.cardemulation.action.HOST_NFCF_SERVICE";
+
+    /**
+     * The name of the meta-data element that contains
+     * more information about this service.
+     */
+    public static final String SERVICE_META_DATA =
+            "android.nfc.cardemulation.host_nfcf_service";
+
+    /**
+     * Reason for {@link #onDeactivated(int)}.
+     * Indicates deactivation was due to the NFC link
+     * being lost.
+     */
+    public static final int DEACTIVATION_LINK_LOSS = 0;
+
+    static final String TAG = "NfcFService";
+
+    /**
+     * MSG_COMMAND_PACKET is sent by NfcService when
+     * a NFC-F command packet has been received.
+     *
+     * @hide
+     */
+    public static final int MSG_COMMAND_PACKET = 0;
+
+    /**
+     * MSG_RESPONSE_PACKET is sent to NfcService to send
+     * a response packet back to the remote device.
+     *
+     * @hide
+     */
+    public static final int MSG_RESPONSE_PACKET = 1;
+
+    /**
+     * MSG_DEACTIVATED is sent by NfcService when
+     * the current session is finished; because
+     * the NFC link was deactivated.
+     *
+     * @hide
+     */
+    public static final int MSG_DEACTIVATED = 2;
+
+   /**
+     * @hide
+     */
+    public static final String KEY_DATA = "data";
+
+    /**
+     * @hide
+     */
+    public static final String KEY_MESSENGER = "messenger";
+
+    /**
+     * Messenger interface to NfcService for sending responses.
+     * Only accessed on main thread by the message handler.
+     *
+     * @hide
+     */
+    Messenger mNfcService = null;
+
+    final Messenger mMessenger = new Messenger(new MsgHandler());
+
+    final class MsgHandler extends Handler {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+            case MSG_COMMAND_PACKET:
+                Bundle dataBundle = msg.getData();
+                if (dataBundle == null) {
+                    return;
+                }
+                if (mNfcService == null) mNfcService = msg.replyTo;
+
+                byte[] packet = dataBundle.getByteArray(KEY_DATA);
+                if (packet != null) {
+                    byte[] responsePacket = processNfcFPacket(packet, null);
+                    if (responsePacket != null) {
+                        if (mNfcService == null) {
+                            Log.e(TAG, "Response not sent; service was deactivated.");
+                            return;
+                        }
+                        Message responseMsg = Message.obtain(null, MSG_RESPONSE_PACKET);
+                        Bundle responseBundle = new Bundle();
+                        responseBundle.putByteArray(KEY_DATA, responsePacket);
+                        responseMsg.setData(responseBundle);
+                        responseMsg.replyTo = mMessenger;
+                        try {
+                            mNfcService.send(responseMsg);
+                        } catch (RemoteException e) {
+                            Log.e("TAG", "Response not sent; RemoteException calling into " +
+                                    "NfcService.");
+                        }
+                    }
+                } else {
+                    Log.e(TAG, "Received MSG_COMMAND_PACKET without data.");
+                }
+                break;
+            case MSG_RESPONSE_PACKET:
+                if (mNfcService == null) {
+                    Log.e(TAG, "Response not sent; service was deactivated.");
+                    return;
+                }
+                try {
+                    msg.replyTo = mMessenger;
+                    mNfcService.send(msg);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "RemoteException calling into NfcService.");
+                }
+                break;
+            case MSG_DEACTIVATED:
+                // Make sure we won't call into NfcService again
+                mNfcService = null;
+                onDeactivated(msg.arg1);
+                break;
+            default:
+                super.handleMessage(msg);
+            }
+        }
+    }
+
+    @Override
+    public final IBinder onBind(Intent intent) {
+        return mMessenger.getBinder();
+    }
+
+    /**
+     * Sends a response packet back to the remote device.
+     *
+     * <p>Note: this method may be called from any thread and will not block.
+     * @param responsePacket A byte-array containing the response packet.
+     */
+    public final void sendResponsePacket(byte[] responsePacket) {
+        Message responseMsg = Message.obtain(null, MSG_RESPONSE_PACKET);
+        Bundle dataBundle = new Bundle();
+        dataBundle.putByteArray(KEY_DATA, responsePacket);
+        responseMsg.setData(dataBundle);
+        try {
+            mMessenger.send(responseMsg);
+        } catch (RemoteException e) {
+            Log.e("TAG", "Local messenger has died.");
+        }
+    }
+
+    /**
+     * <p>This method will be called when a NFC-F packet has been received
+     * from a remote device. A response packet can be provided directly
+     * by returning a byte-array in this method. Note that in general
+     * response packets must be sent as quickly as possible, given the fact
+     * that the user is likely holding their device over an NFC reader
+     * when this method is called.
+     *
+     * <p class="note">This method is running on the main thread of your application.
+     * If you cannot return a response packet immediately, return null
+     * and use the {@link #sendResponsePacket(byte[])} method later.
+     *
+     * @param commandPacket The NFC-F packet that was received from the remote device
+     * @param extras A bundle containing extra data. May be null.
+     * @return a byte-array containing the response packet, or null if no
+     *         response packet can be sent at this point.
+     */
+    public abstract byte[] processNfcFPacket(byte[] commandPacket, Bundle extras);
+
+    /**
+     * This method will be called in following possible scenarios:
+     * <li>The NFC link has been lost
+     * @param reason {@link #DEACTIVATION_LINK_LOSS}
+     */
+    public abstract void onDeactivated(int reason);
+}
diff --git a/nfc/java/android/nfc/cardemulation/NfcFCardEmulation.java b/nfc/java/android/nfc/cardemulation/NfcFCardEmulation.java
new file mode 100644
index 0000000..48bbf5b6
--- /dev/null
+++ b/nfc/java/android/nfc/cardemulation/NfcFCardEmulation.java
@@ -0,0 +1,473 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc.cardemulation;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.nfc.INfcFCardEmulation;
+import android.nfc.NfcAdapter;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * This class can be used to query the state of
+ * NFC-F card emulation services.
+ *
+ * For a general introduction into NFC card emulation,
+ * please read the <a href="{@docRoot}guide/topics/connectivity/nfc/hce.html">
+ * NFC card emulation developer guide</a>.</p>
+ *
+ * <p class="note">Use of this class requires the
+ * {@link PackageManager#FEATURE_NFC_HOST_CARD_EMULATION_NFCF}
+ * to be present on the device.
+ */
+public final class NfcFCardEmulation {
+    static final String TAG = "NfcFCardEmulation";
+
+    static boolean sIsInitialized = false;
+    static HashMap<Context, NfcFCardEmulation> sCardEmus = new HashMap<Context, NfcFCardEmulation>();
+    static INfcFCardEmulation sService;
+
+    final Context mContext;
+
+    private NfcFCardEmulation(Context context, INfcFCardEmulation service) {
+        mContext = context.getApplicationContext();
+        sService = service;
+    }
+
+    /**
+     * Helper to get an instance of this class.
+     *
+     * @param adapter A reference to an NfcAdapter object.
+     * @return
+     */
+    public static synchronized NfcFCardEmulation getInstance(NfcAdapter adapter) {
+        if (adapter == null) throw new NullPointerException("NfcAdapter is null");
+        Context context = adapter.getContext();
+        if (context == null) {
+            Log.e(TAG, "NfcAdapter context is null.");
+            throw new UnsupportedOperationException();
+        }
+        if (!sIsInitialized) {
+            PackageManager pm = context.getPackageManager();
+            if (pm == null) {
+                Log.e(TAG, "Cannot get PackageManager");
+                throw new UnsupportedOperationException();
+            }
+            if (!pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION_NFCF)) {
+                Log.e(TAG, "This device does not support NFC-F card emulation");
+                throw new UnsupportedOperationException();
+            }
+            sIsInitialized = true;
+        }
+        NfcFCardEmulation manager = sCardEmus.get(context);
+        if (manager == null) {
+            // Get card emu service
+            INfcFCardEmulation service = adapter.getNfcFCardEmulationService();
+            if (service == null) {
+                Log.e(TAG, "This device does not implement the INfcFCardEmulation interface.");
+                throw new UnsupportedOperationException();
+            }
+            manager = new NfcFCardEmulation(context, service);
+            sCardEmus.put(context, manager);
+        }
+        return manager;
+    }
+
+    /**
+     * Retrieves the current System Code for the specified service.
+     *
+     * <p>Before calling {@link #registerSystemCodeForService(ComponentName, String)},
+     * the System Code contained in the Manifest file is returned. After calling
+     * {@link #registerSystemCodeForService(ComponentName, String)}, the System Code
+     * registered there is returned. After calling
+     * {@link #unregisterSystemCodeForService(ComponentName)}, "null" is returned.
+     *
+     * @param service The component name of the service
+     * @return the current System Code
+     */
+    public String getSystemCodeForService(ComponentName service) throws RuntimeException {
+        if (service == null) {
+            throw new NullPointerException("service is null");
+        }
+        try {
+            return sService.getSystemCodeForService(mContext.getUser().getIdentifier(), service);
+        } catch (RemoteException e) {
+            // Try one more time
+            recoverService();
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                return null;
+            }
+            try {
+                return sService.getSystemCodeForService(mContext.getUser().getIdentifier(),
+                        service);
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to reach CardEmulationService.");
+                ee.rethrowAsRuntimeException();
+                return null;
+            }
+        }
+    }
+
+    /**
+     * Registers a System Code for the specified service.
+     *
+     * <p>The System Code must be in range from "4000" to "4FFF" (excluding "4*FF").
+     *
+     * <p>If a System Code was previously registered for this service
+     * (either statically through the manifest, or dynamically by using this API),
+     * it will be replaced with this one.
+     *
+     * <p>Even if the same System Code is already registered for another service,
+     * this method succeeds in registering the System Code.
+     *
+     * <p>Note that you can only register a System Code for a service that
+     * is running under the same UID as the caller of this API. Typically
+     * this means you need to call this from the same
+     * package as the service itself, though UIDs can also
+     * be shared between packages using shared UIDs.
+     *
+     * @param service The component name of the service
+     * @param systemCode The System Code to be registered
+     * @return whether the registration was successful.
+     */
+    public boolean registerSystemCodeForService(ComponentName service, String systemCode)
+            throws RuntimeException {
+        if (service == null || systemCode == null) {
+            throw new NullPointerException("service or systemCode is null");
+        }
+        try {
+            return sService.registerSystemCodeForService(mContext.getUser().getIdentifier(),
+                    service, systemCode);
+        } catch (RemoteException e) {
+            // Try one more time
+            recoverService();
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                return false;
+            }
+            try {
+                return sService.registerSystemCodeForService(mContext.getUser().getIdentifier(),
+                        service, systemCode);
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to reach CardEmulationService.");
+                ee.rethrowAsRuntimeException();
+                return false;
+            }
+        }
+    }
+
+    /**
+     * Removes a registered System Code for the specified service.
+     *
+     * @param service The component name of the service
+     * @return whether the System Code was successfully removed.
+     */
+    public boolean unregisterSystemCodeForService(ComponentName service) throws RuntimeException {
+        if (service == null) {
+            throw new NullPointerException("service is null");
+        }
+        try {
+            return sService.removeSystemCodeForService(mContext.getUser().getIdentifier(), service);
+        } catch (RemoteException e) {
+            // Try one more time
+            recoverService();
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                return false;
+            }
+            try {
+                return sService.removeSystemCodeForService(mContext.getUser().getIdentifier(),
+                        service);
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to reach CardEmulationService.");
+                ee.rethrowAsRuntimeException();
+                return false;
+            }
+        }
+    }
+
+    /**
+     * Retrieves the current NFCID2 for the specified service.
+     *
+     * <p>Before calling {@link #setNfcid2ForService(ComponentName, String)},
+     * the NFCID2 contained in the Manifest file is returned. If "random" is specified
+     * in the Manifest file, a random number assigned by the system at installation time
+     * is returned. After setting an NFCID2
+     * with {@link #setNfcid2ForService(ComponentName, String)}, this NFCID2 is returned.
+     *
+     * @param service The component name of the service
+     * @return the current NFCID2
+     */
+    public String getNfcid2ForService(ComponentName service) throws RuntimeException {
+        if (service == null) {
+            throw new NullPointerException("service is null");
+        }
+        try {
+            return sService.getNfcid2ForService(mContext.getUser().getIdentifier(), service);
+        } catch (RemoteException e) {
+            // Try one more time
+            recoverService();
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                return null;
+            }
+            try {
+                return sService.getNfcid2ForService(mContext.getUser().getIdentifier(), service);
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to reach CardEmulationService.");
+                ee.rethrowAsRuntimeException();
+                return null;
+            }
+        }
+    }
+
+    /**
+     * Set a NFCID2 for the specified service.
+     *
+     * <p>The NFCID2 must be in range from "02FE000000000000" to "02FEFFFFFFFFFFFF".
+     *
+     * <p>If a NFCID2 was previously set for this service
+     * (either statically through the manifest, or dynamically by using this API),
+     * it will be replaced.
+     *
+     * <p>Note that you can only set the NFCID2 for a service that
+     * is running under the same UID as the caller of this API. Typically
+     * this means you need to call this from the same
+     * package as the service itself, though UIDs can also
+     * be shared between packages using shared UIDs.
+     *
+     * @param service The component name of the service
+     * @param nfcid2 The NFCID2 to be registered
+     * @return whether the setting was successful.
+     */
+    public boolean setNfcid2ForService(ComponentName service, String nfcid2)
+            throws RuntimeException {
+        if (service == null || nfcid2 == null) {
+            throw new NullPointerException("service or nfcid2 is null");
+        }
+        try {
+            return sService.setNfcid2ForService(mContext.getUser().getIdentifier(),
+                    service, nfcid2);
+        } catch (RemoteException e) {
+            // Try one more time
+            recoverService();
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                return false;
+            }
+            try {
+                return sService.setNfcid2ForService(mContext.getUser().getIdentifier(),
+                        service, nfcid2);
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to reach CardEmulationService.");
+                ee.rethrowAsRuntimeException();
+                return false;
+            }
+        }
+    }
+
+    /**
+     * Allows a foreground application to specify which card emulation service
+     * should be enabled while a specific Activity is in the foreground.
+     *
+     * <p>The specified HCE-F service is only enabled when the corresponding application is
+     * in the foreground and this method has been called. When the application is moved to
+     * the background, {@link #disableService(Activity)} is called, or
+     * NFCID2 or System Code is replaced, the HCE-F service is disabled.
+     *
+     * <p>The specified Activity must currently be in resumed state. A good
+     * paradigm is to call this method in your {@link Activity#onResume}, and to call
+     * {@link #disableService(Activity)} in your {@link Activity#onPause}.
+     *
+     * <p>Note that this preference is not persisted by the OS, and hence must be
+     * called every time the Activity is resumed.
+     *
+     * @param activity The activity which prefers this service to be invoked
+     * @param service The service to be preferred while this activity is in the foreground
+     * @return whether the registration was successful
+     */
+    public boolean enableService(Activity activity, ComponentName service) throws RuntimeException {
+        if (activity == null || service == null) {
+            throw new NullPointerException("activity or service is null");
+        }
+        // Verify the activity is in the foreground before calling into NfcService
+        if (!activity.isResumed()) {
+            throw new IllegalArgumentException("Activity must be resumed.");
+        }
+        try {
+            return sService.enableNfcFForegroundService(service);
+        } catch (RemoteException e) {
+            // Try one more time
+            recoverService();
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                return false;
+            }
+            try {
+                return sService.enableNfcFForegroundService(service);
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to reach CardEmulationService.");
+                ee.rethrowAsRuntimeException();
+                return false;
+            }
+        }
+    }
+
+    /**
+     * Disables the service for the specified Activity.
+     *
+     * <p>Note that the specified Activity must still be in resumed
+     * state at the time of this call. A good place to call this method
+     * is in your {@link Activity#onPause} implementation.
+     *
+     * @param activity The activity which the service was registered for
+     * @return true when successful
+     */
+    public boolean disableService(Activity activity) throws RuntimeException {
+        if (activity == null) {
+            throw new NullPointerException("activity is null");
+        }
+        if (!activity.isResumed()) {
+            throw new IllegalArgumentException("Activity must be resumed.");
+        }
+        try {
+            return sService.disableNfcFForegroundService();
+        } catch (RemoteException e) {
+            // Try one more time
+            recoverService();
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                return false;
+            }
+            try {
+                return sService.disableNfcFForegroundService();
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to reach CardEmulationService.");
+                ee.rethrowAsRuntimeException();
+                return false;
+            }
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public List<NfcFServiceInfo> getNfcFServices() {
+        try {
+            return sService.getNfcFServices(mContext.getUser().getIdentifier());
+        } catch (RemoteException e) {
+            // Try one more time
+            recoverService();
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                return null;
+            }
+            try {
+                return sService.getNfcFServices(mContext.getUser().getIdentifier());
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to reach CardEmulationService.");
+                return null;
+            }
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public int getMaxNumOfRegisterableSystemCodes() {
+        try {
+            return sService.getMaxNumOfRegisterableSystemCodes();
+        } catch (RemoteException e) {
+            // Try one more time
+            recoverService();
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                return -1;
+            }
+            try {
+                return sService.getMaxNumOfRegisterableSystemCodes();
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to reach CardEmulationService.");
+                return -1;
+            }
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public static boolean isValidSystemCode(String systemCode) {
+        if (systemCode == null) {
+            return false;
+        }
+        if (systemCode.length() != 4) {
+            Log.e(TAG, "System Code " + systemCode + " is not a valid System Code.");
+            return false;
+        }
+        // check if the value is between "4000" and "4FFF" (excluding "4*FF")
+        if (!systemCode.startsWith("4") || systemCode.toUpperCase().endsWith("FF")) {
+            Log.e(TAG, "System Code " + systemCode + " is not a valid System Code.");
+            return false;
+        }
+        try {
+            Integer.parseInt(systemCode, 16);
+        } catch (NumberFormatException e) {
+            Log.e(TAG, "System Code " + systemCode + " is not a valid System Code.");
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * @hide
+     */
+    public static boolean isValidNfcid2(String nfcid2) {
+        if (nfcid2 == null) {
+            return false;
+        }
+        if (nfcid2.length() != 16) {
+            Log.e(TAG, "NFCID2 " + nfcid2 + " is not a valid NFCID2.");
+            return false;
+        }
+        // check if the the value starts with "02FE"
+        if (!nfcid2.toUpperCase().startsWith("02FE")) {
+            Log.e(TAG, "NFCID2 " + nfcid2 + " is not a valid NFCID2.");
+            return false;
+        }
+        try {
+            Long.parseLong(nfcid2, 16);
+        } catch (NumberFormatException e) {
+            Log.e(TAG, "NFCID2 " + nfcid2 + " is not a valid NFCID2.");
+            return false;
+        }
+        return true;
+    }
+
+    void recoverService() {
+        NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
+        sService = adapter.getNfcFCardEmulationService();
+    }
+
+}
+
diff --git a/nfc/java/android/nfc/cardemulation/NfcFServiceInfo.aidl b/nfc/java/android/nfc/cardemulation/NfcFServiceInfo.aidl
new file mode 100644
index 0000000..56b98eb
--- /dev/null
+++ b/nfc/java/android/nfc/cardemulation/NfcFServiceInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc.cardemulation;
+
+parcelable NfcFServiceInfo;
diff --git a/nfc/java/android/nfc/cardemulation/NfcFServiceInfo.java b/nfc/java/android/nfc/cardemulation/NfcFServiceInfo.java
new file mode 100644
index 0000000..33bc169
--- /dev/null
+++ b/nfc/java/android/nfc/cardemulation/NfcFServiceInfo.java
@@ -0,0 +1,497 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**********************************************************************
+ * This file is not a part of the NFC mainline module                 *
+ * *******************************************************************/
+
+package android.nfc.cardemulation;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.content.ComponentName;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.graphics.drawable.Drawable;
+import android.nfc.Flags;
+import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Xml;
+import android.util.proto.ProtoOutputStream;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+ * Class to hold NfcF service info.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+public final class NfcFServiceInfo implements Parcelable {
+    static final String TAG = "NfcFServiceInfo";
+
+    private static final String DEFAULT_T3T_PMM = "FFFFFFFFFFFFFFFF";
+
+    /**
+     * The service that implements this
+     */
+    private final ResolveInfo mService;
+
+    /**
+     * Description of the service
+     */
+    private final String mDescription;
+
+    /**
+     * System Code of the service
+     */
+    private final String mSystemCode;
+
+    /**
+     * System Code of the service registered by API
+     */
+    private String mDynamicSystemCode;
+
+    /**
+     * NFCID2 of the service
+     */
+    private final String mNfcid2;
+
+    /**
+     * NFCID2 of the service registered by API
+     */
+    private String mDynamicNfcid2;
+
+    /**
+     * The uid of the package the service belongs to
+     */
+    private final int mUid;
+
+    /**
+     * LF_T3T_PMM of the service
+     */
+    private final String mT3tPmm;
+
+    /**
+     * @hide
+     */
+    public NfcFServiceInfo(ResolveInfo info, String description,
+            String systemCode, String dynamicSystemCode, String nfcid2, String dynamicNfcid2,
+            int uid, String t3tPmm) {
+        this.mService = info;
+        this.mDescription = description;
+        this.mSystemCode = systemCode;
+        this.mDynamicSystemCode = dynamicSystemCode;
+        this.mNfcid2 = nfcid2;
+        this.mDynamicNfcid2 = dynamicNfcid2;
+        this.mUid = uid;
+        this.mT3tPmm = t3tPmm;
+    }
+
+    /**
+     * Creates a new NfcFServiceInfo object.
+     *
+     * @param pm packageManager instance
+     * @param info app component info
+     * @throws XmlPullParserException If an error occurs parsing the element.
+     * @throws IOException If an error occurs reading the element.
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    public NfcFServiceInfo(@NonNull PackageManager pm, @NonNull ResolveInfo info)
+            throws XmlPullParserException, IOException {
+        ServiceInfo si = info.serviceInfo;
+        XmlResourceParser parser = null;
+        try {
+            parser = si.loadXmlMetaData(pm, HostNfcFService.SERVICE_META_DATA);
+            if (parser == null) {
+                throw new XmlPullParserException("No " + HostNfcFService.SERVICE_META_DATA +
+                        " meta-data");
+            }
+
+            int eventType = parser.getEventType();
+            while (eventType != XmlPullParser.START_TAG &&
+                    eventType != XmlPullParser.END_DOCUMENT) {
+                eventType = parser.next();
+            }
+
+            String tagName = parser.getName();
+            if (!"host-nfcf-service".equals(tagName)) {
+                throw new XmlPullParserException(
+                        "Meta-data does not start with <host-nfcf-service> tag");
+            }
+
+            Resources res = pm.getResourcesForApplication(si.applicationInfo);
+            AttributeSet attrs = Xml.asAttributeSet(parser);
+            TypedArray sa = res.obtainAttributes(attrs,
+                    com.android.internal.R.styleable.HostNfcFService);
+            mService = info;
+            mDescription = sa.getString(
+                    com.android.internal.R.styleable.HostNfcFService_description);
+            mDynamicSystemCode = null;
+            mDynamicNfcid2 = null;
+            sa.recycle();
+
+            String systemCode = null;
+            String nfcid2 = null;
+            String t3tPmm = null;
+            final int depth = parser.getDepth();
+
+            while (((eventType = parser.next()) != XmlPullParser.END_TAG ||
+                    parser.getDepth() > depth) && eventType != XmlPullParser.END_DOCUMENT) {
+                tagName = parser.getName();
+                if (eventType == XmlPullParser.START_TAG &&
+                        "system-code-filter".equals(tagName) && systemCode == null) {
+                    final TypedArray a = res.obtainAttributes(attrs,
+                            com.android.internal.R.styleable.SystemCodeFilter);
+                    systemCode = a.getString(
+                            com.android.internal.R.styleable.SystemCodeFilter_name).toUpperCase();
+                    if (!isValidSystemCode(systemCode) &&
+                            !systemCode.equalsIgnoreCase("NULL")) {
+                        Log.e(TAG, "Invalid System Code: " + systemCode);
+                        systemCode = null;
+                    }
+                    a.recycle();
+                } else if (eventType == XmlPullParser.START_TAG &&
+                        "nfcid2-filter".equals(tagName) && nfcid2 == null) {
+                    final TypedArray a = res.obtainAttributes(attrs,
+                            com.android.internal.R.styleable.Nfcid2Filter);
+                    nfcid2 = a.getString(
+                            com.android.internal.R.styleable.Nfcid2Filter_name).toUpperCase();
+                    if (!nfcid2.equalsIgnoreCase("RANDOM") &&
+                            !nfcid2.equalsIgnoreCase("NULL") &&
+                            !isValidNfcid2(nfcid2)) {
+                        Log.e(TAG, "Invalid NFCID2: " + nfcid2);
+                        nfcid2 = null;
+                    }
+                    a.recycle();
+                } else if (eventType == XmlPullParser.START_TAG && tagName.equals("t3tPmm-filter")
+                        && t3tPmm == null) {
+                    final TypedArray a = res.obtainAttributes(attrs,
+                            com.android.internal.R.styleable.T3tPmmFilter);
+                    t3tPmm = a.getString(
+                            com.android.internal.R.styleable.T3tPmmFilter_name).toUpperCase();
+                    a.recycle();
+                }
+            }
+            mSystemCode = (systemCode == null ? "NULL" : systemCode);
+            mNfcid2 = (nfcid2 == null ? "NULL" : nfcid2);
+            mT3tPmm = (t3tPmm == null ? DEFAULT_T3T_PMM : t3tPmm);
+        } catch (NameNotFoundException e) {
+            throw new XmlPullParserException("Unable to create context for: " + si.packageName);
+        } finally {
+            if (parser != null) parser.close();
+        }
+        // Set uid
+        mUid = si.applicationInfo.uid;
+    }
+
+    /**
+     * Returns the app component corresponding to this NFCF service.
+     *
+     * @return app component for this service
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    @NonNull
+    public ComponentName getComponent() {
+        return new ComponentName(mService.serviceInfo.packageName,
+                mService.serviceInfo.name);
+    }
+
+    /**
+     * Returns the system code corresponding to this service.
+     *
+     * @return system code for this service
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    @NonNull
+    public String getSystemCode() {
+        return (mDynamicSystemCode == null ? mSystemCode : mDynamicSystemCode);
+    }
+
+    /**
+     * Add or replace a system code to this service.
+     * @param systemCode system code to set or replace
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    public void setDynamicSystemCode(@NonNull String systemCode) {
+        mDynamicSystemCode = systemCode;
+    }
+
+    /**
+     * Returns NFC ID2.
+     *
+     * @return nfc id2 to return
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    @NonNull
+    public String getNfcid2() {
+        return (mDynamicNfcid2 == null ? mNfcid2 : mDynamicNfcid2);
+    }
+
+    /**
+     * Set or replace NFC ID2
+     *
+     * @param nfcid2 NFC ID2 string
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    public void setDynamicNfcid2(@NonNull String nfcid2) {
+        mDynamicNfcid2 = nfcid2;
+    }
+
+    /**
+     * Returns description of service.
+     * @return user readable description of service
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    @NonNull
+    public String getDescription() {
+        return mDescription;
+    }
+
+    /**
+     * Returns uid of service.
+     * @return uid of the service
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    public int getUid() {
+        return mUid;
+    }
+
+    /**
+     * Returns LF_T3T_PMM of the service
+     * @return returns LF_T3T_PMM of the service
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    @NonNull
+    public String getT3tPmm() {
+        return mT3tPmm;
+    }
+
+    /**
+     * Load application label for this service.
+     * @param pm packagemanager instance
+     * @return label name corresponding to service
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    @NonNull
+    public CharSequence loadLabel(@NonNull PackageManager pm) {
+        return mService.loadLabel(pm);
+    }
+
+    /**
+     * Load application icon for this service.
+     * @param pm packagemanager instance
+     * @return app icon corresponding to service
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    @NonNull
+    public Drawable loadIcon(@NonNull PackageManager pm) {
+        return mService.loadIcon(pm);
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder out = new StringBuilder("NfcFService: ");
+        out.append(getComponent());
+        out.append(", UID: " + mUid);
+        out.append(", description: " + mDescription);
+        out.append(", System Code: " + mSystemCode);
+        if (mDynamicSystemCode != null) {
+            out.append(", dynamic System Code: " + mDynamicSystemCode);
+        }
+        out.append(", NFCID2: " + mNfcid2);
+        if (mDynamicNfcid2 != null) {
+            out.append(", dynamic NFCID2: " + mDynamicNfcid2);
+        }
+        out.append(", T3T PMM:" + mT3tPmm);
+        return out.toString();
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (this == o) return true;
+        if (!(o instanceof NfcFServiceInfo)) return false;
+        NfcFServiceInfo thatService = (NfcFServiceInfo) o;
+
+        if (!thatService.getComponent().equals(this.getComponent())) return false;
+        if (thatService.getUid() != this.getUid()) return false;
+        if (!thatService.mSystemCode.equalsIgnoreCase(this.mSystemCode)) return false;
+        if (!thatService.mNfcid2.equalsIgnoreCase(this.mNfcid2)) return false;
+        if (!thatService.mT3tPmm.equalsIgnoreCase(this.mT3tPmm)) return false;
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        return getComponent().hashCode();
+    }
+
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        mService.writeToParcel(dest, flags);
+        dest.writeString(mDescription);
+        dest.writeString(mSystemCode);
+        dest.writeInt(mDynamicSystemCode != null ? 1 : 0);
+        if (mDynamicSystemCode != null) {
+            dest.writeString(mDynamicSystemCode);
+        }
+        dest.writeString(mNfcid2);
+        dest.writeInt(mDynamicNfcid2 != null ? 1 : 0);
+        if (mDynamicNfcid2 != null) {
+            dest.writeString(mDynamicNfcid2);
+        }
+        dest.writeInt(mUid);
+        dest.writeString(mT3tPmm);
+    };
+
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    public static final @NonNull Parcelable.Creator<NfcFServiceInfo> CREATOR =
+            new Parcelable.Creator<NfcFServiceInfo>() {
+        @Override
+        public NfcFServiceInfo createFromParcel(Parcel source) {
+            ResolveInfo info = ResolveInfo.CREATOR.createFromParcel(source);
+            String description = source.readString();
+            String systemCode = source.readString();
+            String dynamicSystemCode = null;
+            if (source.readInt() != 0) {
+                dynamicSystemCode = source.readString();
+            }
+            String nfcid2 = source.readString();
+            String dynamicNfcid2 = null;
+            if (source.readInt() != 0) {
+                dynamicNfcid2 = source.readString();
+            }
+            int uid = source.readInt();
+            String t3tPmm = source.readString();
+            NfcFServiceInfo service = new NfcFServiceInfo(info, description,
+                    systemCode, dynamicSystemCode, nfcid2, dynamicNfcid2, uid, t3tPmm);
+            return service;
+        }
+
+        @Override
+        public NfcFServiceInfo[] newArray(int size) {
+            return new NfcFServiceInfo[size];
+        }
+    };
+
+    /**
+     * Dump contents of the service for debugging.
+     * @param fd parcelfiledescriptor instance
+     * @param pw printwriter instance
+     * @param args args for dumping
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    public void dump(@NonNull ParcelFileDescriptor fd, @NonNull PrintWriter pw,
+                     @NonNull String[] args) {
+        pw.println("    " + getComponent()
+                + " (Description: " + getDescription() + ")"
+                + " (UID: " + getUid() + ")");
+        pw.println("    System Code: " + getSystemCode());
+        pw.println("    NFCID2: " + getNfcid2());
+        pw.println("    T3tPmm: " + getT3tPmm());
+    }
+
+    /**
+     * Dump debugging info as NfcFServiceInfoProto.
+     *
+     * If the output belongs to a sub message, the caller is responsible for wrapping this function
+     * between {@link ProtoOutputStream#start(long)} and {@link ProtoOutputStream#end(long)}.
+     *
+     * @param proto the ProtoOutputStream to write to
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    public void dumpDebug(@NonNull ProtoOutputStream proto) {
+        getComponent().dumpDebug(proto, NfcFServiceInfoProto.COMPONENT_NAME);
+        proto.write(NfcFServiceInfoProto.DESCRIPTION, getDescription());
+        proto.write(NfcFServiceInfoProto.SYSTEM_CODE, getSystemCode());
+        proto.write(NfcFServiceInfoProto.NFCID2, getNfcid2());
+        proto.write(NfcFServiceInfoProto.T3T_PMM, getT3tPmm());
+    }
+
+    /**
+     * Copied over from {@link NfcFCardEmulation#isValidSystemCode(String)}
+     * @hide
+     */
+    private static boolean isValidSystemCode(String systemCode) {
+        if (systemCode == null) {
+            return false;
+        }
+        if (systemCode.length() != 4) {
+            Log.e(TAG, "System Code " + systemCode + " is not a valid System Code.");
+            return false;
+        }
+        // check if the value is between "4000" and "4FFF" (excluding "4*FF")
+        if (!systemCode.startsWith("4") || systemCode.toUpperCase().endsWith("FF")) {
+            Log.e(TAG, "System Code " + systemCode + " is not a valid System Code.");
+            return false;
+        }
+        try {
+            Integer.parseInt(systemCode, 16);
+        } catch (NumberFormatException e) {
+            Log.e(TAG, "System Code " + systemCode + " is not a valid System Code.");
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Copied over from {@link NfcFCardEmulation#isValidNfcid2(String)}
+     * @hide
+     */
+    private static boolean isValidNfcid2(String nfcid2) {
+        if (nfcid2 == null) {
+            return false;
+        }
+        if (nfcid2.length() != 16) {
+            Log.e(TAG, "NFCID2 " + nfcid2 + " is not a valid NFCID2.");
+            return false;
+        }
+        // check if the the value starts with "02FE"
+        if (!nfcid2.toUpperCase().startsWith("02FE")) {
+            Log.e(TAG, "NFCID2 " + nfcid2 + " is not a valid NFCID2.");
+            return false;
+        }
+        try {
+            Long.parseLong(nfcid2, 16);
+        } catch (NumberFormatException e) {
+            Log.e(TAG, "NFCID2 " + nfcid2 + " is not a valid NFCID2.");
+            return false;
+        }
+        return true;
+    }
+}
diff --git a/nfc/java/android/nfc/cardemulation/OWNERS b/nfc/java/android/nfc/cardemulation/OWNERS
new file mode 100644
index 0000000..35e9713
--- /dev/null
+++ b/nfc/java/android/nfc/cardemulation/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 48448
+include platform/packages/apps/Nfc:/OWNERS
diff --git a/nfc/java/android/nfc/cardemulation/OffHostApduService.java b/nfc/java/android/nfc/cardemulation/OffHostApduService.java
new file mode 100644
index 0000000..2286e84
--- /dev/null
+++ b/nfc/java/android/nfc/cardemulation/OffHostApduService.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc.cardemulation;
+
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.app.Service;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.IBinder;
+
+/**
+ * <p>OffHostApduService is a convenience {@link Service} class that can be
+ * extended to describe one or more NFC applications that are residing
+ * off-host, for example on an embedded secure element or a UICC.
+ *
+ * <div class="special reference">
+ * <h3>Developer Guide</h3>
+ * For a general introduction into the topic of card emulation,
+ * please read the <a href="{@docRoot}guide/topics/connectivity/nfc/hce.html">
+ * NFC card emulation developer guide.</a></p>
+ * </div>
+ *
+ * <h3>NFC Protocols</h3>
+ * <p>Off-host applications represented by this class are based on the NFC-Forum ISO-DEP
+ * protocol (based on ISO/IEC 14443-4) and support processing
+ * command Application Protocol Data Units (APDUs) as
+ * defined in the ISO/IEC 7816-4 specification.
+ *
+ * <h3>Service selection</h3>
+ * <p>When a remote NFC device wants to talk to your
+ * off-host NFC application, it sends a so-called
+ * "SELECT AID" APDU as defined in the ISO/IEC 7816-4 specification.
+ * The AID is an application identifier defined in ISO/IEC 7816-4.
+ *
+ * <p>The registration procedure for AIDs is defined in the
+ * ISO/IEC 7816-5 specification. If you don't want to register an
+ * AID, you are free to use AIDs in the proprietary range:
+ * bits 8-5 of the first byte must each be set to '1'. For example,
+ * "0xF00102030405" is a proprietary AID. If you do use proprietary
+ * AIDs, it is recommended to choose an AID of at least 6 bytes,
+ * to reduce the risk of collisions with other applications that
+ * might be using proprietary AIDs as well.
+ *
+ * <h3>AID groups</h3>
+ * <p>In some cases, an off-host environment may need to register multiple AIDs
+ * to implement a certain application, and it needs to be sure
+ * that it is the default handler for all of these AIDs (as opposed
+ * to some AIDs in the group going to another service).
+ *
+ * <p>An AID group is a list of AIDs that should be considered as
+ * belonging together by the OS. For all AIDs in an AID group, the
+ * OS will guarantee one of the following:
+ * <ul>
+ * <li>All AIDs in the group are routed to the off-host execution environment
+ * <li>No AIDs in the group are routed to the off-host execution environment
+ * </ul>
+ * In other words, there is no in-between state, where some AIDs
+ * in the group can be routed to this off-host execution environment,
+ * and some to another or a host-based {@link HostApduService}.
+ * <h3>AID groups and categories</h3>
+ * <p>Each AID group can be associated with a category. This allows
+ * the Android OS to classify services, and it allows the user to
+ * set defaults at the category level instead of the AID level.
+ *
+ * <p>You can use
+ * {@link CardEmulation#isDefaultServiceForCategory(android.content.ComponentName, String)}
+ * to determine if your off-host service is the default handler for a category.
+ *
+ * <p>In this version of the platform, the only known categories
+ * are {@link CardEmulation#CATEGORY_PAYMENT} and {@link CardEmulation#CATEGORY_OTHER}.
+ * AID groups without a category, or with a category that is not recognized
+ * by the current platform version, will automatically be
+ * grouped into the {@link CardEmulation#CATEGORY_OTHER} category.
+ *
+ * <h3>Service AID registration</h3>
+ * <p>To tell the platform which AIDs
+ * reside off-host and are managed by this service, a {@link #SERVICE_META_DATA}
+ * entry must be included in the declaration of the service. An
+ * example of a OffHostApduService manifest declaration is shown below:
+ * <pre> &lt;service android:name=".MyOffHostApduService" android:exported="true" android:permission="android.permission.BIND_NFC_SERVICE"&gt;
+ *     &lt;intent-filter&gt;
+ *         &lt;action android:name="android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE"/&gt;
+ *     &lt;/intent-filter&gt;
+ *     &lt;meta-data android:name="android.nfc.cardemulation.off_host_apdu_ervice" android:resource="@xml/apduservice"/&gt;
+ * &lt;/service&gt;</pre>
+ *
+ * This meta-data tag points to an apduservice.xml file.
+ * An example of this file with a single AID group declaration is shown below:
+ * <pre>
+ * &lt;offhost-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
+ *           android:description="@string/servicedesc"&gt;
+ *       &lt;aid-group android:description="@string/subscription" android:category="other">
+ *           &lt;aid-filter android:name="F0010203040506"/&gt;
+ *           &lt;aid-filter android:name="F0394148148100"/&gt;
+ *       &lt;/aid-group&gt;
+ * &lt;/offhost-apdu-service&gt;
+ * </pre>
+ *
+ * <p>The {@link android.R.styleable#OffHostApduService &lt;offhost-apdu-service&gt;} is required
+ * to contain a
+ * {@link android.R.styleable#OffHostApduService_description &lt;android:description&gt;}
+ * attribute that contains a user-friendly description of the service that may be shown in UI.
+ *
+ * <p>The {@link android.R.styleable#OffHostApduService &lt;offhost-apdu-service&gt;} must
+ * contain one or more {@link android.R.styleable#AidGroup &lt;aid-group&gt;} tags.
+ * Each {@link android.R.styleable#AidGroup &lt;aid-group&gt;} must contain one or
+ * more {@link android.R.styleable#AidFilter &lt;aid-filter&gt;} tags, each of which
+ * contains a single AID. The AID must be specified in hexadecimal format, and contain
+ * an even number of characters.
+ *
+ * <p>This registration will allow the service to be included
+ * as an option for being the default handler for categories.
+ * The Android OS will take care of correctly
+ * routing the AIDs to the off-host execution environment,
+ * based on which service the user has selected to be the handler for a certain category.
+ *
+ * <p>The service may define additional actions outside of the
+ * Android namespace that provide further interaction with
+ * the off-host execution environment.
+ *
+ * <p class="note">Use of this class requires the
+ * {@link PackageManager#FEATURE_NFC_HOST_CARD_EMULATION} to be present
+ * on the device.
+ */
+public abstract class OffHostApduService extends Service {
+    /**
+     * The {@link Intent} action that must be declared as handled by the service.
+     */
+    @SdkConstant(SdkConstantType.SERVICE_ACTION)
+    public static final String SERVICE_INTERFACE =
+            "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE";
+
+    /**
+     * The name of the meta-data element that contains
+     * more information about this service.
+     */
+    public static final String SERVICE_META_DATA =
+            "android.nfc.cardemulation.off_host_apdu_service";
+
+    /**
+     * The Android platform itself will not bind to this service,
+     * but merely uses its declaration to keep track of what AIDs
+     * the service is interested in. This information is then used
+     * to present the user with a list of applications that can handle
+     * an AID, as well as correctly route those AIDs either to the host (in case
+     * the user preferred a {@link HostApduService}), or to an off-host
+     * execution environment (in case the user preferred a {@link OffHostApduService}.
+     *
+     * Implementers may define additional actions outside of the
+     * Android namespace that allow further interactions with
+     * the off-host execution environment. Such implementations
+     * would need to override this method.
+     */
+    public abstract IBinder onBind(Intent intent);
+}
diff --git a/nfc/java/android/nfc/cardemulation/Utils.java b/nfc/java/android/nfc/cardemulation/Utils.java
new file mode 100644
index 0000000..202e1cf
--- /dev/null
+++ b/nfc/java/android/nfc/cardemulation/Utils.java
@@ -0,0 +1,37 @@
+/*
+ * 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 android.nfc.cardemulation;
+
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.content.ComponentNameProto;
+import android.util.proto.ProtoOutputStream;
+
+/** @hide */
+public final class Utils {
+    private Utils() {
+    }
+
+    /** Copied from {@link ComponentName#dumpDebug(ProtoOutputStream, long)} */
+    public static void dumpDebugComponentName(
+            @NonNull ComponentName componentName, @NonNull ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+        proto.write(ComponentNameProto.PACKAGE_NAME, componentName.getPackageName());
+        proto.write(ComponentNameProto.CLASS_NAME, componentName.getClassName());
+        proto.end(token);
+    }
+}
diff --git a/nfc/java/android/nfc/dta/NfcDta.java b/nfc/java/android/nfc/dta/NfcDta.java
new file mode 100644
index 0000000..8801662
--- /dev/null
+++ b/nfc/java/android/nfc/dta/NfcDta.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2017 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 android.nfc.dta;
+
+import android.content.Context;
+import android.nfc.INfcDta;
+import android.nfc.NfcAdapter;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.HashMap;
+
+/**
+ * This class provides the primary API for DTA operations.
+ * @hide
+ */
+public final class NfcDta {
+    private static final String TAG = "NfcDta";
+
+    private static INfcDta sService;
+    private static HashMap<Context, NfcDta> sNfcDtas = new HashMap<Context, NfcDta>();
+
+    private final Context mContext;
+
+    private NfcDta(Context context, INfcDta service) {
+        mContext = context.getApplicationContext();
+        sService = service;
+    }
+
+    /**
+     * Helper to get an instance of this class.
+     *
+     * @param adapter A reference to an NfcAdapter object.
+     * @return
+     */
+    public static synchronized NfcDta getInstance(NfcAdapter adapter) {
+        if (adapter == null) throw new NullPointerException("NfcAdapter is null");
+        Context context = adapter.getContext();
+        if (context == null) {
+            Log.e(TAG, "NfcAdapter context is null.");
+            throw new UnsupportedOperationException();
+        }
+
+        NfcDta manager = sNfcDtas.get(context);
+        if (manager == null) {
+            INfcDta service = adapter.getNfcDtaInterface();
+            if (service == null) {
+                Log.e(TAG, "This device does not implement the INfcDta interface.");
+                throw new UnsupportedOperationException();
+            }
+            manager = new NfcDta(context, service);
+            sNfcDtas.put(context, manager);
+        }
+        return manager;
+    }
+
+    /**
+     * Enables DTA mode
+     *
+     * @return true/false if enabling was successful
+     */
+    public boolean enableDta() {
+        try {
+            sService.enableDta();
+        } catch (RemoteException e) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Disables DTA mode
+     *
+     * @return true/false if disabling was successful
+     */
+    public boolean disableDta() {
+        try {
+            sService.disableDta();
+        } catch (RemoteException e) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Enables Server
+     *
+     * @return true/false if enabling was successful
+     */
+    public boolean enableServer(String serviceName, int serviceSap, int miu,
+            int rwSize, int testCaseId) {
+        try {
+            return sService.enableServer(serviceName, serviceSap, miu, rwSize, testCaseId);
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Disables Server
+     *
+     * @return true/false if disabling was successful
+     */
+    public boolean disableServer() {
+        try {
+            sService.disableServer();
+        } catch (RemoteException e) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Enables Client
+     *
+     * @return true/false if enabling was successful
+     */
+    public boolean enableClient(String serviceName, int miu, int rwSize,
+            int testCaseId) {
+        try {
+            return sService.enableClient(serviceName, miu, rwSize, testCaseId);
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Disables client
+     *
+     * @return true/false if disabling was successful
+     */
+    public boolean disableClient() {
+        try {
+            sService.disableClient();
+        } catch (RemoteException e) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Registers Message Service
+     *
+     * @return true/false if registration was successful
+     */
+    public boolean registerMessageService(String msgServiceName) {
+        try {
+            return sService.registerMessageService(msgServiceName);
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+}
diff --git a/nfc/java/android/nfc/dta/OWNERS b/nfc/java/android/nfc/dta/OWNERS
new file mode 100644
index 0000000..35e9713
--- /dev/null
+++ b/nfc/java/android/nfc/dta/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 48448
+include platform/packages/apps/Nfc:/OWNERS
diff --git a/nfc/java/android/nfc/flags.aconfig b/nfc/java/android/nfc/flags.aconfig
new file mode 100644
index 0000000..01a4570
--- /dev/null
+++ b/nfc/java/android/nfc/flags.aconfig
@@ -0,0 +1,71 @@
+package: "android.nfc"
+
+flag {
+    name: "enable_nfc_mainline"
+    namespace: "nfc"
+    description: "Flag for NFC mainline changes"
+    bug: "292140387"
+}
+
+flag {
+    name: "enable_nfc_reader_option"
+    namespace: "nfc"
+    description: "Flag for NFC reader option API changes"
+    bug: "291187960"
+}
+
+flag {
+    name: "enable_nfc_user_restriction"
+    namespace: "nfc"
+    description: "Flag for NFC user restriction"
+    bug: "291187960"
+}
+
+flag {
+    name: "nfc_observe_mode"
+    namespace: "nfc"
+    description: "Enable NFC Observe Mode"
+    bug: "294217286"
+}
+
+flag {
+    name: "nfc_read_polling_loop"
+    namespace: "nfc"
+    description: "Enable NFC Polling Loop Notifications"
+    bug: "294217286"
+}
+
+flag {
+    name: "nfc_observe_mode_st_shim"
+    namespace: "nfc"
+    description: "Enable NFC Observe Mode ST shim"
+    bug: "294217286"
+}
+
+flag {
+    name: "nfc_read_polling_loop_st_shim"
+    namespace: "nfc"
+    description: "Enable NFC Polling Loop Notifications ST shim"
+    bug: "294217286"
+}
+
+flag {
+    name: "enable_tag_detection_broadcasts"
+    namespace: "nfc"
+    description: "Enable sending broadcasts to Wallet role holder when a tag enters/leaves the field."
+    bug: "306203494"
+}
+
+flag {
+    name: "enable_nfc_charging"
+    namespace: "nfc"
+    description: "Flag for NFC charging changes"
+    bug: "292143899"
+}
+
+flag {
+    name: "enable_nfc_set_discovery_tech"
+    namespace: "nfc"
+    description: "Flag for NFC set discovery tech API"
+    bug: "300351519"
+}
diff --git a/nfc/java/android/nfc/package.html b/nfc/java/android/nfc/package.html
new file mode 100644
index 0000000..55c1d16
--- /dev/null
+++ b/nfc/java/android/nfc/package.html
@@ -0,0 +1,33 @@
+<HTML>
+<BODY>
+<p>Provides access to Near Field Communication (NFC) functionality, allowing applications to read
+NDEF message in NFC tags. A "tag" may actually be another device that appears as a tag.</p>
+
+<p>For more information, see the
+<a href="{@docRoot}guide/topics/connectivity/nfc/index.html">Near Field Communication</a> guide.</p>
+{@more}
+
+<p>Here's a summary of the classes:</p>
+
+<dl>
+  <dt>{@link android.nfc.NfcManager}</dt>
+  <dd>This is the high level manager, used to obtain this device's {@link android.nfc.NfcAdapter}. You can
+acquire an instance using {@link android.content.Context#getSystemService}.</dd>
+  <dt>{@link android.nfc.NfcAdapter}</dt>
+  <dd>This represents the device's NFC adapter, which is your entry-point to performing NFC
+operations. You can acquire an instance with {@link android.nfc.NfcManager#getDefaultAdapter}, or
+{@link android.nfc.NfcAdapter#getDefaultAdapter(android.content.Context)}.</dd>
+  <dt>{@link android.nfc.NdefMessage}</dt>
+  <dd>Represents an NDEF data message, which is the standard format in which "records"
+carrying data are transmitted between devices and tags. Your application can receive these
+messages from an {@link android.nfc.NfcAdapter#ACTION_TAG_DISCOVERED} intent.</dd>
+  <dt>{@link android.nfc.NdefRecord}</dt>
+  <dd>Represents a record, which is delivered in a {@link android.nfc.NdefMessage} and describes the
+type of data being shared and carries the data itself.</dd>
+</dl>
+
+<p class="note"><strong>Note:</strong>
+Not all Android-powered devices provide NFC functionality.</p>
+
+</BODY>
+</HTML>
diff --git a/nfc/java/android/nfc/tech/BasicTagTechnology.java b/nfc/java/android/nfc/tech/BasicTagTechnology.java
new file mode 100644
index 0000000..ae468fe
--- /dev/null
+++ b/nfc/java/android/nfc/tech/BasicTagTechnology.java
@@ -0,0 +1,161 @@
+/*
+ * 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 android.nfc.tech;
+
+import android.nfc.ErrorCodes;
+import android.nfc.Tag;
+import android.nfc.TransceiveResult;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * A base class for tag technologies that are built on top of transceive().
+ */
+abstract class BasicTagTechnology implements TagTechnology {
+    private static final String TAG = "NFC";
+
+    final Tag mTag;
+
+    boolean mIsConnected;
+    int mSelectedTechnology;
+
+    BasicTagTechnology(Tag tag, int tech) throws RemoteException {
+        mTag = tag;
+        mSelectedTechnology = tech;
+    }
+
+    @Override
+    public Tag getTag() {
+        return mTag;
+    }
+
+    /** Internal helper to throw IllegalStateException if the technology isn't connected */
+    void checkConnected() {
+       if ((mTag.getConnectedTechnology() != mSelectedTechnology) ||
+               (mTag.getConnectedTechnology() == -1)) {
+           throw new IllegalStateException("Call connect() first!");
+       }
+    }
+
+    @Override
+    public boolean isConnected() {
+        if (!mIsConnected) {
+            return false;
+        }
+
+        try {
+            return mTag.getTagService().isPresent(mTag.getServiceHandle());
+        } catch (RemoteException e) {
+            Log.e(TAG, "NFC service dead", e);
+            return false;
+        }
+    }
+
+    @Override
+    public void connect() throws IOException {
+        try {
+            int errorCode = mTag.getTagService().connect(mTag.getServiceHandle(),
+                    mSelectedTechnology);
+
+            if (errorCode == ErrorCodes.SUCCESS) {
+                // Store this in the tag object
+                if (!mTag.setConnectedTechnology(mSelectedTechnology)) {
+                    Log.e(TAG, "Close other technology first!");
+                    throw new IOException("Only one TagTechnology can be connected at a time.");
+                }
+                mIsConnected = true;
+            } else if (errorCode == ErrorCodes.ERROR_NOT_SUPPORTED) {
+                throw new UnsupportedOperationException("Connecting to " +
+                        "this technology is not supported by the NFC " +
+                        "adapter.");
+            } else {
+                throw new IOException();
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "NFC service dead", e);
+            throw new IOException("NFC service died");
+        }
+    }
+
+    /** @hide */
+    @Override
+    public void reconnect() throws IOException {
+        if (!mIsConnected) {
+            throw new IllegalStateException("Technology not connected yet");
+        }
+
+        try {
+            int errorCode = mTag.getTagService().reconnect(mTag.getServiceHandle());
+
+            if (errorCode != ErrorCodes.SUCCESS) {
+                mIsConnected = false;
+                mTag.setTechnologyDisconnected();
+                throw new IOException();
+            }
+        } catch (RemoteException e) {
+            mIsConnected = false;
+            mTag.setTechnologyDisconnected();
+            Log.e(TAG, "NFC service dead", e);
+            throw new IOException("NFC service died");
+        }
+    }
+
+    @Override
+    public void close() throws IOException {
+        try {
+            /* Note that we don't want to physically disconnect the tag,
+             * but just reconnect to it to reset its state
+             */
+            mTag.getTagService().resetTimeouts();
+            mTag.getTagService().reconnect(mTag.getServiceHandle());
+        } catch (RemoteException e) {
+            Log.e(TAG, "NFC service dead", e);
+        } finally {
+            mIsConnected = false;
+            mTag.setTechnologyDisconnected();
+        }
+    }
+
+    /** Internal getMaxTransceiveLength() */
+    int getMaxTransceiveLengthInternal() {
+        try {
+            return mTag.getTagService().getMaxTransceiveLength(mSelectedTechnology);
+        } catch (RemoteException e) {
+            Log.e(TAG, "NFC service dead", e);
+            return 0;
+        }
+    }
+    /** Internal transceive */
+    byte[] transceive(byte[] data, boolean raw) throws IOException {
+        checkConnected();
+
+        try {
+            TransceiveResult result = mTag.getTagService().transceive(mTag.getServiceHandle(),
+                    data, raw);
+            if (result == null) {
+                throw new IOException("transceive failed");
+            } else {
+                return result.getResponseOrThrow();
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "NFC service dead", e);
+            throw new IOException("NFC service died");
+        }
+    }
+}
diff --git a/nfc/java/android/nfc/tech/IsoDep.java b/nfc/java/android/nfc/tech/IsoDep.java
new file mode 100644
index 0000000..0ba0c5a
--- /dev/null
+++ b/nfc/java/android/nfc/tech/IsoDep.java
@@ -0,0 +1,209 @@
+/*
+ * 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 android.nfc.tech;
+
+import android.nfc.ErrorCodes;
+import android.nfc.Tag;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * Provides access to ISO-DEP (ISO 14443-4) properties and I/O operations on a {@link Tag}.
+ *
+ * <p>Acquire an {@link IsoDep} object using {@link #get}.
+ * <p>The primary ISO-DEP I/O operation is {@link #transceive}. Applications must
+ * implement their own protocol stack on top of {@link #transceive}.
+ * <p>Tags that enumerate the {@link IsoDep} technology in {@link Tag#getTechList}
+ * will also enumerate
+ * {@link NfcA} or {@link NfcB} (since IsoDep builds on top of either of these).
+ *
+ * <p class="note"><strong>Note:</strong> Methods that perform I/O operations
+ * require the {@link android.Manifest.permission#NFC} permission.
+ */
+public final class IsoDep extends BasicTagTechnology {
+    private static final String TAG = "NFC";
+
+    /** @hide */
+    public static final String EXTRA_HI_LAYER_RESP = "hiresp";
+    /** @hide */
+    public static final String EXTRA_HIST_BYTES = "histbytes";
+
+    private byte[] mHiLayerResponse = null;
+    private byte[] mHistBytes = null;
+
+    /**
+     * Get an instance of {@link IsoDep} for the given tag.
+     * <p>Does not cause any RF activity and does not block.
+     * <p>Returns null if {@link IsoDep} was not enumerated in {@link Tag#getTechList}.
+     * This indicates the tag does not support ISO-DEP.
+     *
+     * @param tag an ISO-DEP compatible tag
+     * @return ISO-DEP object
+     */
+    public static IsoDep get(Tag tag) {
+        if (!tag.hasTech(TagTechnology.ISO_DEP)) return null;
+        try {
+            return new IsoDep(tag);
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /** @hide */
+    public IsoDep(Tag tag)
+            throws RemoteException {
+        super(tag, TagTechnology.ISO_DEP);
+        Bundle extras = tag.getTechExtras(TagTechnology.ISO_DEP);
+        if (extras != null) {
+            mHiLayerResponse = extras.getByteArray(EXTRA_HI_LAYER_RESP);
+            mHistBytes = extras.getByteArray(EXTRA_HIST_BYTES);
+        }
+    }
+
+    /**
+     * Set the timeout of {@link #transceive} in milliseconds.
+     * <p>The timeout only applies to ISO-DEP {@link #transceive}, and is
+     * reset to a default value when {@link #close} is called.
+     * <p>Setting a longer timeout may be useful when performing
+     * transactions that require a long processing time on the tag
+     * such as key generation.
+     *
+     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+     *
+     * @param timeout timeout value in milliseconds
+     * @throws SecurityException if the tag object is reused after the tag has left the field
+     */
+    public void setTimeout(int timeout) {
+        try {
+            int err = mTag.getTagService().setTimeout(TagTechnology.ISO_DEP, timeout);
+            if (err != ErrorCodes.SUCCESS) {
+                throw new IllegalArgumentException("The supplied timeout is not valid");
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "NFC service dead", e);
+        }
+    }
+
+    /**
+     * Get the current timeout for {@link #transceive} in milliseconds.
+     *
+     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+     *
+     * @return timeout value in milliseconds
+     * @throws SecurityException if the tag object is reused after the tag has left the field
+     */
+    public int getTimeout() {
+        try {
+            return mTag.getTagService().getTimeout(TagTechnology.ISO_DEP);
+        } catch (RemoteException e) {
+            Log.e(TAG, "NFC service dead", e);
+            return 0;
+        }
+    }
+
+    /**
+     * Return the ISO-DEP historical bytes for {@link NfcA} tags.
+     * <p>Does not cause any RF activity and does not block.
+     * <p>The historical bytes can be used to help identify a tag. They are present
+     * only on {@link IsoDep} tags that are based on {@link NfcA} RF technology.
+     * If this tag is not {@link NfcA} then null is returned.
+     * <p>In ISO 14443-4 terminology, the historical bytes are a subset of the RATS
+     * response.
+     *
+     * @return ISO-DEP historical bytes, or null if this is not a {@link NfcA} tag
+     */
+    public byte[] getHistoricalBytes() {
+        return mHistBytes;
+    }
+
+    /**
+     * Return the higher layer response bytes for {@link NfcB} tags.
+     * <p>Does not cause any RF activity and does not block.
+     * <p>The higher layer response bytes can be used to help identify a tag.
+     * They are present only on {@link IsoDep} tags that are based on {@link NfcB}
+     * RF technology. If this tag is not {@link NfcB} then null is returned.
+     * <p>In ISO 14443-4 terminology, the higher layer bytes are a subset of the
+     * ATTRIB response.
+     *
+     * @return ISO-DEP historical bytes, or null if this is not a {@link NfcB} tag
+     */
+    public byte[] getHiLayerResponse() {
+        return mHiLayerResponse;
+    }
+
+    /**
+     * Send raw ISO-DEP data to the tag and receive the response.
+     *
+     * <p>Applications must only send the INF payload, and not the start of frame and
+     * end of frame indicators. Applications do not need to fragment the payload, it
+     * will be automatically fragmented and defragmented by {@link #transceive} if
+     * it exceeds FSD/FSC limits.
+     *
+     * <p>Use {@link #getMaxTransceiveLength} to retrieve the maximum number of bytes
+     * that can be sent with {@link #transceive}.
+     *
+     * <p>This is an I/O operation and will block until complete. It must
+     * not be called from the main application thread. A blocked call will be canceled with
+     * {@link IOException} if {@link #close} is called from another thread.
+     *
+     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+     *
+     * @param data command bytes to send, must not be null
+     * @return response bytes received, will not be null
+     * @throws TagLostException if the tag leaves the field
+     * @throws IOException if there is an I/O failure, or this operation is canceled
+     * @throws SecurityException if the tag object is reused after the tag has left the field
+     */
+    public byte[] transceive(byte[] data) throws IOException {
+        return transceive(data, true);
+    }
+
+    /**
+     * Return the maximum number of bytes that can be sent with {@link #transceive}.
+     * @return the maximum number of bytes that can be sent with {@link #transceive}.
+     */
+    public int getMaxTransceiveLength() {
+        return getMaxTransceiveLengthInternal();
+    }
+
+    /**
+     * <p>Standard APDUs have a 1-byte length field, allowing a maximum of
+     * 255 payload bytes, which results in a maximum APDU length of 261 bytes.
+     *
+     * <p>Extended length APDUs have a 3-byte length field, allowing 65535
+     * payload bytes.
+     *
+     * <p>Some NFC adapters, like the one used in the Nexus S and the Galaxy Nexus
+     * do not support extended length APDUs. They are expected to be well-supported
+     * in the future though. Use this method to check for extended length APDU
+     * support.
+     *
+     * @return whether the NFC adapter on this device supports extended length APDUs.
+     * @throws SecurityException if the tag object is reused after the tag has left the field
+     */
+    public boolean isExtendedLengthApduSupported() {
+        try {
+            return mTag.getTagService().getExtendedLengthApdusSupported();
+        } catch (RemoteException e) {
+            Log.e(TAG, "NFC service dead", e);
+            return false;
+        }
+    }
+}
diff --git a/nfc/java/android/nfc/tech/MifareClassic.java b/nfc/java/android/nfc/tech/MifareClassic.java
new file mode 100644
index 0000000..26f54e6
--- /dev/null
+++ b/nfc/java/android/nfc/tech/MifareClassic.java
@@ -0,0 +1,655 @@
+/*
+ * 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 android.nfc.tech;
+
+import android.nfc.ErrorCodes;
+import android.nfc.Tag;
+import android.nfc.TagLostException;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * Provides access to MIFARE Classic properties and I/O operations on a {@link Tag}.
+ *
+ * <p>Acquire a {@link MifareClassic} object using {@link #get}.
+ *
+ * <p>MIFARE Classic is also known as MIFARE Standard.
+ * <p>MIFARE Classic tags are divided into sectors, and each sector is sub-divided into
+ * blocks. Block size is always 16 bytes ({@link #BLOCK_SIZE}. Sector size varies.
+ * <ul>
+ * <li>MIFARE Classic Mini are 320 bytes ({@link #SIZE_MINI}), with 5 sectors each of 4 blocks.
+ * <li>MIFARE Classic 1k are 1024 bytes ({@link #SIZE_1K}), with 16 sectors each of 4 blocks.
+ * <li>MIFARE Classic 2k are 2048 bytes ({@link #SIZE_2K}), with 32 sectors each of 4 blocks.
+ * <li>MIFARE Classic 4k are 4096 bytes ({@link #SIZE_4K}). The first 32 sectors contain 4 blocks
+ * and the last 8 sectors contain 16 blocks.
+ * </ul>
+ *
+ * <p>MIFARE Classic tags require authentication on a per-sector basis before any
+ * other I/O operations on that sector can be performed. There are two keys per sector,
+ * and ACL bits determine what I/O operations are allowed on that sector after
+ * authenticating with a key. {@see #authenticateSectorWithKeyA} and
+ * {@see #authenticateSectorWithKeyB}.
+ *
+ * <p>Three well-known authentication keys are defined in this class:
+ * {@link #KEY_DEFAULT}, {@link #KEY_MIFARE_APPLICATION_DIRECTORY},
+ * {@link #KEY_NFC_FORUM}.
+ * <ul>
+ * <li>{@link #KEY_DEFAULT} is the default factory key for MIFARE Classic.
+ * <li>{@link #KEY_MIFARE_APPLICATION_DIRECTORY} is the well-known key for
+ * MIFARE Classic cards that have been formatted according to the
+ * MIFARE Application Directory (MAD) specification.
+ * <li>{@link #KEY_NFC_FORUM} is the well-known key for MIFARE Classic cards that
+ * have been formatted according to the NXP specification for NDEF on MIFARE Classic.
+ *
+ * <p>Implementation of this class on a Android NFC device is optional.
+ * If it is not implemented, then
+ * {@link MifareClassic} will never be enumerated in {@link Tag#getTechList}.
+ * If it is enumerated, then all {@link MifareClassic} I/O operations will be supported,
+ * and {@link Ndef#MIFARE_CLASSIC} NDEF tags will also be supported. In either case,
+ * {@link NfcA} will also be enumerated on the tag, because all MIFARE Classic tags are also
+ * {@link NfcA}.
+ *
+ * <p class="note"><strong>Note:</strong> Methods that perform I/O operations
+ * require the {@link android.Manifest.permission#NFC} permission.
+ */
+public final class MifareClassic extends BasicTagTechnology {
+    private static final String TAG = "NFC";
+
+    /**
+     * The default factory key.
+     */
+    public static final byte[] KEY_DEFAULT =
+            {(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF};
+    /**
+     * The well-known key for tags formatted according to the
+     * MIFARE Application Directory (MAD) specification.
+     */
+    public static final byte[] KEY_MIFARE_APPLICATION_DIRECTORY =
+            {(byte)0xA0,(byte)0xA1,(byte)0xA2,(byte)0xA3,(byte)0xA4,(byte)0xA5};
+    /**
+     * The well-known key for tags formatted according to the
+     * NDEF on MIFARE Classic specification.
+     */
+    public static final byte[] KEY_NFC_FORUM =
+            {(byte)0xD3,(byte)0xF7,(byte)0xD3,(byte)0xF7,(byte)0xD3,(byte)0xF7};
+
+    /** A MIFARE Classic compatible card of unknown type */
+    public static final int TYPE_UNKNOWN = -1;
+    /** A MIFARE Classic tag */
+    public static final int TYPE_CLASSIC = 0;
+    /** A MIFARE Plus tag */
+    public static final int TYPE_PLUS = 1;
+    /** A MIFARE Pro tag */
+    public static final int TYPE_PRO = 2;
+
+    /** Tag contains 16 sectors, each with 4 blocks. */
+    public static final int SIZE_1K = 1024;
+    /** Tag contains 32 sectors, each with 4 blocks. */
+    public static final int SIZE_2K = 2048;
+    /**
+     * Tag contains 40 sectors. The first 32 sectors contain 4 blocks and the last 8 sectors
+     * contain 16 blocks.
+     */
+    public static final int SIZE_4K = 4096;
+    /** Tag contains 5 sectors, each with 4 blocks. */
+    public static final int SIZE_MINI = 320;
+
+    /** Size of a MIFARE Classic block (in bytes) */
+    public static final int BLOCK_SIZE = 16;
+
+    private static final int MAX_BLOCK_COUNT = 256;
+    private static final int MAX_SECTOR_COUNT = 40;
+
+    private boolean mIsEmulated;
+    private int mType;
+    private int mSize;
+
+    /**
+     * Get an instance of {@link MifareClassic} for the given tag.
+     * <p>Does not cause any RF activity and does not block.
+     * <p>Returns null if {@link MifareClassic} was not enumerated in {@link Tag#getTechList}.
+     * This indicates the tag is not MIFARE Classic compatible, or this Android
+     * device does not support MIFARE Classic.
+     *
+     * @param tag an MIFARE Classic compatible tag
+     * @return MIFARE Classic object
+     */
+    public static MifareClassic get(Tag tag) {
+        if (!tag.hasTech(TagTechnology.MIFARE_CLASSIC)) return null;
+        try {
+            return new MifareClassic(tag);
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /** @hide */
+    public MifareClassic(Tag tag) throws RemoteException {
+        super(tag, TagTechnology.MIFARE_CLASSIC);
+
+        NfcA a = NfcA.get(tag);  // MIFARE Classic is always based on NFC a
+
+        mIsEmulated = false;
+
+        switch (a.getSak()) {
+        case 0x01:
+        case 0x08:
+            mType = TYPE_CLASSIC;
+            mSize = SIZE_1K;
+            break;
+        case 0x09:
+            mType = TYPE_CLASSIC;
+            mSize = SIZE_MINI;
+            break;
+        case 0x10:
+            mType = TYPE_PLUS;
+            mSize = SIZE_2K;
+            // SecLevel = SL2
+            break;
+        case 0x11:
+            mType = TYPE_PLUS;
+            mSize = SIZE_4K;
+            // Seclevel = SL2
+            break;
+        case 0x18:
+            mType = TYPE_CLASSIC;
+            mSize = SIZE_4K;
+            break;
+        case 0x28:
+            mType = TYPE_CLASSIC;
+            mSize = SIZE_1K;
+            mIsEmulated = true;
+            break;
+        case 0x38:
+            mType = TYPE_CLASSIC;
+            mSize = SIZE_4K;
+            mIsEmulated = true;
+            break;
+        case 0x88:
+            mType = TYPE_CLASSIC;
+            mSize = SIZE_1K;
+            // NXP-tag: false
+            break;
+        case 0x98:
+        case 0xB8:
+            mType = TYPE_PRO;
+            mSize = SIZE_4K;
+            break;
+        default:
+            // Stack incorrectly reported a MifareClassic. We cannot handle this
+            // gracefully - we have no idea of the memory layout. Bail.
+            throw new RuntimeException(
+                    "Tag incorrectly enumerated as MIFARE Classic, SAK = " + a.getSak());
+        }
+    }
+
+    /**
+     * Return the type of this MIFARE Classic compatible tag.
+     * <p>One of {@link #TYPE_UNKNOWN}, {@link #TYPE_CLASSIC}, {@link #TYPE_PLUS} or
+     * {@link #TYPE_PRO}.
+     * <p>Does not cause any RF activity and does not block.
+     *
+     * @return type
+     */
+    public int getType() {
+        return mType;
+    }
+
+    /**
+     * Return the size of the tag in bytes
+     * <p>One of {@link #SIZE_MINI}, {@link #SIZE_1K}, {@link #SIZE_2K}, {@link #SIZE_4K}.
+     * These constants are equal to their respective size in bytes.
+     * <p>Does not cause any RF activity and does not block.
+     * @return size in bytes
+     */
+    public int getSize() {
+        return mSize;
+    }
+
+    /**
+     * Return true if the tag is emulated, determined at discovery time.
+     * These are actually smart-cards that emulate a MIFARE Classic interface.
+     * They can be treated identically to a MIFARE Classic tag.
+     * @hide
+     */
+    public boolean isEmulated() {
+        return mIsEmulated;
+    }
+
+    /**
+     * Return the number of MIFARE Classic sectors.
+     * <p>Does not cause any RF activity and does not block.
+     * @return number of sectors
+     */
+    public int getSectorCount() {
+        switch (mSize) {
+        case SIZE_1K:
+            return 16;
+        case SIZE_2K:
+            return 32;
+        case SIZE_4K:
+            return 40;
+        case SIZE_MINI:
+            return 5;
+        default:
+            return 0;
+        }
+    }
+
+    /**
+     * Return the total number of MIFARE Classic blocks.
+     * <p>Does not cause any RF activity and does not block.
+     * @return total number of blocks
+     */
+    public int getBlockCount() {
+        return mSize / BLOCK_SIZE;
+    }
+
+    /**
+     * Return the number of blocks in the given sector.
+     * <p>Does not cause any RF activity and does not block.
+     *
+     * @param sectorIndex index of sector, starting from 0
+     * @return number of blocks in the sector
+     */
+    public int getBlockCountInSector(int sectorIndex) {
+        validateSector(sectorIndex);
+
+        if (sectorIndex < 32) {
+            return 4;
+        } else {
+            return 16;
+        }
+    }
+
+    /**
+     * Return the sector that contains a given block.
+     * <p>Does not cause any RF activity and does not block.
+     *
+     * @param blockIndex index of block to lookup, starting from 0
+     * @return sector index that contains the block
+     */
+    public int blockToSector(int blockIndex) {
+        validateBlock(blockIndex);
+
+        if (blockIndex < 32 * 4) {
+            return blockIndex / 4;
+        } else {
+            return 32 + (blockIndex - 32 * 4) / 16;
+        }
+    }
+
+    /**
+     * Return the first block of a given sector.
+     * <p>Does not cause any RF activity and does not block.
+     *
+     * @param sectorIndex index of sector to lookup, starting from 0
+     * @return block index of first block in sector
+     */
+    public int sectorToBlock(int sectorIndex) {
+        if (sectorIndex < 32) {
+            return sectorIndex * 4;
+        } else {
+            return 32 * 4 + (sectorIndex - 32) * 16;
+        }
+    }
+
+    /**
+     * Authenticate a sector with key A.
+     *
+     * <p>Successful authentication of a sector with key A enables other
+     * I/O operations on that sector. The set of operations granted by key A
+     * key depends on the ACL bits set in that sector. For more information
+     * see the MIFARE Classic specification on <a href="http://www.nxp.com">http://www.nxp.com</a>.
+     *
+     * <p>A failed authentication attempt causes an implicit reconnection to the
+     * tag, so authentication to other sectors will be lost.
+     *
+     * <p>This is an I/O operation and will block until complete. It must
+     * not be called from the main application thread. A blocked call will be canceled with
+     * {@link IOException} if {@link #close} is called from another thread.
+     *
+     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+     *
+     * @param sectorIndex index of sector to authenticate, starting from 0
+     * @param key 6-byte authentication key
+     * @return true on success, false on authentication failure
+     * @throws TagLostException if the tag leaves the field
+     * @throws IOException if there is an I/O failure, or the operation is canceled
+     */
+    public boolean authenticateSectorWithKeyA(int sectorIndex, byte[] key) throws IOException {
+        return authenticate(sectorIndex, key, true);
+    }
+
+    /**
+     * Authenticate a sector with key B.
+     *
+     * <p>Successful authentication of a sector with key B enables other
+     * I/O operations on that sector. The set of operations granted by key B
+     * depends on the ACL bits set in that sector. For more information
+     * see the MIFARE Classic specification on <a href="http://www.nxp.com">http://www.nxp.com</a>.
+     *
+     * <p>A failed authentication attempt causes an implicit reconnection to the
+     * tag, so authentication to other sectors will be lost.
+     *
+     * <p>This is an I/O operation and will block until complete. It must
+     * not be called from the main application thread. A blocked call will be canceled with
+     * {@link IOException} if {@link #close} is called from another thread.
+     *
+     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+     *
+     * @param sectorIndex index of sector to authenticate, starting from 0
+     * @param key 6-byte authentication key
+     * @return true on success, false on authentication failure
+     * @throws TagLostException if the tag leaves the field
+     * @throws IOException if there is an I/O failure, or the operation is canceled
+     */
+    public boolean authenticateSectorWithKeyB(int sectorIndex, byte[] key) throws IOException {
+        return authenticate(sectorIndex, key, false);
+    }
+
+    private boolean authenticate(int sector, byte[] key, boolean keyA) throws IOException {
+        validateSector(sector);
+        checkConnected();
+
+        byte[] cmd = new byte[12];
+
+        // First byte is the command
+        if (keyA) {
+            cmd[0] = 0x60; // phHal_eMifareAuthentA
+        } else {
+            cmd[0] = 0x61; // phHal_eMifareAuthentB
+        }
+
+        // Second byte is block address
+        // Authenticate command takes a block address. Authenticating a block
+        // of a sector will authenticate the entire sector.
+        cmd[1] = (byte) sectorToBlock(sector);
+
+        // Next 4 bytes are last 4 bytes of UID
+        byte[] uid = getTag().getId();
+        System.arraycopy(uid, uid.length - 4, cmd, 2, 4);
+
+        // Next 6 bytes are key
+        System.arraycopy(key, 0, cmd, 6, 6);
+
+        try {
+            if (transceive(cmd, false) != null) {
+                return true;
+            }
+        } catch (TagLostException e) {
+            throw e;
+        } catch (IOException e) {
+            // No need to deal with, will return false anyway
+        }
+        return false;
+    }
+
+    /**
+     * Read 16-byte block.
+     *
+     * <p>This is an I/O operation and will block until complete. It must
+     * not be called from the main application thread. A blocked call will be canceled with
+     * {@link IOException} if {@link #close} is called from another thread.
+     *
+     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+     *
+     * @param blockIndex index of block to read, starting from 0
+     * @return 16 byte block
+     * @throws TagLostException if the tag leaves the field
+     * @throws IOException if there is an I/O failure, or the operation is canceled
+     */
+    public byte[] readBlock(int blockIndex) throws IOException {
+        validateBlock(blockIndex);
+        checkConnected();
+
+        byte[] cmd = { 0x30, (byte) blockIndex };
+        return transceive(cmd, false);
+    }
+
+    /**
+     * Write 16-byte block.
+     *
+     * <p>This is an I/O operation and will block until complete. It must
+     * not be called from the main application thread. A blocked call will be canceled with
+     * {@link IOException} if {@link #close} is called from another thread.
+     *
+     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+     *
+     * @param blockIndex index of block to write, starting from 0
+     * @param data 16 bytes of data to write
+     * @throws TagLostException if the tag leaves the field
+     * @throws IOException if there is an I/O failure, or the operation is canceled
+     */
+    public void writeBlock(int blockIndex, byte[] data) throws IOException {
+        validateBlock(blockIndex);
+        checkConnected();
+        if (data.length != 16) {
+            throw new IllegalArgumentException("must write 16-bytes");
+        }
+
+        byte[] cmd = new byte[data.length + 2];
+        cmd[0] = (byte) 0xA0; // MF write command
+        cmd[1] = (byte) blockIndex;
+        System.arraycopy(data, 0, cmd, 2, data.length);
+
+        transceive(cmd, false);
+    }
+
+    /**
+     * Increment a value block, storing the result in the temporary block on the tag.
+     *
+     * <p>This is an I/O operation and will block until complete. It must
+     * not be called from the main application thread. A blocked call will be canceled with
+     * {@link IOException} if {@link #close} is called from another thread.
+     *
+     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+     *
+     * @param blockIndex index of block to increment, starting from 0
+     * @param value non-negative to increment by
+     * @throws TagLostException if the tag leaves the field
+     * @throws IOException if there is an I/O failure, or the operation is canceled
+     */
+    public void increment(int blockIndex, int value) throws IOException {
+        validateBlock(blockIndex);
+        validateValueOperand(value);
+        checkConnected();
+
+        ByteBuffer cmd = ByteBuffer.allocate(6);
+        cmd.order(ByteOrder.LITTLE_ENDIAN);
+        cmd.put( (byte) 0xC1 );
+        cmd.put( (byte) blockIndex );
+        cmd.putInt(value);
+
+        transceive(cmd.array(), false);
+    }
+
+    /**
+     * Decrement a value block, storing the result in the temporary block on the tag.
+     *
+     * <p>This is an I/O operation and will block until complete. It must
+     * not be called from the main application thread. A blocked call will be canceled with
+     * {@link IOException} if {@link #close} is called from another thread.
+     *
+     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+     *
+     * @param blockIndex index of block to decrement, starting from 0
+     * @param value non-negative to decrement by
+     * @throws TagLostException if the tag leaves the field
+     * @throws IOException if there is an I/O failure, or the operation is canceled
+     */
+    public void decrement(int blockIndex, int value) throws IOException {
+        validateBlock(blockIndex);
+        validateValueOperand(value);
+        checkConnected();
+
+        ByteBuffer cmd = ByteBuffer.allocate(6);
+        cmd.order(ByteOrder.LITTLE_ENDIAN);
+        cmd.put( (byte) 0xC0 );
+        cmd.put( (byte) blockIndex );
+        cmd.putInt(value);
+
+        transceive(cmd.array(), false);
+    }
+
+    /**
+     * Copy from the temporary block to a value block.
+     *
+     * <p>This is an I/O operation and will block until complete. It must
+     * not be called from the main application thread. A blocked call will be canceled with
+     * {@link IOException} if {@link #close} is called from another thread.
+     *
+     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+     *
+     * @param blockIndex index of block to copy to
+     * @throws TagLostException if the tag leaves the field
+     * @throws IOException if there is an I/O failure, or the operation is canceled
+     */
+    public void transfer(int blockIndex) throws IOException {
+        validateBlock(blockIndex);
+        checkConnected();
+
+        byte[] cmd = { (byte) 0xB0, (byte) blockIndex };
+
+        transceive(cmd, false);
+    }
+
+    /**
+     * Copy from a value block to the temporary block.
+     *
+     * <p>This is an I/O operation and will block until complete. It must
+     * not be called from the main application thread. A blocked call will be canceled with
+     * {@link IOException} if {@link #close} is called from another thread.
+     *
+     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+     *
+     * @param blockIndex index of block to copy from
+     * @throws TagLostException if the tag leaves the field
+     * @throws IOException if there is an I/O failure, or the operation is canceled
+     */
+    public void restore(int blockIndex) throws IOException {
+        validateBlock(blockIndex);
+        checkConnected();
+
+        byte[] cmd = { (byte) 0xC2, (byte) blockIndex };
+
+        transceive(cmd, false);
+    }
+
+    /**
+     * Send raw NfcA data to a tag and receive the response.
+     *
+     * <p>This is equivalent to connecting to this tag via {@link NfcA}
+     * and calling {@link NfcA#transceive}. Note that all MIFARE Classic
+     * tags are based on {@link NfcA} technology.
+     *
+     * <p>Use {@link #getMaxTransceiveLength} to retrieve the maximum number of bytes
+     * that can be sent with {@link #transceive}.
+     *
+     * <p>This is an I/O operation and will block until complete. It must
+     * not be called from the main application thread. A blocked call will be canceled with
+     * {@link IOException} if {@link #close} is called from another thread.
+     *
+     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+     *
+     * @see NfcA#transceive
+     */
+    public byte[] transceive(byte[] data) throws IOException {
+        return transceive(data, true);
+    }
+
+    /**
+     * Return the maximum number of bytes that can be sent with {@link #transceive}.
+     * @return the maximum number of bytes that can be sent with {@link #transceive}.
+     */
+    public int getMaxTransceiveLength() {
+        return getMaxTransceiveLengthInternal();
+    }
+
+    /**
+     * Set the {@link #transceive} timeout in milliseconds.
+     *
+     * <p>The timeout only applies to {@link #transceive} on this object,
+     * and is reset to a default value when {@link #close} is called.
+     *
+     * <p>Setting a longer timeout may be useful when performing
+     * transactions that require a long processing time on the tag
+     * such as key generation.
+     *
+     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+     *
+     * @param timeout timeout value in milliseconds
+     * @throws SecurityException if the tag object is reused after the tag has left the field
+     */
+    public void setTimeout(int timeout) {
+        try {
+            int err = mTag.getTagService().setTimeout(TagTechnology.MIFARE_CLASSIC, timeout);
+            if (err != ErrorCodes.SUCCESS) {
+                throw new IllegalArgumentException("The supplied timeout is not valid");
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "NFC service dead", e);
+        }
+    }
+
+    /**
+     * Get the current {@link #transceive} timeout in milliseconds.
+     *
+     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+     *
+     * @return timeout value in milliseconds
+     * @throws SecurityException if the tag object is reused after the tag has left the field
+     */
+    public int getTimeout() {
+        try {
+            return mTag.getTagService().getTimeout(TagTechnology.MIFARE_CLASSIC);
+        } catch (RemoteException e) {
+            Log.e(TAG, "NFC service dead", e);
+            return 0;
+        }
+    }
+
+    private static void validateSector(int sector) {
+        // Do not be too strict on upper bounds checking, since some cards
+        // have more addressable memory than they report. For example,
+        // MIFARE Plus 2k cards will appear as MIFARE Classic 1k cards when in
+        // MIFARE Classic compatibility mode.
+        // Note that issuing a command to an out-of-bounds block is safe - the
+        // tag should report error causing IOException. This validation is a
+        // helper to guard against obvious programming mistakes.
+        if (sector < 0 || sector >= MAX_SECTOR_COUNT) {
+            throw new IndexOutOfBoundsException("sector out of bounds: " + sector);
+        }
+    }
+
+    private static void validateBlock(int block) {
+        // Just looking for obvious out of bounds...
+        if (block < 0 || block >= MAX_BLOCK_COUNT) {
+            throw new IndexOutOfBoundsException("block out of bounds: " + block);
+        }
+    }
+
+    private static void validateValueOperand(int value) {
+        if (value < 0) {
+            throw new IllegalArgumentException("value operand negative");
+        }
+    }
+}
diff --git a/nfc/java/android/nfc/tech/MifareUltralight.java b/nfc/java/android/nfc/tech/MifareUltralight.java
new file mode 100644
index 0000000..c0416a3
--- /dev/null
+++ b/nfc/java/android/nfc/tech/MifareUltralight.java
@@ -0,0 +1,280 @@
+/*
+ * 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 android.nfc.tech;
+
+import android.nfc.ErrorCodes;
+import android.nfc.Tag;
+import android.nfc.TagLostException;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.io.IOException;
+
+//TOOD: Ultralight C 3-DES authentication, one-way counter
+
+/**
+ * Provides access to MIFARE Ultralight properties and I/O operations on a {@link Tag}.
+ *
+ * <p>Acquire a {@link MifareUltralight} object using {@link #get}.
+ *
+ * <p>MIFARE Ultralight compatible tags have 4 byte pages {@link #PAGE_SIZE}.
+ * The primary operations on an Ultralight tag are {@link #readPages} and
+ * {@link #writePage}.
+ *
+ * <p>The original MIFARE Ultralight consists of a 64 byte EEPROM. The first
+ * 4 pages are for the OTP area, manufacturer data, and locking bits. They are
+ * readable and some bits are writable. The final 12 pages are the user
+ * read/write area. For more information see the NXP data sheet MF0ICU1.
+ *
+ * <p>The MIFARE Ultralight C consists of a 192 byte EEPROM. The first 4 pages
+ * are for OTP, manufacturer data, and locking bits. The next 36 pages are the
+ * user read/write area. The next 4 pages are additional locking bits, counters
+ * and authentication configuration and are readable. The final 4 pages are for
+ * the authentication key and are not readable. For more information see the
+ * NXP data sheet MF0ICU2.
+ *
+ * <p>Implementation of this class on a Android NFC device is optional.
+ * If it is not implemented, then
+ * {@link MifareUltralight} will never be enumerated in {@link Tag#getTechList}.
+ * If it is enumerated, then all {@link MifareUltralight} I/O operations will be supported.
+ * In either case, {@link NfcA} will also be enumerated on the tag,
+ * because all MIFARE Ultralight tags are also {@link NfcA} tags.
+ *
+ * <p class="note"><strong>Note:</strong> Methods that perform I/O operations
+ * require the {@link android.Manifest.permission#NFC} permission.
+ */
+public final class MifareUltralight extends BasicTagTechnology {
+    private static final String TAG = "NFC";
+
+    /** A MIFARE Ultralight compatible tag of unknown type */
+    public static final int TYPE_UNKNOWN = -1;
+    /** A MIFARE Ultralight tag */
+    public static final int TYPE_ULTRALIGHT = 1;
+    /** A MIFARE Ultralight C tag */
+    public static final int TYPE_ULTRALIGHT_C = 2;
+
+    /** Size of a MIFARE Ultralight page in bytes */
+    public static final int PAGE_SIZE = 4;
+
+    private static final int NXP_MANUFACTURER_ID = 0x04;
+    private static final int MAX_PAGE_COUNT = 256;
+
+    /** @hide */
+    public static final String EXTRA_IS_UL_C = "isulc";
+
+    private int mType;
+
+    /**
+     * Get an instance of {@link MifareUltralight} for the given tag.
+     * <p>Returns null if {@link MifareUltralight} was not enumerated in
+     * {@link Tag#getTechList} - this indicates the tag is not MIFARE
+     * Ultralight compatible, or that this Android
+     * device does not implement MIFARE Ultralight.
+     * <p>Does not cause any RF activity and does not block.
+     *
+     * @param tag an MIFARE Ultralight compatible tag
+     * @return MIFARE Ultralight object
+     */
+    public static MifareUltralight get(Tag tag) {
+        if (!tag.hasTech(TagTechnology.MIFARE_ULTRALIGHT)) return null;
+        try {
+            return new MifareUltralight(tag);
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /** @hide */
+    public MifareUltralight(Tag tag) throws RemoteException {
+        super(tag, TagTechnology.MIFARE_ULTRALIGHT);
+
+        // Check if this could actually be a MIFARE
+        NfcA a = NfcA.get(tag);
+
+        mType = TYPE_UNKNOWN;
+
+        if (a.getSak() == 0x00 && tag.getId()[0] == NXP_MANUFACTURER_ID) {
+            Bundle extras = tag.getTechExtras(TagTechnology.MIFARE_ULTRALIGHT);
+            if (extras.getBoolean(EXTRA_IS_UL_C)) {
+                mType = TYPE_ULTRALIGHT_C;
+            } else {
+                mType = TYPE_ULTRALIGHT;
+            }
+        }
+    }
+
+    /**
+     * Return the MIFARE Ultralight type of the tag.
+     * <p>One of {@link #TYPE_ULTRALIGHT} or {@link #TYPE_ULTRALIGHT_C} or
+     * {@link #TYPE_UNKNOWN}.
+     * <p>Depending on how the tag has been formatted, it can be impossible
+     * to accurately classify between original MIFARE Ultralight and
+     * Ultralight C. So treat this method as a hint.
+     * <p>Does not cause any RF activity and does not block.
+     *
+     * @return the type
+     */
+    public int getType() {
+        return mType;
+    }
+
+    /**
+     * Read 4 pages (16 bytes).
+     *
+     * <p>The MIFARE Ultralight protocol always reads 4 pages at a time, to
+     * reduce the number of commands required to read an entire tag.
+     * <p>If a read spans past the last readable block, then the tag will
+     * return pages that have been wrapped back to the first blocks. MIFARE
+     * Ultralight tags have readable blocks 0x00 through 0x0F. So a read to
+     * block offset 0x0E would return blocks 0x0E, 0x0F, 0x00, 0x01. MIFARE
+     * Ultralight C tags have readable blocks 0x00 through 0x2B. So a read to
+     * block 0x2A would return blocks 0x2A, 0x2B, 0x00, 0x01.
+     *
+     * <p>This is an I/O operation and will block until complete. It must
+     * not be called from the main application thread. A blocked call will be canceled with
+     * {@link IOException} if {@link #close} is called from another thread.
+     *
+     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+     *
+     * @param pageOffset index of first page to read, starting from 0
+     * @return 4 pages (16 bytes)
+     * @throws TagLostException if the tag leaves the field
+     * @throws IOException if there is an I/O failure, or the operation is canceled
+     */
+    public byte[] readPages(int pageOffset) throws IOException {
+        validatePageIndex(pageOffset);
+        checkConnected();
+
+        byte[] cmd = { 0x30, (byte) pageOffset};
+        return transceive(cmd, false);
+    }
+
+    /**
+     * Write 1 page (4 bytes).
+     *
+     * <p>The MIFARE Ultralight protocol always writes 1 page at a time, to
+     * minimize EEPROM write cycles.
+     *
+     * <p>This is an I/O operation and will block until complete. It must
+     * not be called from the main application thread. A blocked call will be canceled with
+     * {@link IOException} if {@link #close} is called from another thread.
+     *
+     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+     *
+     * @param pageOffset index of page to write, starting from 0
+     * @param data 4 bytes to write
+     * @throws TagLostException if the tag leaves the field
+     * @throws IOException if there is an I/O failure, or the operation is canceled
+     */
+    public void writePage(int pageOffset, byte[] data) throws IOException {
+        validatePageIndex(pageOffset);
+        checkConnected();
+
+        byte[] cmd = new byte[data.length + 2];
+        cmd[0] = (byte) 0xA2;
+        cmd[1] = (byte) pageOffset;
+        System.arraycopy(data, 0, cmd, 2, data.length);
+
+        transceive(cmd, false);
+    }
+
+    /**
+     * Send raw NfcA data to a tag and receive the response.
+     *
+     * <p>This is equivalent to connecting to this tag via {@link NfcA}
+     * and calling {@link NfcA#transceive}. Note that all MIFARE Classic
+     * tags are based on {@link NfcA} technology.
+     *
+     * <p>Use {@link #getMaxTransceiveLength} to retrieve the maximum number of bytes
+     * that can be sent with {@link #transceive}.
+     *
+     * <p>This is an I/O operation and will block until complete. It must
+     * not be called from the main application thread. A blocked call will be canceled with
+     * {@link IOException} if {@link #close} is called from another thread.
+     *
+     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+     *
+     * @see NfcA#transceive
+     */
+    public byte[] transceive(byte[] data) throws IOException {
+        return transceive(data, true);
+    }
+
+    /**
+     * Return the maximum number of bytes that can be sent with {@link #transceive}.
+     * @return the maximum number of bytes that can be sent with {@link #transceive}.
+     */
+    public int getMaxTransceiveLength() {
+        return getMaxTransceiveLengthInternal();
+    }
+
+    /**
+     * Set the {@link #transceive} timeout in milliseconds.
+     *
+     * <p>The timeout only applies to {@link #transceive} on this object,
+     * and is reset to a default value when {@link #close} is called.
+     *
+     * <p>Setting a longer timeout may be useful when performing
+     * transactions that require a long processing time on the tag
+     * such as key generation.
+     *
+     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+     *
+     * @param timeout timeout value in milliseconds
+     * @throws SecurityException if the tag object is reused after the tag has left the field
+     */
+    public void setTimeout(int timeout) {
+        try {
+            int err = mTag.getTagService().setTimeout(
+                    TagTechnology.MIFARE_ULTRALIGHT, timeout);
+            if (err != ErrorCodes.SUCCESS) {
+                throw new IllegalArgumentException("The supplied timeout is not valid");
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "NFC service dead", e);
+        }
+    }
+
+    /**
+     * Get the current {@link #transceive} timeout in milliseconds.
+     *
+     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+     *
+     * @return timeout value in milliseconds
+     * @throws SecurityException if the tag object is reused after the tag has left the field
+     */
+    public int getTimeout() {
+        try {
+            return mTag.getTagService().getTimeout(TagTechnology.MIFARE_ULTRALIGHT);
+        } catch (RemoteException e) {
+            Log.e(TAG, "NFC service dead", e);
+            return 0;
+        }
+    }
+
+    private static void validatePageIndex(int pageIndex) {
+        // Do not be too strict on upper bounds checking, since some cards
+        // may have more addressable memory than they report.
+        // Note that issuing a command to an out-of-bounds block is safe - the
+        // tag will wrap the read to an addressable area. This validation is a
+        // helper to guard against obvious programming mistakes.
+        if (pageIndex < 0 || pageIndex >= MAX_PAGE_COUNT) {
+            throw new IndexOutOfBoundsException("page out of bounds: " + pageIndex);
+        }
+    }
+}
diff --git a/nfc/java/android/nfc/tech/Ndef.java b/nfc/java/android/nfc/tech/Ndef.java
new file mode 100644
index 0000000..7d83f15
--- /dev/null
+++ b/nfc/java/android/nfc/tech/Ndef.java
@@ -0,0 +1,408 @@
+/*
+ * 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 android.nfc.tech;
+
+import android.nfc.ErrorCodes;
+import android.nfc.FormatException;
+import android.nfc.INfcTag;
+import android.nfc.NdefMessage;
+import android.nfc.Tag;
+import android.nfc.TagLostException;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * Provides access to NDEF content and operations on a {@link Tag}.
+ *
+ * <p>Acquire a {@link Ndef} object using {@link #get}.
+ *
+ * <p>NDEF is an NFC Forum data format. The data formats are implemented in
+ * {@link android.nfc.NdefMessage} and
+ * {@link android.nfc.NdefRecord}. This class provides methods to
+ * retrieve and modify the {@link android.nfc.NdefMessage}
+ * on a tag.
+ *
+ * <p>There are currently four NFC Forum standardized tag types that can be
+ * formatted to contain NDEF data.
+ * <ul>
+ * <li>NFC Forum Type 1 Tag ({@link #NFC_FORUM_TYPE_1}), such as the Innovision Topaz
+ * <li>NFC Forum Type 2 Tag ({@link #NFC_FORUM_TYPE_2}), such as the NXP MIFARE Ultralight
+ * <li>NFC Forum Type 3 Tag ({@link #NFC_FORUM_TYPE_3}), such as Sony Felica
+ * <li>NFC Forum Type 4 Tag ({@link #NFC_FORUM_TYPE_4}), such as NXP MIFARE Desfire
+ * </ul>
+ * It is mandatory for all Android devices with NFC to correctly enumerate
+ * {@link Ndef} on NFC Forum Tag Types 1-4, and implement all NDEF operations
+ * as defined in this class.
+ *
+ * <p>Some vendors have their own well defined specifications for storing NDEF data
+ * on tags that do not fall into the above categories. Android devices with NFC
+ * should enumerate and implement {@link Ndef} under these vendor specifications
+ * where possible, but it is not mandatory. {@link #getType} returns a String
+ * describing this specification, for example {@link #MIFARE_CLASSIC} is
+ * <code>com.nxp.ndef.mifareclassic</code>.
+ *
+ * <p>Android devices that support MIFARE Classic must also correctly
+ * implement {@link Ndef} on MIFARE Classic tags formatted to NDEF.
+ *
+ * <p>For guaranteed compatibility across all Android devices with NFC, it is
+ * recommended to use NFC Forum Types 1-4 in new deployments of NFC tags
+ * with NDEF payload. Vendor NDEF formats will not work on all Android devices.
+ *
+ * <p class="note"><strong>Note:</strong> Methods that perform I/O operations
+ * require the {@link android.Manifest.permission#NFC} permission.
+ */
+public final class Ndef extends BasicTagTechnology {
+    private static final String TAG = "NFC";
+
+    /** @hide */
+    public static final int NDEF_MODE_READ_ONLY = 1;
+    /** @hide */
+    public static final int NDEF_MODE_READ_WRITE = 2;
+    /** @hide */
+    public static final int NDEF_MODE_UNKNOWN = 3;
+
+    /** @hide */
+    public static final String EXTRA_NDEF_MSG = "ndefmsg";
+
+    /** @hide */
+    public static final String EXTRA_NDEF_MAXLENGTH = "ndefmaxlength";
+
+    /** @hide */
+    public static final String EXTRA_NDEF_CARDSTATE = "ndefcardstate";
+
+    /** @hide */
+    public static final String EXTRA_NDEF_TYPE = "ndeftype";
+
+    /** @hide */
+    public static final int TYPE_OTHER = -1;
+    /** @hide */
+    public static final int TYPE_1 = 1;
+    /** @hide */
+    public static final int TYPE_2 = 2;
+    /** @hide */
+    public static final int TYPE_3 = 3;
+    /** @hide */
+    public static final int TYPE_4 = 4;
+    /** @hide */
+    public static final int TYPE_MIFARE_CLASSIC = 101;
+    /** @hide */
+    public static final int TYPE_ICODE_SLI = 102;
+
+    /** @hide */
+    public static final String UNKNOWN = "android.ndef.unknown";
+
+    /** NFC Forum Tag Type 1 */
+    public static final String NFC_FORUM_TYPE_1 = "org.nfcforum.ndef.type1";
+    /** NFC Forum Tag Type 2 */
+    public static final String NFC_FORUM_TYPE_2 = "org.nfcforum.ndef.type2";
+    /** NFC Forum Tag Type 3 */
+    public static final String NFC_FORUM_TYPE_3 = "org.nfcforum.ndef.type3";
+    /** NFC Forum Tag Type 4 */
+    public static final String NFC_FORUM_TYPE_4 = "org.nfcforum.ndef.type4";
+    /** NDEF on MIFARE Classic */
+    public static final String MIFARE_CLASSIC = "com.nxp.ndef.mifareclassic";
+    /**
+     * NDEF on iCODE SLI
+     * @hide
+     */
+    public static final String ICODE_SLI = "com.nxp.ndef.icodesli";
+
+    private final int mMaxNdefSize;
+    private final int mCardState;
+    private final NdefMessage mNdefMsg;
+    private final int mNdefType;
+
+    /**
+     * Get an instance of {@link Ndef} for the given tag.
+     *
+     * <p>Returns null if {@link Ndef} was not enumerated in {@link Tag#getTechList}.
+     * This indicates the tag is not NDEF formatted, or that this tag
+     * is NDEF formatted but under a vendor specification that this Android
+     * device does not implement.
+     *
+     * <p>Does not cause any RF activity and does not block.
+     *
+     * @param tag an NDEF compatible tag
+     * @return Ndef object
+     */
+    public static Ndef get(Tag tag) {
+        if (!tag.hasTech(TagTechnology.NDEF)) return null;
+        try {
+            return new Ndef(tag);
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Internal constructor, to be used by NfcAdapter
+     * @hide
+     */
+    public Ndef(Tag tag) throws RemoteException {
+        super(tag, TagTechnology.NDEF);
+        Bundle extras = tag.getTechExtras(TagTechnology.NDEF);
+        if (extras != null) {
+            mMaxNdefSize = extras.getInt(EXTRA_NDEF_MAXLENGTH);
+            mCardState = extras.getInt(EXTRA_NDEF_CARDSTATE);
+            mNdefMsg = extras.getParcelable(EXTRA_NDEF_MSG, android.nfc.NdefMessage.class);
+            mNdefType = extras.getInt(EXTRA_NDEF_TYPE);
+        } else {
+            throw new NullPointerException("NDEF tech extras are null.");
+        }
+
+    }
+
+    /**
+     * Get the {@link NdefMessage} that was read from the tag at discovery time.
+     *
+     * <p>If the NDEF Message is modified by an I/O operation then it
+     * will not be updated here, this function only returns what was discovered
+     * when the tag entered the field.
+     * <p>Note that this method may return null if the tag was in the
+     * INITIALIZED state as defined by NFC Forum, as in this state the
+     * tag is formatted to support NDEF but does not contain a message yet.
+     * <p>Does not cause any RF activity and does not block.
+     * @return NDEF Message read from the tag at discovery time, can be null
+     */
+    public NdefMessage getCachedNdefMessage() {
+        return mNdefMsg;
+    }
+
+    /**
+     * Get the NDEF tag type.
+     *
+     * <p>Returns one of {@link #NFC_FORUM_TYPE_1}, {@link #NFC_FORUM_TYPE_2},
+     * {@link #NFC_FORUM_TYPE_3}, {@link #NFC_FORUM_TYPE_4},
+     * {@link #MIFARE_CLASSIC} or another NDEF tag type that has not yet been
+     * formalized in this Android API.
+     *
+     * <p>Does not cause any RF activity and does not block.
+     *
+     * @return a string representing the NDEF tag type
+     */
+    public String getType() {
+        switch (mNdefType) {
+            case TYPE_1:
+                return NFC_FORUM_TYPE_1;
+            case TYPE_2:
+                return NFC_FORUM_TYPE_2;
+            case TYPE_3:
+                return NFC_FORUM_TYPE_3;
+            case TYPE_4:
+                return NFC_FORUM_TYPE_4;
+            case TYPE_MIFARE_CLASSIC:
+                return MIFARE_CLASSIC;
+            case TYPE_ICODE_SLI:
+                return ICODE_SLI;
+            default:
+                return UNKNOWN;
+        }
+    }
+
+    /**
+     * Get the maximum NDEF message size in bytes.
+     *
+     * <p>Does not cause any RF activity and does not block.
+     *
+     * @return size in bytes
+     */
+    public int getMaxSize() {
+        return mMaxNdefSize;
+    }
+
+    /**
+     * Determine if the tag is writable.
+     *
+     * <p>NFC Forum tags can be in read-only or read-write states.
+     *
+     * <p>Does not cause any RF activity and does not block.
+     *
+     * <p>Requires {@link android.Manifest.permission#NFC} permission.
+     *
+     * @return true if the tag is writable
+     */
+    public boolean isWritable() {
+        return (mCardState == NDEF_MODE_READ_WRITE);
+    }
+
+    /**
+     * Read the current {@link android.nfc.NdefMessage} on this tag.
+     *
+     * <p>This always reads the current NDEF Message stored on the tag.
+     *
+     * <p>Note that this method may return null if the tag was in the
+     * INITIALIZED state as defined by NFC Forum, as in that state the
+     * tag is formatted to support NDEF but does not contain a message yet.
+     *
+     * <p>This is an I/O operation and will block until complete. It must
+     * not be called from the main application thread. A blocked call will be canceled with
+     * {@link IOException} if {@link #close} is called from another thread.
+     *
+     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+     *
+     * @return the NDEF Message, can be null
+     * @throws TagLostException if the tag leaves the field
+     * @throws IOException if there is an I/O failure, or the operation is canceled
+     * @throws FormatException if the NDEF Message on the tag is malformed
+     * @throws SecurityException if the tag object is reused after the tag has left the field
+     */
+    public NdefMessage getNdefMessage() throws IOException, FormatException {
+        checkConnected();
+
+        try {
+            INfcTag tagService = mTag.getTagService();
+            if (tagService == null) {
+                throw new IOException("Mock tags don't support this operation.");
+            }
+            int serviceHandle = mTag.getServiceHandle();
+            if (tagService.isNdef(serviceHandle)) {
+                NdefMessage msg = tagService.ndefRead(serviceHandle);
+                if (msg == null && !tagService.isPresent(serviceHandle)) {
+                    throw new TagLostException();
+                }
+                return msg;
+            } else if (!tagService.isPresent(serviceHandle)) {
+                throw new TagLostException();
+            } else {
+                return null;
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "NFC service dead", e);
+            return null;
+        }
+    }
+
+    /**
+     * Overwrite the {@link NdefMessage} on this tag.
+     *
+     * <p>This is an I/O operation and will block until complete. It must
+     * not be called from the main application thread. A blocked call will be canceled with
+     * {@link IOException} if {@link #close} is called from another thread.
+     *
+     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+     *
+     * @param msg the NDEF Message to write, must not be null
+     * @throws TagLostException if the tag leaves the field
+     * @throws IOException if there is an I/O failure, or the operation is canceled
+     * @throws FormatException if the NDEF Message to write is malformed
+     * @throws SecurityException if the tag object is reused after the tag has left the field
+     */
+    public void writeNdefMessage(NdefMessage msg) throws IOException, FormatException {
+        checkConnected();
+
+        try {
+            INfcTag tagService = mTag.getTagService();
+            if (tagService == null) {
+                throw new IOException("Mock tags don't support this operation.");
+            }
+            int serviceHandle = mTag.getServiceHandle();
+            if (tagService.isNdef(serviceHandle)) {
+                int errorCode = tagService.ndefWrite(serviceHandle, msg);
+                switch (errorCode) {
+                    case ErrorCodes.SUCCESS:
+                        break;
+                    case ErrorCodes.ERROR_IO:
+                        throw new IOException();
+                    case ErrorCodes.ERROR_INVALID_PARAM:
+                        throw new FormatException();
+                    default:
+                        // Should not happen
+                        throw new IOException();
+                }
+            }
+            else {
+                throw new IOException("Tag is not ndef");
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "NFC service dead", e);
+        }
+    }
+
+    /**
+     * Indicates whether a tag can be made read-only with {@link #makeReadOnly()}.
+     *
+     * <p>Does not cause any RF activity and does not block.
+     *
+     * @return true if it is possible to make this tag read-only
+     * @throws SecurityException if the tag object is reused after the tag has left the field
+     */
+    public boolean canMakeReadOnly() {
+        INfcTag tagService = mTag.getTagService();
+        if (tagService == null) {
+            return false;
+        }
+        try {
+            return tagService.canMakeReadOnly(mNdefType);
+        } catch (RemoteException e) {
+            Log.e(TAG, "NFC service dead", e);
+            return false;
+        }
+    }
+
+    /**
+     * Make a tag read-only.
+     *
+     * <p>This sets the CC field to indicate the tag is read-only,
+     * and where possible permanently sets the lock bits to prevent
+     * any further modification of the memory.
+     * <p>This is a one-way process and cannot be reverted!
+     *
+     * <p>This is an I/O operation and will block until complete. It must
+     * not be called from the main application thread. A blocked call will be canceled with
+     * {@link IOException} if {@link #close} is called from another thread.
+     *
+     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+     *
+     * @return true on success, false if it is not possible to make this tag read-only
+     * @throws TagLostException if the tag leaves the field
+     * @throws IOException if there is an I/O failure, or the operation is canceled
+     * @throws SecurityException if the tag object is reused after the tag has left the field
+     */
+    public boolean makeReadOnly() throws IOException {
+        checkConnected();
+
+        try {
+            INfcTag tagService = mTag.getTagService();
+            if (tagService == null) {
+                return false;
+            }
+            if (tagService.isNdef(mTag.getServiceHandle())) {
+                int errorCode = tagService.ndefMakeReadOnly(mTag.getServiceHandle());
+                switch (errorCode) {
+                    case ErrorCodes.SUCCESS:
+                        return true;
+                    case ErrorCodes.ERROR_IO:
+                        throw new IOException();
+                    case ErrorCodes.ERROR_INVALID_PARAM:
+                        return false;
+                    default:
+                        // Should not happen
+                        throw new IOException();
+                }
+           }
+           else {
+               throw new IOException("Tag is not ndef");
+           }
+        } catch (RemoteException e) {
+            Log.e(TAG, "NFC service dead", e);
+            return false;
+        }
+    }
+}
diff --git a/nfc/java/android/nfc/tech/NdefFormatable.java b/nfc/java/android/nfc/tech/NdefFormatable.java
new file mode 100644
index 0000000..2240fe7
--- /dev/null
+++ b/nfc/java/android/nfc/tech/NdefFormatable.java
@@ -0,0 +1,182 @@
+/*
+ * 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 android.nfc.tech;
+
+import android.nfc.ErrorCodes;
+import android.nfc.FormatException;
+import android.nfc.INfcTag;
+import android.nfc.NdefMessage;
+import android.nfc.Tag;
+import android.nfc.TagLostException;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * Provide access to NDEF format operations on a {@link Tag}.
+ *
+ * <p>Acquire a {@link NdefFormatable} object using {@link #get}.
+ *
+ * <p>Android devices with NFC must only enumerate and implement this
+ * class for tags for which it can format to NDEF.
+ *
+ * <p>Unfortunately the procedures to convert unformated tags to NDEF formatted
+ * tags are not specified by NFC Forum, and are not generally well-known. So
+ * there is no mandatory set of tags for which all Android devices with NFC
+ * must support {@link NdefFormatable}.
+ *
+ * <p class="note"><strong>Note:</strong> Methods that perform I/O operations
+ * require the {@link android.Manifest.permission#NFC} permission.
+ */
+public final class NdefFormatable extends BasicTagTechnology {
+    private static final String TAG = "NFC";
+
+    /**
+     * Get an instance of {@link NdefFormatable} for the given tag.
+     * <p>Does not cause any RF activity and does not block.
+     * <p>Returns null if {@link NdefFormatable} was not enumerated in {@link Tag#getTechList}.
+     * This indicates the tag is not NDEF formatable by this Android device.
+     *
+     * @param tag an NDEF formatable tag
+     * @return NDEF formatable object
+     */
+    public static NdefFormatable get(Tag tag) {
+        if (!tag.hasTech(TagTechnology.NDEF_FORMATABLE)) return null;
+        try {
+            return new NdefFormatable(tag);
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Internal constructor, to be used by NfcAdapter
+     * @hide
+     */
+    public NdefFormatable(Tag tag) throws RemoteException {
+        super(tag, TagTechnology.NDEF_FORMATABLE);
+    }
+
+    /**
+     * Format a tag as NDEF, and write a {@link NdefMessage}.
+     *
+     * <p>This is a multi-step process, an IOException is thrown
+     * if any one step fails.
+     * <p>The card is left in a read-write state after this operation.
+     *
+     * <p>This is an I/O operation and will block until complete. It must
+     * not be called from the main application thread. A blocked call will be canceled with
+     * {@link IOException} if {@link #close} is called from another thread.
+     *
+     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+     *
+     * @param firstMessage the NDEF message to write after formatting, can be null
+     * @throws TagLostException if the tag leaves the field
+     * @throws IOException if there is an I/O failure, or the operation is canceled
+     * @throws FormatException if the NDEF Message to write is malformed
+     */
+    public void format(NdefMessage firstMessage) throws IOException, FormatException {
+        format(firstMessage, false);
+    }
+
+    /**
+     * Formats a tag as NDEF, write a {@link NdefMessage}, and make read-only.
+     *
+     * <p>This is a multi-step process, an IOException is thrown
+     * if any one step fails.
+     * <p>The card is left in a read-only state if this method returns successfully.
+     *
+     * <p>This is an I/O operation and will block until complete. It must
+     * not be called from the main application thread. A blocked call will be canceled with
+     * {@link IOException} if {@link #close} is called from another thread.
+     *
+     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+     *
+     * @param firstMessage the NDEF message to write after formatting
+     * @throws TagLostException if the tag leaves the field
+     * @throws IOException if there is an I/O failure, or the operation is canceled
+     * @throws FormatException if the NDEF Message to write is malformed
+     * @throws SecurityException if the tag object is reused after the tag has left the field
+     */
+    public void formatReadOnly(NdefMessage firstMessage) throws IOException, FormatException {
+        format(firstMessage, true);
+    }
+
+    /*package*/ void format(NdefMessage firstMessage, boolean makeReadOnly) throws IOException,
+            FormatException {
+        checkConnected();
+
+        try {
+            int serviceHandle = mTag.getServiceHandle();
+            INfcTag tagService = mTag.getTagService();
+            if (tagService == null) {
+                throw new IOException();
+            }
+            int errorCode = tagService.formatNdef(serviceHandle, MifareClassic.KEY_DEFAULT);
+            switch (errorCode) {
+                case ErrorCodes.SUCCESS:
+                    break;
+                case ErrorCodes.ERROR_IO:
+                    throw new IOException();
+                case ErrorCodes.ERROR_INVALID_PARAM:
+                    throw new FormatException();
+                default:
+                    // Should not happen
+                    throw new IOException();
+            }
+            // Now check and see if the format worked
+            if (!tagService.isNdef(serviceHandle)) {
+                throw new IOException();
+            }
+
+            // Write a message, if one was provided
+            if (firstMessage != null) {
+                errorCode = tagService.ndefWrite(serviceHandle, firstMessage);
+                switch (errorCode) {
+                    case ErrorCodes.SUCCESS:
+                        break;
+                    case ErrorCodes.ERROR_IO:
+                        throw new IOException();
+                    case ErrorCodes.ERROR_INVALID_PARAM:
+                        throw new FormatException();
+                    default:
+                        // Should not happen
+                        throw new IOException();
+                }
+            }
+
+            // optionally make read-only
+            if (makeReadOnly) {
+                errorCode = tagService.ndefMakeReadOnly(serviceHandle);
+                switch (errorCode) {
+                    case ErrorCodes.SUCCESS:
+                        break;
+                    case ErrorCodes.ERROR_IO:
+                        throw new IOException();
+                    case ErrorCodes.ERROR_INVALID_PARAM:
+                        throw new IOException();
+                    default:
+                        // Should not happen
+                        throw new IOException();
+                }
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "NFC service dead", e);
+        }
+    }
+}
diff --git a/nfc/java/android/nfc/tech/NfcA.java b/nfc/java/android/nfc/tech/NfcA.java
new file mode 100644
index 0000000..7e66483
--- /dev/null
+++ b/nfc/java/android/nfc/tech/NfcA.java
@@ -0,0 +1,173 @@
+/*
+ * 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 android.nfc.tech;
+
+import android.nfc.ErrorCodes;
+import android.nfc.Tag;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * Provides access to NFC-A (ISO 14443-3A) properties and I/O operations on a {@link Tag}.
+ *
+ * <p>Acquire a {@link NfcA} object using {@link #get}.
+ * <p>The primary NFC-A I/O operation is {@link #transceive}. Applications must
+ * implement their own protocol stack on top of {@link #transceive}.
+ *
+ * <p class="note"><strong>Note:</strong> Methods that perform I/O operations
+ * require the {@link android.Manifest.permission#NFC} permission.
+ */
+public final class NfcA extends BasicTagTechnology {
+    private static final String TAG = "NFC";
+
+    /** @hide */
+    public static final String EXTRA_SAK = "sak";
+    /** @hide */
+    public static final String EXTRA_ATQA = "atqa";
+
+    private short mSak;
+    private byte[] mAtqa;
+
+    /**
+     * Get an instance of {@link NfcA} for the given tag.
+     * <p>Returns null if {@link NfcA} was not enumerated in {@link Tag#getTechList}.
+     * This indicates the tag does not support NFC-A.
+     * <p>Does not cause any RF activity and does not block.
+     *
+     * @param tag an NFC-A compatible tag
+     * @return NFC-A object
+     */
+    public static NfcA get(Tag tag) {
+        if (!tag.hasTech(TagTechnology.NFC_A)) return null;
+        try {
+            return new NfcA(tag);
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /** @hide */
+    public NfcA(Tag tag) throws RemoteException {
+        super(tag, TagTechnology.NFC_A);
+        Bundle extras = tag.getTechExtras(TagTechnology.NFC_A);
+        mSak = extras.getShort(EXTRA_SAK);
+        mAtqa = extras.getByteArray(EXTRA_ATQA);
+    }
+
+    /**
+     * Return the ATQA/SENS_RES bytes from tag discovery.
+     *
+     * <p>Does not cause any RF activity and does not block.
+     *
+     * @return ATQA/SENS_RES bytes
+     */
+    public byte[] getAtqa() {
+        return mAtqa;
+    }
+
+    /**
+     * Return the SAK/SEL_RES bytes from tag discovery.
+     *
+     * <p>Does not cause any RF activity and does not block.
+     *
+     * @return SAK bytes
+     */
+    public short getSak() {
+        return mSak;
+    }
+
+    /**
+     * Send raw NFC-A commands to the tag and receive the response.
+     *
+     * <p>Applications must not append the EoD (CRC) to the payload,
+     * it will be automatically calculated.
+     * <p>Applications must only send commands that are complete bytes,
+     * for example a SENS_REQ is not possible (these are used to
+     * manage tag polling and initialization).
+     *
+     * <p>Use {@link #getMaxTransceiveLength} to retrieve the maximum number of bytes
+     * that can be sent with {@link #transceive}.
+     *
+     * <p>This is an I/O operation and will block until complete. It must
+     * not be called from the main application thread. A blocked call will be canceled with
+     * {@link IOException} if {@link #close} is called from another thread.
+     *
+     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+     *
+     * @param data bytes to send
+     * @return bytes received in response
+     * @throws TagLostException if the tag leaves the field
+     * @throws IOException if there is an I/O failure, or this operation is canceled
+     */
+    public byte[] transceive(byte[] data) throws IOException {
+        return transceive(data, true);
+    }
+
+    /**
+     * Return the maximum number of bytes that can be sent with {@link #transceive}.
+     * @return the maximum number of bytes that can be sent with {@link #transceive}.
+     */
+    public int getMaxTransceiveLength() {
+        return getMaxTransceiveLengthInternal();
+    }
+
+    /**
+     * Set the {@link #transceive} timeout in milliseconds.
+     *
+     * <p>The timeout only applies to {@link #transceive} on this object,
+     * and is reset to a default value when {@link #close} is called.
+     *
+     * <p>Setting a longer timeout may be useful when performing
+     * transactions that require a long processing time on the tag
+     * such as key generation.
+     *
+     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+     *
+     * @param timeout timeout value in milliseconds
+     * @throws SecurityException if the tag object is reused after the tag has left the field
+     */
+    public void setTimeout(int timeout) {
+        try {
+            int err = mTag.getTagService().setTimeout(TagTechnology.NFC_A, timeout);
+            if (err != ErrorCodes.SUCCESS) {
+                throw new IllegalArgumentException("The supplied timeout is not valid");
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "NFC service dead", e);
+        }
+    }
+
+    /**
+     * Get the current {@link #transceive} timeout in milliseconds.
+     *
+     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+     *
+     * @return timeout value in milliseconds
+     * @throws SecurityException if the tag object is reused after the tag has left the field
+     */
+    public int getTimeout() {
+        try {
+            return mTag.getTagService().getTimeout(TagTechnology.NFC_A);
+        } catch (RemoteException e) {
+            Log.e(TAG, "NFC service dead", e);
+            return 0;
+        }
+    }
+}
diff --git a/nfc/java/android/nfc/tech/NfcB.java b/nfc/java/android/nfc/tech/NfcB.java
new file mode 100644
index 0000000..3ebd47f
--- /dev/null
+++ b/nfc/java/android/nfc/tech/NfcB.java
@@ -0,0 +1,125 @@
+/*
+ * 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 android.nfc.tech;
+
+import android.nfc.Tag;
+import android.os.Bundle;
+import android.os.RemoteException;
+
+import java.io.IOException;
+
+/**
+ * Provides access to NFC-B (ISO 14443-3B) properties and I/O operations on a {@link Tag}.
+ *
+ * <p>Acquire a {@link NfcB} object using {@link #get}.
+ * <p>The primary NFC-B I/O operation is {@link #transceive}. Applications must
+ * implement their own protocol stack on top of {@link #transceive}.
+ *
+ * <p class="note"><strong>Note:</strong> Methods that perform I/O operations
+ * require the {@link android.Manifest.permission#NFC} permission.
+ */
+public final class NfcB extends BasicTagTechnology {
+    /** @hide */
+    public static final String EXTRA_APPDATA = "appdata";
+    /** @hide */
+    public static final String EXTRA_PROTINFO = "protinfo";
+
+    private byte[] mAppData;
+    private byte[] mProtInfo;
+
+    /**
+     * Get an instance of {@link NfcB} for the given tag.
+     * <p>Returns null if {@link NfcB} was not enumerated in {@link Tag#getTechList}.
+     * This indicates the tag does not support NFC-B.
+     * <p>Does not cause any RF activity and does not block.
+     *
+     * @param tag an NFC-B compatible tag
+     * @return NFC-B object
+     */
+    public static NfcB get(Tag tag) {
+        if (!tag.hasTech(TagTechnology.NFC_B)) return null;
+        try {
+            return new NfcB(tag);
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /** @hide */
+    public NfcB(Tag tag) throws RemoteException {
+        super(tag, TagTechnology.NFC_B);
+        Bundle extras = tag.getTechExtras(TagTechnology.NFC_B);
+        mAppData = extras.getByteArray(EXTRA_APPDATA);
+        mProtInfo = extras.getByteArray(EXTRA_PROTINFO);
+    }
+
+    /**
+     * Return the Application Data bytes from ATQB/SENSB_RES at tag discovery.
+     *
+     * <p>Does not cause any RF activity and does not block.
+     *
+     * @return Application Data bytes from ATQB/SENSB_RES bytes
+     */
+    public byte[] getApplicationData() {
+        return mAppData;
+    }
+
+    /**
+     * Return the Protocol Info bytes from ATQB/SENSB_RES at tag discovery.
+     *
+     * <p>Does not cause any RF activity and does not block.
+     *
+     * @return Protocol Info bytes from ATQB/SENSB_RES bytes
+     */
+    public byte[] getProtocolInfo() {
+        return mProtInfo;
+    }
+
+    /**
+     * Send raw NFC-B commands to the tag and receive the response.
+     *
+     * <p>Applications must not append the EoD (CRC) to the payload,
+     * it will be automatically calculated.
+     * <p>Applications must not send commands that manage the polling
+     * loop and initialization (SENSB_REQ, SLOT_MARKER etc).
+     *
+     * <p>Use {@link #getMaxTransceiveLength} to retrieve the maximum number of bytes
+     * that can be sent with {@link #transceive}.
+     *
+     * <p>This is an I/O operation and will block until complete. It must
+     * not be called from the main application thread. A blocked call will be canceled with
+     * {@link IOException} if {@link #close} is called from another thread.
+     *
+     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+     *
+     * @param data bytes to send
+     * @return bytes received in response
+     * @throws TagLostException if the tag leaves the field
+     * @throws IOException if there is an I/O failure, or this operation is canceled
+     */
+    public byte[] transceive(byte[] data) throws IOException {
+        return transceive(data, true);
+    }
+
+    /**
+     * Return the maximum number of bytes that can be sent with {@link #transceive}.
+     * @return the maximum number of bytes that can be sent with {@link #transceive}.
+     */
+    public int getMaxTransceiveLength() {
+        return getMaxTransceiveLengthInternal();
+    }
+}
diff --git a/nfc/java/android/nfc/tech/NfcBarcode.java b/nfc/java/android/nfc/tech/NfcBarcode.java
new file mode 100644
index 0000000..421ba78
--- /dev/null
+++ b/nfc/java/android/nfc/tech/NfcBarcode.java
@@ -0,0 +1,130 @@
+/*
+ * 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.
+ */
+
+package android.nfc.tech;
+
+import android.nfc.Tag;
+import android.os.Bundle;
+import android.os.RemoteException;
+
+/**
+ * Provides access to tags containing just a barcode.
+ *
+ * <p>Acquire an {@link NfcBarcode} object using {@link #get}.
+ *
+ */
+public final class NfcBarcode extends BasicTagTechnology {
+
+    /** Kovio Tags */
+    public static final int TYPE_KOVIO = 1;
+    public static final int TYPE_UNKNOWN = -1;
+
+    /** @hide */
+    public static final String EXTRA_BARCODE_TYPE = "barcodetype";
+
+    private int mType;
+
+    /**
+     * Get an instance of {@link NfcBarcode} for the given tag.
+     *
+     * <p>Returns null if {@link NfcBarcode} was not enumerated in {@link Tag#getTechList}.
+     *
+     * <p>Does not cause any RF activity and does not block.
+     *
+     * @param tag an NfcBarcode compatible tag
+     * @return NfcBarcode object
+     */
+    public static NfcBarcode get(Tag tag) {
+        if (!tag.hasTech(TagTechnology.NFC_BARCODE)) return null;
+        try {
+            return new NfcBarcode(tag);
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Internal constructor, to be used by NfcAdapter
+     * @hide
+     */
+    public NfcBarcode(Tag tag) throws RemoteException {
+        super(tag, TagTechnology.NFC_BARCODE);
+        Bundle extras = tag.getTechExtras(TagTechnology.NFC_BARCODE);
+        if (extras != null) {
+            mType = extras.getInt(EXTRA_BARCODE_TYPE);
+        } else {
+            throw new NullPointerException("NfcBarcode tech extras are null.");
+        }
+    }
+
+    /**
+     * Returns the NFC Barcode tag type.
+     *
+     * <p>Currently only one of {@link #TYPE_KOVIO} or {@link #TYPE_UNKNOWN}.
+     *
+     * <p>Does not cause any RF activity and does not block.
+     *
+     * @return the NFC Barcode tag type
+     */
+    public int getType() {
+        return mType;
+    }
+
+    /**
+     * Returns the barcode of an NfcBarcode tag.
+     *
+     * <p> Tags of {@link #TYPE_KOVIO} return 16 bytes:
+     *     <ul>
+     *     <p> The first byte is 0x80 ORd with a manufacturer ID, corresponding
+     *       to ISO/IEC 7816-6.
+     *     <p> The second byte describes the payload data format. Defined data
+     *       format types include the following:<ul>
+     *       <li>0x00: Reserved for manufacturer assignment</li>
+     *       <li>0x01: 96-bit URL with "http://www." prefix</li>
+     *       <li>0x02: 96-bit URL with "https://www." prefix</li>
+     *       <li>0x03: 96-bit URL with "http://" prefix</li>
+     *       <li>0x04: 96-bit URL with "https://" prefix</li>
+     *       <li>0x05: 96-bit GS1 EPC</li>
+     *       <li>0x06-0xFF: reserved</li>
+     *       </ul>
+     *     <p>The following 12 bytes are payload:<ul>
+     *       <li> In case of a URL payload, the payload is encoded in US-ASCII,
+     *            following the limitations defined in RFC3987.
+     *            {@see <a href="http://www.ietf.org/rfc/rfc3987.txt">RFC 3987</a>}</li>
+     *       <li> In case of GS1 EPC data, see <a href="http://www.gs1.org/gsmp/kc/epcglobal/tds/">
+     *            GS1 Electronic Product Code (EPC) Tag Data Standard (TDS)</a> for more details.
+     *       </li>
+     *     </ul>
+     *     <p>The last 2 bytes comprise the CRC.
+     *     </ul>
+     * <p>Does not cause any RF activity and does not block.
+     *
+     * @return a byte array containing the barcode
+     * @see <a href="http://www.thinfilm.no/docs/thinfilm-nfc-barcode-datasheet.pdf">
+     *      Thinfilm NFC Barcode tag specification (previously Kovio NFC Barcode)</a>
+     * @see <a href="http://www.thinfilm.no/docs/thinfilm-nfc-barcode-data-format.pdf">
+     *      Thinfilm NFC Barcode data format (previously Kovio NFC Barcode)</a>
+     */
+    public byte[] getBarcode() {
+        switch (mType) {
+            case TYPE_KOVIO:
+                // For Kovio tags the barcode matches the ID
+                return mTag.getId();
+            default:
+                return null;
+        }
+    }
+}
diff --git a/nfc/java/android/nfc/tech/NfcF.java b/nfc/java/android/nfc/tech/NfcF.java
new file mode 100644
index 0000000..2ccd388
--- /dev/null
+++ b/nfc/java/android/nfc/tech/NfcF.java
@@ -0,0 +1,177 @@
+/*
+ * 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 android.nfc.tech;
+
+import android.nfc.ErrorCodes;
+import android.nfc.Tag;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * Provides access to NFC-F (JIS 6319-4) properties and I/O operations on a {@link Tag}.
+ *
+ * <p>Acquire a {@link NfcF} object using {@link #get}.
+ * <p>The primary NFC-F I/O operation is {@link #transceive}. Applications must
+ * implement their own protocol stack on top of {@link #transceive}.
+ *
+ * <p class="note"><strong>Note:</strong> Methods that perform I/O operations
+ * require the {@link android.Manifest.permission#NFC} permission.
+ */
+public final class NfcF extends BasicTagTechnology {
+    private static final String TAG = "NFC";
+
+    /** @hide */
+    public static final String EXTRA_SC = "systemcode";
+    /** @hide */
+    public static final String EXTRA_PMM = "pmm";
+
+    private byte[] mSystemCode = null;
+    private byte[] mManufacturer = null;
+
+    /**
+     * Get an instance of {@link NfcF} for the given tag.
+     * <p>Returns null if {@link NfcF} was not enumerated in {@link Tag#getTechList}.
+     * This indicates the tag does not support NFC-F.
+     * <p>Does not cause any RF activity and does not block.
+     *
+     * @param tag an NFC-F compatible tag
+     * @return NFC-F object
+     */
+    public static NfcF get(Tag tag) {
+        if (!tag.hasTech(TagTechnology.NFC_F)) return null;
+        try {
+            return new NfcF(tag);
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /** @hide */
+    public NfcF(Tag tag) throws RemoteException {
+        super(tag, TagTechnology.NFC_F);
+        Bundle extras = tag.getTechExtras(TagTechnology.NFC_F);
+        if (extras != null) {
+            mSystemCode = extras.getByteArray(EXTRA_SC);
+            mManufacturer = extras.getByteArray(EXTRA_PMM);
+        }
+    }
+
+    /**
+     * Return the System Code bytes from tag discovery.
+     *
+     * <p>Does not cause any RF activity and does not block.
+     *
+     * @return System Code bytes
+     */
+    public byte[] getSystemCode() {
+      return mSystemCode;
+    }
+
+    /**
+     * Return the Manufacturer bytes from tag discovery.
+     *
+     * <p>Does not cause any RF activity and does not block.
+     *
+     * @return Manufacturer bytes
+     */
+    public byte[] getManufacturer() {
+      return mManufacturer;
+    }
+
+    /**
+     * Send raw NFC-F commands to the tag and receive the response.
+     *
+     * <p>Applications must not prefix the SoD (preamble and sync code)
+     * and/or append the EoD (CRC) to the payload, it will be automatically calculated.
+     *
+     * <p>A typical NFC-F frame for this method looks like:
+     * <pre>
+     * LENGTH (1 byte) --- CMD (1 byte) -- IDm (8 bytes) -- PARAMS (LENGTH - 10 bytes)
+     * </pre>
+     *
+     * <p>Use {@link #getMaxTransceiveLength} to retrieve the maximum amount of bytes
+     * that can be sent with {@link #transceive}.
+     *
+     * <p>This is an I/O operation and will block until complete. It must
+     * not be called from the main application thread. A blocked call will be canceled with
+     * {@link IOException} if {@link #close} is called from another thread.
+     *
+     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+     *
+     * @param data bytes to send
+     * @return bytes received in response
+     * @throws TagLostException if the tag leaves the field
+     * @throws IOException if there is an I/O failure, or this operation is canceled
+     */
+    public byte[] transceive(byte[] data) throws IOException {
+        return transceive(data, true);
+    }
+
+    /**
+     * Return the maximum number of bytes that can be sent with {@link #transceive}.
+     * @return the maximum number of bytes that can be sent with {@link #transceive}.
+     */
+    public int getMaxTransceiveLength() {
+        return getMaxTransceiveLengthInternal();
+    }
+
+    /**
+     * Set the {@link #transceive} timeout in milliseconds.
+     *
+     * <p>The timeout only applies to {@link #transceive} on this object,
+     * and is reset to a default value when {@link #close} is called.
+     *
+     * <p>Setting a longer timeout may be useful when performing
+     * transactions that require a long processing time on the tag
+     * such as key generation.
+     *
+     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+     *
+     * @param timeout timeout value in milliseconds
+     * @throws SecurityException if the tag object is reused after the tag has left the field
+     */
+    public void setTimeout(int timeout) {
+        try {
+            int err = mTag.getTagService().setTimeout(TagTechnology.NFC_F, timeout);
+            if (err != ErrorCodes.SUCCESS) {
+                throw new IllegalArgumentException("The supplied timeout is not valid");
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "NFC service dead", e);
+        }
+    }
+
+    /**
+     * Get the current {@link #transceive} timeout in milliseconds.
+     *
+     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+     *
+     * @return timeout value in milliseconds
+     * @throws SecurityException if the tag object is reused after the tag has left the field
+     */
+    public int getTimeout() {
+        try {
+            return mTag.getTagService().getTimeout(TagTechnology.NFC_F);
+        } catch (RemoteException e) {
+            Log.e(TAG, "NFC service dead", e);
+            return 0;
+        }
+    }
+}
diff --git a/nfc/java/android/nfc/tech/NfcV.java b/nfc/java/android/nfc/tech/NfcV.java
new file mode 100644
index 0000000..186c63b
--- /dev/null
+++ b/nfc/java/android/nfc/tech/NfcV.java
@@ -0,0 +1,126 @@
+/*
+ * 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 android.nfc.tech;
+
+import android.nfc.Tag;
+import android.os.Bundle;
+import android.os.RemoteException;
+
+import java.io.IOException;
+
+/**
+ * Provides access to NFC-V (ISO 15693) properties and I/O operations on a {@link Tag}.
+ *
+ * <p>Acquire a {@link NfcV} object using {@link #get}.
+ * <p>The primary NFC-V I/O operation is {@link #transceive}. Applications must
+ * implement their own protocol stack on top of {@link #transceive}.
+ *
+ * <p class="note"><strong>Note:</strong> Methods that perform I/O operations
+ * require the {@link android.Manifest.permission#NFC} permission.
+ */
+public final class NfcV extends BasicTagTechnology {
+    /** @hide */
+    public static final String EXTRA_RESP_FLAGS = "respflags";
+
+    /** @hide */
+    public static final String EXTRA_DSFID = "dsfid";
+
+    private byte mRespFlags;
+    private byte mDsfId;
+
+    /**
+     * Get an instance of {@link NfcV} for the given tag.
+     * <p>Returns null if {@link NfcV} was not enumerated in {@link Tag#getTechList}.
+     * This indicates the tag does not support NFC-V.
+     * <p>Does not cause any RF activity and does not block.
+     *
+     * @param tag an NFC-V compatible tag
+     * @return NFC-V object
+     */
+    public static NfcV get(Tag tag) {
+        if (!tag.hasTech(TagTechnology.NFC_V)) return null;
+        try {
+            return new NfcV(tag);
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /** @hide */
+    public NfcV(Tag tag) throws RemoteException {
+        super(tag, TagTechnology.NFC_V);
+        Bundle extras = tag.getTechExtras(TagTechnology.NFC_V);
+        mRespFlags = extras.getByte(EXTRA_RESP_FLAGS);
+        mDsfId = extras.getByte(EXTRA_DSFID);
+    }
+
+    /**
+     * Return the Response Flag bytes from tag discovery.
+     *
+     * <p>Does not cause any RF activity and does not block.
+     *
+     * @return Response Flag bytes
+     */
+    public byte getResponseFlags() {
+        return mRespFlags;
+    }
+
+    /**
+     * Return the DSF ID bytes from tag discovery.
+     *
+     * <p>Does not cause any RF activity and does not block.
+     *
+     * @return DSF ID bytes
+     */
+    public byte getDsfId() {
+        return mDsfId;
+    }
+
+    /**
+     * Send raw NFC-V commands to the tag and receive the response.
+     *
+     * <p>Applications must not append the CRC to the payload,
+     * it will be automatically calculated. The application does
+     * provide FLAGS, CMD and PARAMETER bytes.
+     *
+     * <p>Use {@link #getMaxTransceiveLength} to retrieve the maximum amount of bytes
+     * that can be sent with {@link #transceive}.
+     *
+     * <p>This is an I/O operation and will block until complete. It must
+     * not be called from the main application thread. A blocked call will be canceled with
+     * {@link IOException} if {@link #close} is called from another thread.
+     *
+     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+     *
+     * @param data bytes to send
+     * @return bytes received in response
+     * @throws TagLostException if the tag leaves the field
+     * @throws IOException if there is an I/O failure, or this operation is canceled
+     */
+    public byte[] transceive(byte[] data) throws IOException {
+        return transceive(data, true);
+    }
+
+
+    /**
+     * Return the maximum number of bytes that can be sent with {@link #transceive}.
+     * @return the maximum number of bytes that can be sent with {@link #transceive}.
+     */
+    public int getMaxTransceiveLength() {
+        return getMaxTransceiveLengthInternal();
+    }
+}
diff --git a/nfc/java/android/nfc/tech/OWNERS b/nfc/java/android/nfc/tech/OWNERS
new file mode 100644
index 0000000..35e9713
--- /dev/null
+++ b/nfc/java/android/nfc/tech/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 48448
+include platform/packages/apps/Nfc:/OWNERS
diff --git a/nfc/java/android/nfc/tech/TagTechnology.java b/nfc/java/android/nfc/tech/TagTechnology.java
new file mode 100644
index 0000000..839fe42
--- /dev/null
+++ b/nfc/java/android/nfc/tech/TagTechnology.java
@@ -0,0 +1,224 @@
+/*
+ * 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 android.nfc.tech;
+
+import android.nfc.Tag;
+
+import java.io.Closeable;
+import java.io.IOException;
+
+/**
+ * {@link TagTechnology} is an interface to a technology in a {@link Tag}.
+ * <p>
+ * Obtain a {@link TagTechnology} implementation by calling the static method <code>get()</code>
+ * on the implementation class.
+ * <p>
+ * NFC tags are based on a number of independently developed technologies and offer a
+ * wide range of capabilities. The
+ * {@link TagTechnology} implementations provide access to these different
+ * technologies and capabilities. Some sub-classes map to technology
+ * specification (for example {@link NfcA}, {@link IsoDep}, others map to
+ * pseudo-technologies or capabilities (for example {@link Ndef}, {@link NdefFormatable}).
+ * <p>
+ * It is mandatory for all Android NFC devices to provide the following
+ * {@link TagTechnology} implementations.
+ * <ul>
+ * <li>{@link NfcA} (also known as ISO 14443-3A)
+ * <li>{@link NfcB} (also known as ISO 14443-3B)
+ * <li>{@link NfcF} (also known as JIS 6319-4)
+ * <li>{@link NfcV} (also known as ISO 15693)
+ * <li>{@link IsoDep}
+ * <li>{@link Ndef} on NFC Forum Type 1, Type 2, Type 3 or Type 4 compliant tags
+ * </ul>
+ * It is optional for Android NFC devices to provide the following
+ * {@link TagTechnology} implementations. If it is not provided, the
+ * Android device will never enumerate that class via {@link Tag#getTechList}.
+ * <ul>
+ * <li>{@link MifareClassic}
+ * <li>{@link MifareUltralight}
+ * <li>{@link NfcBarcode}
+ * <li>{@link NdefFormatable} must only be enumerated on tags for which this Android device
+ * is capable of formatting. Proprietary knowledge is often required to format a tag
+ * to make it NDEF compatible.
+ * </ul>
+ * <p>
+ * {@link TagTechnology} implementations provide methods that fall into two classes:
+ * <em>cached getters</em> and <em>I/O operations</em>.
+ * <h4>Cached getters</h4>
+ * These methods (usually prefixed by <code>get</code> or <code>is</code>) return
+ * properties of the tag, as determined at discovery time. These methods will never
+ * block or cause RF activity, and do not require {@link #connect} to have been called.
+ * They also never update, for example if a property is changed by an I/O operation with a tag
+ * then the cached getter will still return the result from tag discovery time.
+ * <h4>I/O operations</h4>
+ * I/O operations may require RF activity, and may block. They have the following semantics.
+ * <ul>
+ * <li>{@link #connect} must be called before using any other I/O operation.
+ * <li>{@link #close} must be called after completing I/O operations with a
+ * {@link TagTechnology}, and it will cancel all other blocked I/O operations on other threads
+ * (including {@link #connect} with {@link IOException}.
+ * <li>Only one {@link TagTechnology} can be connected at a time. Other calls to
+ * {@link #connect} will return {@link IOException}.
+ * <li>I/O operations may block, and should never be called on the main application
+ * thread.
+ * </ul>
+ *
+ * <p class="note"><strong>Note:</strong> Methods that perform I/O operations
+ * require the {@link android.Manifest.permission#NFC} permission.
+ */
+public interface TagTechnology extends Closeable {
+    /**
+     * This technology is an instance of {@link NfcA}.
+     * <p>Support for this technology type is mandatory.
+     * @hide
+     */
+    public static final int NFC_A = 1;
+
+    /**
+     * This technology is an instance of {@link NfcB}.
+     * <p>Support for this technology type is mandatory.
+     * @hide
+     */
+    public static final int NFC_B = 2;
+
+    /**
+     * This technology is an instance of {@link IsoDep}.
+     * <p>Support for this technology type is mandatory.
+     * @hide
+     */
+    public static final int ISO_DEP = 3;
+
+    /**
+     * This technology is an instance of {@link NfcF}.
+     * <p>Support for this technology type is mandatory.
+     * @hide
+     */
+    public static final int NFC_F = 4;
+
+    /**
+     * This technology is an instance of {@link NfcV}.
+     * <p>Support for this technology type is mandatory.
+     * @hide
+     */
+    public static final int NFC_V = 5;
+
+    /**
+     * This technology is an instance of {@link Ndef}.
+     * <p>Support for this technology type is mandatory.
+     * @hide
+     */
+    public static final int NDEF = 6;
+
+    /**
+     * This technology is an instance of {@link NdefFormatable}.
+     * <p>Support for this technology type is mandatory.
+     * @hide
+     */
+    public static final int NDEF_FORMATABLE = 7;
+
+    /**
+     * This technology is an instance of {@link MifareClassic}.
+     * <p>Support for this technology type is optional. If a stack doesn't support this technology
+     * type tags using it must still be discovered and present the lower level radio interface
+     * technologies in use.
+     * @hide
+     */
+    public static final int MIFARE_CLASSIC = 8;
+
+    /**
+     * This technology is an instance of {@link MifareUltralight}.
+     * <p>Support for this technology type is optional. If a stack doesn't support this technology
+     * type tags using it must still be discovered and present the lower level radio interface
+     * technologies in use.
+     * @hide
+     */
+    public static final int MIFARE_ULTRALIGHT = 9;
+
+    /**
+     * This technology is an instance of {@link NfcBarcode}.
+     * <p>Support for this technology type is optional. If a stack doesn't support this technology
+     * type tags using it must still be discovered and present the lower level radio interface
+     * technologies in use.
+     * @hide
+     */
+    public static final int NFC_BARCODE = 10;
+
+    /**
+     * Get the {@link Tag} object backing this {@link TagTechnology} object.
+     * @return the {@link Tag} backing this {@link TagTechnology} object.
+     */
+    public Tag getTag();
+
+    /**
+     * Enable I/O operations to the tag from this {@link TagTechnology} object.
+     * <p>May cause RF activity and may block. Must not be called
+     * from the main application thread. A blocked call will be canceled with
+     * {@link IOException} by calling {@link #close} from another thread.
+     * <p>Only one {@link TagTechnology} object can be connected to a {@link Tag} at a time.
+     * <p>Applications must call {@link #close} when I/O operations are complete.
+     *
+     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+     *
+     * @see #close()
+     * @throws TagLostException if the tag leaves the field
+     * @throws IOException if there is an I/O failure, or connect is canceled
+     * @throws SecurityException if the tag object is reused after the tag has left the field
+     */
+    public void connect() throws IOException;
+
+    /**
+     * Re-connect to the {@link Tag} associated with this connection. Reconnecting to a tag can be
+     * used to reset the state of the tag itself.
+     *
+     * <p>May cause RF activity and may block. Must not be called
+     * from the main application thread. A blocked call will be canceled with
+     * {@link IOException} by calling {@link #close} from another thread.
+     *
+     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+     *
+     * @see #connect()
+     * @see #close()
+     * @throws TagLostException if the tag leaves the field
+     * @throws IOException if there is an I/O failure, or connect is canceled
+     * @throws SecurityException if the tag object is reused after the tag has left the field
+     * @hide
+     */
+    public void reconnect() throws IOException;
+
+    /**
+     * Disable I/O operations to the tag from this {@link TagTechnology} object, and release resources.
+     * <p>Also causes all blocked I/O operations on other thread to be canceled and
+     * return with {@link IOException}.
+     *
+     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+     *
+     * @see #connect()
+     * @throws SecurityException if the tag object is reused after the tag has left the field
+     */
+    public void close() throws IOException;
+
+    /**
+     * Helper to indicate if I/O operations should be possible.
+     *
+     * <p>Returns true if {@link #connect} has completed, and {@link #close} has not been
+     * called, and the {@link Tag} is not known to be out of range.
+     * <p>Does not cause RF activity, and does not block.
+     *
+     * @return true if I/O operations should be possible
+     */
+    public boolean isConnected();
+}
diff --git a/nfc/java/android/nfc/tech/package.html b/nfc/java/android/nfc/tech/package.html
new file mode 100644
index 0000000..a99828f
--- /dev/null
+++ b/nfc/java/android/nfc/tech/package.html
@@ -0,0 +1,13 @@
+<HTML>
+<BODY>
+<p>
+These classes provide access to a tag technology's features, which vary by the type
+of tag that is scanned. A scanned tag can support multiple technologies, and you can find
+out what they are by calling {@link android.nfc.Tag#getTechList getTechList()}.</p>
+
+<p>For more information on dealing with tag technologies and handling the ones that you care about, see
+<a href="{@docRoot}guide/topics/nfc/index.html#dispatch">The Tag Dispatch System</a>.
+The {@link android.nfc.tech.TagTechnology} interface provides an overview of the
+supported technologies.</p>
+</BODY>
+</HTML>
diff --git a/nfc/tests/Android.bp b/nfc/tests/Android.bp
new file mode 100644
index 0000000..62566ee
--- /dev/null
+++ b/nfc/tests/Android.bp
@@ -0,0 +1,40 @@
+// Copyright 2021 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 {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+    name: "NfcManagerTests",
+    static_libs: [
+        "androidx.test.ext.junit",
+        "androidx.test.rules",
+        "mockito-target-minus-junit4",
+        "truth",
+    ],
+    libs: [
+        "framework-nfc.impl",
+        "android.test.runner",
+    ],
+    srcs: ["src/**/*.java"],
+    platform_apis: true,
+    certificate: "platform",
+    test_suites: ["device-tests"],
+}
diff --git a/nfc/tests/AndroidManifest.xml b/nfc/tests/AndroidManifest.xml
new file mode 100644
index 0000000..99e2c34c
--- /dev/null
+++ b/nfc/tests/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2021 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.nfc">
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <!-- This is a self-instrumenting test package. -->
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="android.nfc"
+                     android:label="NFC Manager Tests">
+    </instrumentation>
+
+</manifest>
+
diff --git a/nfc/tests/AndroidTest.xml b/nfc/tests/AndroidTest.xml
new file mode 100644
index 0000000..490d6f5
--- /dev/null
+++ b/nfc/tests/AndroidTest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2021 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.
+-->
+<configuration description="Config for NFC Manager test cases">
+    <option name="test-suite-tag" value="apct"/>
+    <option name="test-suite-tag" value="apct-instrumentation"/>
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="NfcManagerTests.apk" />
+    </target_preparer>
+
+    <option name="test-suite-tag" value="apct"/>
+    <option name="test-tag" value="NfcManagerTests"/>
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="android.nfc" />
+        <option name="hidden-api-checks" value="false"/>
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner"/>
+    </test>
+</configuration>
diff --git a/nfc/tests/src/android/nfc/NfcControllerAlwaysOnListenerTest.java b/nfc/tests/src/android/nfc/NfcControllerAlwaysOnListenerTest.java
new file mode 100644
index 0000000..48f4288
--- /dev/null
+++ b/nfc/tests/src/android/nfc/NfcControllerAlwaysOnListenerTest.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright 2021 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 android.nfc;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.nfc.NfcAdapter.ControllerAlwaysOnListener;
+import android.os.RemoteException;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * Test of {@link NfcControllerAlwaysOnListener}.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class NfcControllerAlwaysOnListenerTest {
+
+    private INfcAdapter mNfcAdapter = mock(INfcAdapter.class);
+
+    private Throwable mThrowRemoteException = new RemoteException("RemoteException");
+
+    private static Executor getExecutor() {
+        return new Executor() {
+            @Override
+            public void execute(Runnable command) {
+                command.run();
+            }
+        };
+    }
+
+    private static void verifyListenerInvoked(ControllerAlwaysOnListener listener) {
+        verify(listener, times(1)).onControllerAlwaysOnChanged(anyBoolean());
+    }
+
+    @Test
+    public void testRegister_RegisterUnregisterWhenNotSupported() throws RemoteException {
+        // isControllerAlwaysOnSupported() returns false, not supported.
+        doReturn(false).when(mNfcAdapter).isControllerAlwaysOnSupported();
+        NfcControllerAlwaysOnListener mListener =
+                new NfcControllerAlwaysOnListener(mNfcAdapter);
+        ControllerAlwaysOnListener mockListener1 = mock(ControllerAlwaysOnListener.class);
+        ControllerAlwaysOnListener mockListener2 = mock(ControllerAlwaysOnListener.class);
+
+        // Verify that the state listener will not registered with the NFC Adapter
+        mListener.register(getExecutor(), mockListener1);
+        verify(mNfcAdapter, times(0)).registerControllerAlwaysOnListener(any());
+
+        // Register a second client and no any call to NFC Adapter
+        mListener.register(getExecutor(), mockListener2);
+        verify(mNfcAdapter, times(0)).registerControllerAlwaysOnListener(any());
+
+        // Unregister first listener, and no any call to NFC Adapter
+        mListener.unregister(mockListener1);
+        verify(mNfcAdapter, times(0)).registerControllerAlwaysOnListener(any());
+        verify(mNfcAdapter, times(0)).unregisterControllerAlwaysOnListener(any());
+
+        // Unregister second listener, and no any call to NFC Adapter
+        mListener.unregister(mockListener2);
+        verify(mNfcAdapter, times(0)).registerControllerAlwaysOnListener(any());
+        verify(mNfcAdapter, times(0)).unregisterControllerAlwaysOnListener(any());
+    }
+
+    @Test
+    public void testRegister_RegisterUnregister() throws RemoteException {
+        doReturn(true).when(mNfcAdapter).isControllerAlwaysOnSupported();
+        NfcControllerAlwaysOnListener mListener =
+                new NfcControllerAlwaysOnListener(mNfcAdapter);
+        ControllerAlwaysOnListener mockListener1 = mock(ControllerAlwaysOnListener.class);
+        ControllerAlwaysOnListener mockListener2 = mock(ControllerAlwaysOnListener.class);
+
+        // Verify that the state listener registered with the NFC Adapter
+        mListener.register(getExecutor(), mockListener1);
+        verify(mNfcAdapter, times(1)).registerControllerAlwaysOnListener(any());
+
+        // Register a second client and no new call to NFC Adapter
+        mListener.register(getExecutor(), mockListener2);
+        verify(mNfcAdapter, times(1)).registerControllerAlwaysOnListener(any());
+
+        // Unregister first listener
+        mListener.unregister(mockListener1);
+        verify(mNfcAdapter, times(1)).registerControllerAlwaysOnListener(any());
+        verify(mNfcAdapter, times(0)).unregisterControllerAlwaysOnListener(any());
+
+        // Unregister second listener and the state listener registered with the NFC Adapter
+        mListener.unregister(mockListener2);
+        verify(mNfcAdapter, times(1)).registerControllerAlwaysOnListener(any());
+        verify(mNfcAdapter, times(1)).unregisterControllerAlwaysOnListener(any());
+    }
+
+    @Test
+    public void testRegister_FirstRegisterFails() throws RemoteException {
+        doReturn(true).when(mNfcAdapter).isControllerAlwaysOnSupported();
+        NfcControllerAlwaysOnListener mListener =
+                new NfcControllerAlwaysOnListener(mNfcAdapter);
+        ControllerAlwaysOnListener mockListener1 = mock(ControllerAlwaysOnListener.class);
+        ControllerAlwaysOnListener mockListener2 = mock(ControllerAlwaysOnListener.class);
+
+        // Throw a remote exception whenever first registering
+        doThrow(mThrowRemoteException).when(mNfcAdapter).registerControllerAlwaysOnListener(
+                any());
+
+        mListener.register(getExecutor(), mockListener1);
+        verify(mNfcAdapter, times(1)).registerControllerAlwaysOnListener(any());
+
+        // No longer throw an exception, instead succeed
+        doNothing().when(mNfcAdapter).registerControllerAlwaysOnListener(any());
+
+        // Register a different listener
+        mListener.register(getExecutor(), mockListener2);
+        verify(mNfcAdapter, times(2)).registerControllerAlwaysOnListener(any());
+
+        // Ensure first and second listener were invoked
+        mListener.onControllerAlwaysOnChanged(true);
+        verifyListenerInvoked(mockListener1);
+        verifyListenerInvoked(mockListener2);
+    }
+
+    @Test
+    public void testRegister_RegisterSameListenerTwice() throws RemoteException {
+        doReturn(true).when(mNfcAdapter).isControllerAlwaysOnSupported();
+        NfcControllerAlwaysOnListener mListener =
+                new NfcControllerAlwaysOnListener(mNfcAdapter);
+        ControllerAlwaysOnListener mockListener = mock(ControllerAlwaysOnListener.class);
+
+        // Register the same listener Twice
+        mListener.register(getExecutor(), mockListener);
+        mListener.register(getExecutor(), mockListener);
+        verify(mNfcAdapter, times(1)).registerControllerAlwaysOnListener(any());
+
+        // Invoke a state change and ensure the listener is only called once
+        mListener.onControllerAlwaysOnChanged(true);
+        verifyListenerInvoked(mockListener);
+    }
+
+    @Test
+    public void testNotify_AllListenersNotified() throws RemoteException {
+        doReturn(true).when(mNfcAdapter).isControllerAlwaysOnSupported();
+        NfcControllerAlwaysOnListener listener = new NfcControllerAlwaysOnListener(mNfcAdapter);
+        List<ControllerAlwaysOnListener> mockListeners = new ArrayList<>();
+        for (int i = 0; i < 10; i++) {
+            ControllerAlwaysOnListener mockListener = mock(ControllerAlwaysOnListener.class);
+            listener.register(getExecutor(), mockListener);
+            mockListeners.add(mockListener);
+        }
+
+        // Invoke a state change and ensure all listeners are invoked
+        listener.onControllerAlwaysOnChanged(true);
+        for (ControllerAlwaysOnListener mListener : mockListeners) {
+            verifyListenerInvoked(mListener);
+        }
+    }
+
+    @Test
+    public void testStateChange_CorrectValue() throws RemoteException {
+        doReturn(true).when(mNfcAdapter).isControllerAlwaysOnSupported();
+        runStateChangeValue(true, true);
+        runStateChangeValue(false, false);
+
+    }
+
+    private void runStateChangeValue(boolean isEnabledIn, boolean isEnabledOut) {
+        NfcControllerAlwaysOnListener listener = new NfcControllerAlwaysOnListener(mNfcAdapter);
+        ControllerAlwaysOnListener mockListener = mock(ControllerAlwaysOnListener.class);
+        listener.register(getExecutor(), mockListener);
+        listener.onControllerAlwaysOnChanged(isEnabledIn);
+        verify(mockListener, times(1)).onControllerAlwaysOnChanged(isEnabledOut);
+        verify(mockListener, times(0)).onControllerAlwaysOnChanged(!isEnabledOut);
+    }
+}
diff --git a/nfc/tests/src/android/nfc/TechListParcelTest.java b/nfc/tests/src/android/nfc/TechListParcelTest.java
new file mode 100644
index 0000000..a12bbbc
--- /dev/null
+++ b/nfc/tests/src/android/nfc/TechListParcelTest.java
@@ -0,0 +1,82 @@
+/*
+ * 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 android.nfc;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Parcel;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+
+@RunWith(AndroidJUnit4.class)
+public class TechListParcelTest {
+
+    private static final String[] TECH_LIST_1 = new String[] { "tech1.1", "tech1.2" };
+    private static final String[] TECH_LIST_2 = new String[] { "tech2.1" };
+    private static final String[] TECH_LIST_EMPTY = new String[] {};
+
+    @Test
+    public void testWriteParcel() {
+        TechListParcel techListParcel = new TechListParcel(TECH_LIST_1, TECH_LIST_2);
+
+        Parcel parcel = Parcel.obtain();
+        techListParcel.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        TechListParcel actualTechList =
+                TechListParcel.CREATOR.createFromParcel(parcel);
+        parcel.recycle();
+
+        assertThat(actualTechList.getTechLists().length).isEqualTo(2);
+        assertThat(Arrays.equals(actualTechList.getTechLists()[0], TECH_LIST_1)).isTrue();
+        assertThat(Arrays.equals(actualTechList.getTechLists()[1], TECH_LIST_2)).isTrue();
+    }
+
+    @Test
+    public void testWriteParcelArrayEmpty() {
+        TechListParcel techListParcel = new TechListParcel();
+
+        Parcel parcel = Parcel.obtain();
+        techListParcel.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        TechListParcel actualTechList =
+                TechListParcel.CREATOR.createFromParcel(parcel);
+        parcel.recycle();
+
+        assertThat(actualTechList.getTechLists().length).isEqualTo(0);
+    }
+
+    @Test
+    public void testWriteParcelElementEmpty() {
+        TechListParcel techListParcel = new TechListParcel(TECH_LIST_EMPTY);
+
+        Parcel parcel = Parcel.obtain();
+        techListParcel.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        TechListParcel actualTechList =
+                TechListParcel.CREATOR.createFromParcel(parcel);
+        parcel.recycle();
+
+        assertThat(actualTechList.getTechLists().length).isEqualTo(1);
+        assertThat(Arrays.equals(actualTechList.getTechLists()[0], TECH_LIST_EMPTY)).isTrue();
+    }
+
+}