summaryrefslogtreecommitdiff
path: root/nfc/java/android
diff options
context:
space:
mode:
Diffstat (limited to 'nfc/java/android')
-rw-r--r--nfc/java/android/nfc/INfcAdapter.aidl18
-rw-r--r--nfc/java/android/nfc/INfcCardEmulation.aidl3
-rw-r--r--nfc/java/android/nfc/INfcOemExtensionCallback.aidl25
-rw-r--r--nfc/java/android/nfc/INfcWlcStateListener.aidl30
-rw-r--r--nfc/java/android/nfc/NfcActivityManager.java16
-rw-r--r--nfc/java/android/nfc/NfcAdapter.java316
-rw-r--r--nfc/java/android/nfc/NfcOemExtension.java160
-rw-r--r--nfc/java/android/nfc/NfcWlcStateListener.java122
-rw-r--r--nfc/java/android/nfc/WlcListenerDeviceInfo.aidl19
-rw-r--r--nfc/java/android/nfc/WlcListenerDeviceInfo.java145
-rw-r--r--nfc/java/android/nfc/cardemulation/ApduServiceInfo.java118
-rw-r--r--nfc/java/android/nfc/cardemulation/CardEmulation.java113
-rw-r--r--nfc/java/android/nfc/cardemulation/HostApduService.java96
-rw-r--r--nfc/java/android/nfc/cardemulation/PollingFrame.java243
-rw-r--r--nfc/java/android/nfc/flags.aconfig16
15 files changed, 1310 insertions, 130 deletions
diff --git a/nfc/java/android/nfc/INfcAdapter.aidl b/nfc/java/android/nfc/INfcAdapter.aidl
index 8fea5af8fda1..e151a0e4d6e2 100644
--- a/nfc/java/android/nfc/INfcAdapter.aidl
+++ b/nfc/java/android/nfc/INfcAdapter.aidl
@@ -28,10 +28,13 @@ import android.nfc.INfcVendorNciCallback;
import android.nfc.INfcTag;
import android.nfc.INfcCardEmulation;
import android.nfc.INfcFCardEmulation;
+import android.nfc.INfcOemExtensionCallback;
import android.nfc.INfcUnlockHandler;
import android.nfc.ITagRemovedCallback;
import android.nfc.INfcDta;
+import android.nfc.INfcWlcStateListener;
import android.nfc.NfcAntennaInfo;
+import android.nfc.WlcListenerDeviceInfo;
import android.os.Bundle;
/**
@@ -86,9 +89,24 @@ interface INfcAdapter
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)")
boolean enableReaderOption(boolean enable);
boolean isObserveModeSupported();
+ boolean isObserveModeEnabled();
boolean setObserveMode(boolean enabled);
+
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)")
+ boolean setWlcEnabled(boolean enable);
+ boolean isWlcEnabled();
+ void registerWlcStateListener(in INfcWlcStateListener listener);
+ void unregisterWlcStateListener(in INfcWlcStateListener listener);
+ WlcListenerDeviceInfo getWlcListenerDeviceInfo();
+
void updateDiscoveryTechnology(IBinder b, int pollFlags, int listenFlags);
+
+ void notifyPollingLoop(in Bundle frame);
+ void notifyHceDeactivated();
int sendVendorNciMessage(int mt, int gid, int oid, in byte[] payload);
void registerVendorExtensionCallback(in INfcVendorNciCallback callbacks);
void unregisterVendorExtensionCallback(in INfcVendorNciCallback callbacks);
+ void registerOemExtensionCallback(INfcOemExtensionCallback callbacks);
+ void unregisterOemExtensionCallback(INfcOemExtensionCallback callbacks);
+ void clearPreference();
}
diff --git a/nfc/java/android/nfc/INfcCardEmulation.aidl b/nfc/java/android/nfc/INfcCardEmulation.aidl
index f4b46046bc3e..85a07b74871b 100644
--- a/nfc/java/android/nfc/INfcCardEmulation.aidl
+++ b/nfc/java/android/nfc/INfcCardEmulation.aidl
@@ -30,8 +30,9 @@ interface INfcCardEmulation
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 setShouldDefaultToObserveModeForService(int userId, in android.content.ComponentName service, boolean enable);
boolean registerAidGroupForService(int userHandle, in ComponentName service, in AidGroup aidGroup);
+ boolean registerPollingLoopFilterForService(int userHandle, in ComponentName service, in String pollingLoopFilter, boolean autoTransact);
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);
diff --git a/nfc/java/android/nfc/INfcOemExtensionCallback.aidl b/nfc/java/android/nfc/INfcOemExtensionCallback.aidl
new file mode 100644
index 000000000000..6c9096d5d03e
--- /dev/null
+++ b/nfc/java/android/nfc/INfcOemExtensionCallback.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.nfc;
+
+import android.nfc.Tag;
+
+/**
+ * @hide
+ */
+interface INfcOemExtensionCallback {
+ void onTagConnected(boolean connected, in Tag tag);
+}
diff --git a/nfc/java/android/nfc/INfcWlcStateListener.aidl b/nfc/java/android/nfc/INfcWlcStateListener.aidl
new file mode 100644
index 000000000000..584eb9a128b4
--- /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.WlcListenerDeviceInfo;
+/**
+ * @hide
+ */
+oneway interface INfcWlcStateListener {
+ /**
+ * Called whenever NFC WLC state changes
+ *
+ * @param wlcListenerDeviceInfo NFC wlc listener information
+ */
+ void onWlcStateChanged(in WlcListenerDeviceInfo wlcListenerDeviceInfo);
+}
diff --git a/nfc/java/android/nfc/NfcActivityManager.java b/nfc/java/android/nfc/NfcActivityManager.java
index f03fc0af86b3..0e40db612708 100644
--- a/nfc/java/android/nfc/NfcActivityManager.java
+++ b/nfc/java/android/nfc/NfcActivityManager.java
@@ -195,16 +195,25 @@ public final class NfcActivityManager extends IAppCallback.Stub
Bundle extras) {
boolean isResumed;
Binder token;
+ int pollTech, listenTech;
synchronized (NfcActivityManager.this) {
NfcActivityState state = getActivityState(activity);
state.readerCallback = callback;
state.readerModeFlags = flags;
state.readerModeExtras = extras;
+ pollTech = state.mPollTech;
+ listenTech = state.mListenTech;
token = state.token;
isResumed = state.resumed;
}
if (isResumed) {
- setReaderMode(token, flags, extras);
+ if (listenTech != NfcAdapter.FLAG_USE_ALL_TECH
+ || pollTech != NfcAdapter.FLAG_USE_ALL_TECH) {
+ throw new IllegalStateException(
+ "Cannot be used when alternative DiscoveryTechnology is set");
+ } else {
+ setReaderMode(token, flags, extras);
+ }
}
}
@@ -385,15 +394,12 @@ public final class NfcActivityManager extends IAppCallback.Stub
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) {
+ if (isResumed) {
changeDiscoveryTech(token, NfcAdapter.FLAG_USE_ALL_TECH, NfcAdapter.FLAG_USE_ALL_TECH);
}
diff --git a/nfc/java/android/nfc/NfcAdapter.java b/nfc/java/android/nfc/NfcAdapter.java
index 40bbe746045c..fe78c9b23353 100644
--- a/nfc/java/android/nfc/NfcAdapter.java
+++ b/nfc/java/android/nfc/NfcAdapter.java
@@ -16,6 +16,7 @@
package android.nfc;
+import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
@@ -27,6 +28,7 @@ import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.annotation.UserIdInt;
import android.app.Activity;
import android.app.PendingIntent;
@@ -34,7 +36,9 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.net.Uri;
+import android.nfc.cardemulation.PollingFrame;
import android.nfc.tech.MifareClassic;
import android.nfc.tech.Ndef;
import android.nfc.tech.NfcA;
@@ -76,6 +80,7 @@ public final class NfcAdapter {
static final String TAG = "NFC";
private final NfcControllerAlwaysOnListener mControllerAlwaysOnListener;
+ private final NfcWlcStateListener mNfcWlcStateListener;
private final NfcVendorNciCallbackListener mNfcVendorNciCallbackListener;
/**
@@ -483,6 +488,25 @@ public final class NfcAdapter {
"android.nfc.action.REQUIRE_UNLOCK_FOR_NFC";
/**
+ * Intent action to start a NFC resolver activity in a customized share session with list of
+ * {@link ResolveInfo}.
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ @RequiresPermission(Manifest.permission.SHOW_CUSTOMIZED_RESOLVER)
+ public static final String ACTION_SHOW_NFC_RESOLVER = "android.nfc.action.SHOW_NFC_RESOLVER";
+
+ /**
+ * "Extras" key for an ArrayList of {@link ResolveInfo} records which are to be shown as the
+ * targets in the customized share session.
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ public static final String EXTRA_RESOLVE_INFOS = "android.nfc.extra.RESOLVE_INFOS";
+
+ /**
* The requested app is correctly added to the Tag intent app preference.
*
* @see #setTagIntentAppPreferenceForUser(int userId, String pkg, boolean allow)
@@ -525,6 +549,7 @@ public final class NfcAdapter {
static boolean sIsInitialized = false;
static boolean sHasNfcFeature;
static boolean sHasCeFeature;
+ static boolean sHasNfcWlcFeature;
static Object sLock = new Object();
@@ -556,6 +581,7 @@ public final class NfcAdapter {
final Context mContext;
final HashMap<NfcUnlockHandler, INfcUnlockHandler> mNfcUnlockHandlers;
final Object mLock;
+ final NfcOemExtension mNfcOemExtension;
ITagRemovedCallback mTagRemovedListener; // protected by mLock
@@ -737,8 +763,9 @@ public final class NfcAdapter {
|| 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) {
+ if (!sHasNfcFeature && !sHasCeFeature && !sHasNfcWlcFeature) {
Log.v(TAG, "this device does not have NFC support");
throw new UnsupportedOperationException();
}
@@ -863,7 +890,9 @@ public final class NfcAdapter {
mTagRemovedListener = null;
mLock = new Object();
mControllerAlwaysOnListener = new NfcControllerAlwaysOnListener(getService());
+ mNfcWlcStateListener = new NfcWlcStateListener(getService());
mNfcVendorNciCallbackListener = new NfcVendorNciCallbackListener(getService());
+ mNfcOemExtension = new NfcOemExtension(mContext, this);
}
/**
@@ -1032,7 +1061,8 @@ public final class NfcAdapter {
Log.e(TAG, "Failed to recover NFC Service.");
}
}
- return serviceState && (isTagReadingEnabled() || isCardEmulationEnabled());
+ return serviceState
+ && (isTagReadingEnabled() || isCardEmulationEnabled() || sHasNfcWlcFeature);
}
/**
@@ -1201,18 +1231,16 @@ public final class NfcAdapter {
}
}
- /**
- * 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.
- */
+ /**
+ * Returns whether Observe Mode is currently enabled or not.
+ *
+ * @return true if observe mode is enabled, false otherwise.
+ */
+
@FlaggedApi(Flags.FLAG_NFC_OBSERVE_MODE)
- public boolean allowTransaction() {
+ public boolean isObserveModeEnabled() {
try {
- return sService.setObserveMode(false);
+ return sService.isObserveModeEnabled();
} catch (RemoteException e) {
attemptDeadServiceRecovery(e);
return false;
@@ -1220,18 +1248,20 @@ public final class NfcAdapter {
}
/**
- * 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.
- */
+ * Controls whether the NFC adapter will allow transactions to proceed or be in observe mode
+ * and simply observe and notify the APDU service of polling loop frames. See
+ * {@link #isObserveModeSupported()} for a description of observe mode.
+ *
+ * @param enabled false disables observe mode to allow the transaction to proceed while true
+ * enables observe mode and does not allow transactions to proceed.
+ *
+ * @return boolean indicating success or failure.
+ */
@FlaggedApi(Flags.FLAG_NFC_OBSERVE_MODE)
- public boolean disallowTransaction() {
+ public boolean setObserveModeEnabled(boolean enabled) {
try {
- return sService.setObserveMode(true);
+ return sService.setObserveMode(enabled);
} catch (RemoteException e) {
attemptDeadServiceRecovery(e);
return false;
@@ -1761,7 +1791,9 @@ public final class NfcAdapter {
private static final int ENABLE_POLLING_FLAGS = 0x0000;
/**
- * Privileged API to enable disable reader polling.
+ * Privileged API to enable or disable reader polling.
+ * Unlike {@link #enableReaderMode(Activity, ReaderCallback, int, Bundle)}, this API does not
+ * need a foreground activity to control reader mode parameters
* Note: Use with caution! The app is responsible for ensuring that the polling state is
* returned to normal.
*
@@ -1775,14 +1807,14 @@ public final class NfcAdapter {
@RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
@FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
@SuppressLint("VisiblySynchronized")
- public void setReaderMode(boolean enablePolling) {
+ public void setReaderModePollingEnabled(boolean enable) {
synchronized (sLock) {
if (!sHasNfcFeature) {
throw new UnsupportedOperationException();
}
}
Binder token = new Binder();
- int flags = enablePolling ? ENABLE_POLLING_FLAGS : DISABLE_POLLING_FLAGS;
+ int flags = enable ? ENABLE_POLLING_FLAGS : DISABLE_POLLING_FLAGS;
try {
NfcAdapter.sService.setReaderMode(token, null, flags, null);
} catch (RemoteException e) {
@@ -1844,10 +1876,7 @@ public final class NfcAdapter {
throw new UnsupportedOperationException();
}
}
- mNfcActivityManager.enableReaderMode(activity, null, pollTechnology, null);
- return;
- }
- if (pollTechnology == FLAG_READER_DISABLE) {
+ } else if (pollTechnology == FLAG_READER_DISABLE) {
synchronized (sLock) {
if (!sHasCeFeature) {
throw new UnsupportedOperationException();
@@ -2761,6 +2790,220 @@ public final class NfcAdapter {
}
}
+ /**
+ * Notifies the system of a new polling loop.
+ *
+ * @param frame is the new frame.
+ *
+ * @hide
+ */
+ @TestApi
+ @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public void notifyPollingLoop(@NonNull PollingFrame pollingFrame) {
+ Bundle frame = pollingFrame.toBundle();
+ try {
+ if (sService == null) {
+ attemptDeadServiceRecovery(null);
+ }
+ sService.notifyPollingLoop(frame);
+ } catch (RemoteException e) {
+ attemptDeadServiceRecovery(e);
+ // Try one more time
+ if (sService == null) {
+ Log.e(TAG, "Failed to recover NFC Service.");
+ return;
+ }
+ try {
+ sService.notifyPollingLoop(frame);
+ } catch (RemoteException ee) {
+ Log.e(TAG, "Failed to recover NFC Service.");
+ }
+ }
+ }
+
+ /**
+ * Notifies the system of a an HCE session being deactivated.
+ * *
+ * @hide
+ */
+ @TestApi
+ @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public void notifyHceDeactivated() {
+ try {
+ if (sService == null) {
+ attemptDeadServiceRecovery(null);
+ }
+ sService.notifyHceDeactivated();
+ } catch (RemoteException e) {
+ attemptDeadServiceRecovery(e);
+ // Try one more time
+ if (sService == null) {
+ Log.e(TAG, "Failed to recover NFC Service.");
+ return;
+ }
+ try {
+ sService.notifyHceDeactivated();
+ } catch (RemoteException ee) {
+ Log.e(TAG, "Failed to recover NFC Service.");
+ }
+ }
+ }
+
+ /**
+ * 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 setWlcEnabled(boolean enable) {
+ if (!sHasNfcWlcFeature) {
+ throw new UnsupportedOperationException();
+ }
+ try {
+ return sService.setWlcEnabled(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.setWlcEnabled(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 WlcListenerDeviceInfo wlcListenerDeviceInfo);
+ }
+
+ /**
+ * 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 WlcListenerDeviceInfo getWlcListenerDeviceInfo() {
+ if (!sHasNfcWlcFeature) {
+ throw new UnsupportedOperationException();
+ }
+ try {
+ return sService.getWlcListenerDeviceInfo();
+ } 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.getWlcListenerDeviceInfo();
+ } catch (RemoteException ee) {
+ Log.e(TAG, "Failed to recover NFC Service.");
+ }
+ return null;
+ }
+ }
+
/**
* Vendor NCI command success.
* @hide
@@ -2882,7 +3125,7 @@ public final class NfcAdapter {
* @hide
*/
@SystemApi
- @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ @FlaggedApi(Flags.FLAG_NFC_VENDOR_CMD)
@RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
public void unregisterNfcVendorNciCallback(@NonNull NfcVendorNciCallback callback) {
mNfcVendorNciCallbackListener.unregister(callback);
@@ -2919,4 +3162,19 @@ public final class NfcAdapter {
void onVendorNciNotification(
@IntRange(from = 9, to = 15) int gid, int oid, @NonNull byte[] payload);
}
+
+ /**
+ * Returns an instance of {@link NfcOemExtension} associated with {@link NfcAdapter} instance.
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+ @NonNull public NfcOemExtension getNfcOemExtension() {
+ synchronized (sLock) {
+ if (!sHasNfcFeature) {
+ throw new UnsupportedOperationException();
+ }
+ }
+ return mNfcOemExtension;
+ }
}
diff --git a/nfc/java/android/nfc/NfcOemExtension.java b/nfc/java/android/nfc/NfcOemExtension.java
new file mode 100644
index 000000000000..1eff58cb80fd
--- /dev/null
+++ b/nfc/java/android/nfc/NfcOemExtension.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Used for OEM extension APIs.
+ * This class holds all the APIs and callbacks defined for OEMs/vendors to extend the NFC stack
+ * for their proprietary features.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+@SystemApi
+public final class NfcOemExtension {
+ private static final String TAG = "NfcOemExtension";
+ private static final int OEM_EXTENSION_RESPONSE_THRESHOLD_MS = 2000;
+ private final NfcAdapter mAdapter;
+ private final NfcOemExtensionCallback mOemNfcExtensionCallback;
+ private final Context mContext;
+ private Executor mExecutor = null;
+ private Callback mCallback = null;
+ private final Object mLock = new Object();
+
+ /**
+ * Interface for Oem extensions for NFC.
+ */
+ public interface Callback {
+ /**
+ * Notify Oem to tag is connected or not
+ * ex - if tag is connected notify cover and Nfctest app if app is in testing mode
+ *
+ * @param connected status of the tag true if tag is connected otherwise false
+ * @param tag Tag details
+ */
+ void onTagConnected(boolean connected, @NonNull Tag tag);
+ }
+
+
+ /**
+ * Constructor to be used only by {@link NfcAdapter}.
+ * @hide
+ */
+ public NfcOemExtension(@NonNull Context context, @NonNull NfcAdapter adapter) {
+ mContext = context;
+ mAdapter = adapter;
+ mOemNfcExtensionCallback = new NfcOemExtensionCallback();
+ }
+
+ /**
+ * Register an {@link Callback} to listen for UWB oem extension callbacks
+ * <p>The provided callback will be invoked by the given {@link Executor}.
+ *
+ * @param executor an {@link Executor} to execute given callback
+ * @param callback oem implementation of {@link Callback}
+ */
+ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+ @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+ public void registerCallback(@NonNull @CallbackExecutor Executor executor,
+ @NonNull Callback callback) {
+ synchronized (mLock) {
+ if (mCallback != null) {
+ Log.e(TAG, "Callback already registered. Unregister existing callback before"
+ + "registering");
+ throw new IllegalArgumentException();
+ }
+ try {
+ NfcAdapter.sService.registerOemExtensionCallback(mOemNfcExtensionCallback);
+ mCallback = callback;
+ mExecutor = executor;
+ } catch (RemoteException e) {
+ mAdapter.attemptDeadServiceRecovery(e);
+ }
+ }
+ }
+
+ /**
+ * Unregister the specified {@link Callback}
+ *
+ * <p>The same {@link Callback} object used when calling
+ * {@link #registerCallback(Executor, Callback)} must be used.
+ *
+ * <p>Callbacks are automatically unregistered when an application process goes away
+ *
+ * @param callback oem implementation of {@link Callback}
+ */
+ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+ @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+ public void unregisterCallback(@NonNull Callback callback) {
+ synchronized (mLock) {
+ if (mCallback == null || mCallback != callback) {
+ Log.e(TAG, "Callback not registered");
+ throw new IllegalArgumentException();
+ }
+ try {
+ NfcAdapter.sService.unregisterOemExtensionCallback(mOemNfcExtensionCallback);
+ mCallback = null;
+ mExecutor = null;
+ } catch (RemoteException e) {
+ mAdapter.attemptDeadServiceRecovery(e);
+ }
+ }
+ }
+
+ /**
+ * Clear NfcService preference, interface method to clear NFC preference values on OEM specific
+ * events. For ex: on soft reset, Nfc default values needs to be overridden by OEM defaults.
+ */
+ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+ @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+ public void clearPreference() {
+ try {
+ NfcAdapter.sService.clearPreference();
+ } catch (RemoteException e) {
+ mAdapter.attemptDeadServiceRecovery(e);
+ }
+ }
+
+ private final class NfcOemExtensionCallback extends INfcOemExtensionCallback.Stub {
+ @Override
+ public void onTagConnected(boolean connected, Tag tag) throws RemoteException {
+ synchronized (mLock) {
+ if (mCallback == null || mExecutor == null) {
+ return;
+ }
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onTagConnected(connected, tag));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
+ }
+}
diff --git a/nfc/java/android/nfc/NfcWlcStateListener.java b/nfc/java/android/nfc/NfcWlcStateListener.java
new file mode 100644
index 000000000000..890cb090f587
--- /dev/null
+++ b/nfc/java/android/nfc/NfcWlcStateListener.java
@@ -0,0 +1,122 @@
+/*
+ * 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 WlcListenerDeviceInfo 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 {
+ if (Flags.enableNfcCharging()) {
+ executor.execute(() -> listener.onWlcStateChanged(
+ mCurrentState));
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
+
+ @Override
+ public void onWlcStateChanged(@NonNull WlcListenerDeviceInfo wlcListenerDeviceInfo) {
+ synchronized (this) {
+ mCurrentState = wlcListenerDeviceInfo;
+
+ for (WlcStateListener cb : mListenerMap.keySet()) {
+ sendCurrentState(cb);
+ }
+ }
+ }
+}
+
diff --git a/nfc/java/android/nfc/WlcListenerDeviceInfo.aidl b/nfc/java/android/nfc/WlcListenerDeviceInfo.aidl
new file mode 100644
index 000000000000..7f2ca545007b
--- /dev/null
+++ b/nfc/java/android/nfc/WlcListenerDeviceInfo.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 WlcListenerDeviceInfo;
diff --git a/nfc/java/android/nfc/WlcListenerDeviceInfo.java b/nfc/java/android/nfc/WlcListenerDeviceInfo.java
new file mode 100644
index 000000000000..45315f812250
--- /dev/null
+++ b/nfc/java/android/nfc/WlcListenerDeviceInfo.java
@@ -0,0 +1,145 @@
+/*
+ * 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.FloatRange;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Contains information of the nfc wireless charging listener device information.
+ */
+@FlaggedApi(Flags.FLAG_ENABLE_NFC_CHARGING)
+public final class WlcListenerDeviceInfo implements Parcelable {
+ /**
+ * Device is currently not connected with any WlcListenerDevice.
+ */
+ public static final int STATE_DISCONNECTED = 1;
+
+ /**
+ * Device is currently connected with a WlcListenerDevice and is charging it.
+ */
+ public static final int STATE_CONNECTED_CHARGING = 2;
+
+ /**
+ * Device is currently connected with a WlcListenerDevice without charging it.
+ */
+ public static final int STATE_CONNECTED_DISCHARGING = 3;
+
+ /**
+ * Possible states from {@link #getState}.
+ * @hide
+ */
+ @IntDef(prefix = { "STATE_" }, value = {
+ STATE_DISCONNECTED,
+ STATE_CONNECTED_CHARGING,
+ STATE_CONNECTED_DISCHARGING
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface WlcListenerState{}
+
+ private int mProductId;
+ private double mTemperature;
+ private double mBatteryLevel;
+ private int mState;
+
+ /**
+ * Create a new object containing wlc listener information.
+ *
+ * @param productId code for the device vendor
+ * @param temperature current temperature
+ * @param batteryLevel current battery level
+ * @param state current state
+ */
+ public WlcListenerDeviceInfo(int productId, double temperature, double batteryLevel,
+ @WlcListenerState int state) {
+ this.mProductId = productId;
+ this.mTemperature = temperature;
+ this.mBatteryLevel = batteryLevel;
+ this.mState = state;
+ }
+
+ /**
+ * ProductId of the WLC listener device.
+ * @return integer that is converted from USI Stylus VendorID[11:0].
+ */
+ public int getProductId() {
+ return mProductId;
+ }
+
+ /**
+ * Temperature of the WLC listener device.
+ * @return the value represents the temperature in °C.
+ */
+ public double getTemperature() {
+ return mTemperature;
+ }
+
+ /**
+ * BatteryLevel of the WLC listener device.
+ * @return battery level in percentage [0-100]
+ */
+ public @FloatRange(from = 0.0, to = 100.0) double getBatteryLevel() {
+ return mBatteryLevel;
+ }
+
+ /**
+ * State of the WLC listener device.
+ */
+ public @WlcListenerState int getState() {
+ return mState;
+ }
+
+ private WlcListenerDeviceInfo(Parcel in) {
+ this.mProductId = in.readInt();
+ this.mTemperature = in.readDouble();
+ this.mBatteryLevel = in.readDouble();
+ this.mState = in.readInt();
+ }
+
+ public static final @NonNull Parcelable.Creator<WlcListenerDeviceInfo> CREATOR =
+ new Parcelable.Creator<WlcListenerDeviceInfo>() {
+ @Override
+ public WlcListenerDeviceInfo createFromParcel(Parcel in) {
+ return new WlcListenerDeviceInfo(in);
+ }
+
+ @Override
+ public WlcListenerDeviceInfo[] newArray(int size) {
+ return new WlcListenerDeviceInfo[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mProductId);
+ dest.writeDouble(mTemperature);
+ dest.writeDouble(mBatteryLevel);
+ dest.writeInt(mState);
+ }
+}
diff --git a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
index 41dee3ab035c..2c7d61eea777 100644
--- a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
+++ b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
@@ -44,6 +44,8 @@ import android.util.Log;
import android.util.Xml;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.R;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -52,6 +54,7 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.regex.Pattern;
@@ -102,6 +105,9 @@ public final class ApduServiceInfo implements Parcelable {
*/
private final HashMap<String, AidGroup> mDynamicAidGroups;
+
+ private final Map<String, Boolean> mAutoTransact;
+
/**
* Whether this service should only be started when the device is unlocked.
*/
@@ -133,6 +139,11 @@ public final class ApduServiceInfo implements Parcelable {
private boolean mCategoryOtherServiceEnabled;
/**
+ * Whether the NFC stack should default to Observe Mode when this preferred service.
+ */
+ private boolean mShouldDefaultToObserveMode;
+
+ /**
* @hide
*/
@UnsupportedAppUsage
@@ -165,10 +176,25 @@ public final class ApduServiceInfo implements Parcelable {
List<AidGroup> staticAidGroups, List<AidGroup> dynamicAidGroups,
boolean requiresUnlock, boolean requiresScreenOn, int bannerResource, int uid,
String settingsActivityName, String offHost, String staticOffHost, boolean isEnabled) {
+ this(info, onHost, description, staticAidGroups, dynamicAidGroups,
+ requiresUnlock, requiresScreenOn, bannerResource, uid,
+ settingsActivityName, offHost, staticOffHost, isEnabled,
+ new HashMap<String, Boolean>());
+ }
+
+ /**
+ * @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,
+ HashMap<String, Boolean> autoTransact) {
this.mService = info;
this.mDescription = description;
this.mStaticAidGroups = new HashMap<String, AidGroup>();
this.mDynamicAidGroups = new HashMap<String, AidGroup>();
+ this.mAutoTransact = autoTransact;
this.mOffHostName = offHost;
this.mStaticOffHostName = staticOffHost;
this.mOnHost = onHost;
@@ -184,7 +210,6 @@ public final class ApduServiceInfo implements Parcelable {
this.mUid = uid;
this.mSettingsActivityName = settingsActivityName;
this.mCategoryOtherServiceEnabled = isEnabled;
-
}
/**
@@ -250,6 +275,9 @@ public final class ApduServiceInfo implements Parcelable {
com.android.internal.R.styleable.HostApduService_settingsActivity);
mOffHostName = null;
mStaticOffHostName = mOffHostName;
+ mShouldDefaultToObserveMode = sa.getBoolean(
+ R.styleable.HostApduService_shouldDefaultToObserveMode,
+ false);
sa.recycle();
} else {
TypedArray sa = res.obtainAttributes(attrs,
@@ -269,6 +297,9 @@ public final class ApduServiceInfo implements Parcelable {
com.android.internal.R.styleable.HostApduService_settingsActivity);
mOffHostName = sa.getString(
com.android.internal.R.styleable.OffHostApduService_secureElementName);
+ mShouldDefaultToObserveMode = sa.getBoolean(
+ R.styleable.HostApduService_shouldDefaultToObserveMode,
+ false);
if (mOffHostName != null) {
if (mOffHostName.equals("eSE")) {
mOffHostName = "eSE1";
@@ -282,6 +313,7 @@ public final class ApduServiceInfo implements Parcelable {
mStaticAidGroups = new HashMap<String, AidGroup>();
mDynamicAidGroups = new HashMap<String, AidGroup>();
+ mAutoTransact = new HashMap<String, Boolean>();
mOnHost = onHost;
final int depth = parser.getDepth();
@@ -364,6 +396,18 @@ public final class ApduServiceInfo implements Parcelable {
Log.e(TAG, "Ignoring invalid or duplicate aid: " + aid);
}
a.recycle();
+ } else if (eventType == XmlPullParser.START_TAG
+ && "polling-loop-filter".equals(tagName) && currentGroup == null) {
+ final TypedArray a = res.obtainAttributes(attrs,
+ com.android.internal.R.styleable.PollingLoopFilter);
+ String plf =
+ a.getString(com.android.internal.R.styleable.PollingLoopFilter_name)
+ .toUpperCase(Locale.ROOT);
+ boolean autoTransact = a.getBoolean(
+ com.android.internal.R.styleable.PollingLoopFilter_autoTransact,
+ false);
+ mAutoTransact.put(plf, autoTransact);
+ a.recycle();
}
}
} catch (NameNotFoundException e) {
@@ -420,6 +464,27 @@ public final class ApduServiceInfo implements Parcelable {
}
/**
+ * Returns the current polling loop filters for this service.
+ * @return List of polling loop filters.
+ */
+ @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP)
+ @NonNull
+ public List<String> getPollingLoopFilters() {
+ return new ArrayList<>(mAutoTransact.keySet());
+ }
+
+ /**
+ * Returns whether this service would like to automatically transact for a given plf.
+ *
+ * @param plf the polling loop filter to query.
+ * @return {@code true} indicating to auto transact, {@code false} indicating to not.
+ */
+ @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public boolean getShouldAutoTransact(@NonNull String plf) {
+ return mAutoTransact.getOrDefault(plf.toUpperCase(Locale.ROOT), false);
+ }
+
+ /**
* 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
@@ -568,6 +633,25 @@ public final class ApduServiceInfo implements Parcelable {
}
/**
+ * Returns whether the NFC stack should default to observe mode when this service is preferred.
+ * @return whether the NFC stack should default to observe mode when this service is preferred
+ */
+ @FlaggedApi(Flags.FLAG_NFC_OBSERVE_MODE)
+ public boolean shouldDefaultToObserveMode() {
+ return mShouldDefaultToObserveMode;
+ }
+
+ /**
+ * Sets whether the NFC stack should default to observe mode when this service is preferred.
+ * @param shouldDefaultToObserveMode whether the NFC stack should default to observe mode when
+ * this service is preferred
+ */
+ @FlaggedApi(Flags.FLAG_NFC_OBSERVE_MODE)
+ public void setShouldDefaultToObserveMode(boolean shouldDefaultToObserveMode) {
+ mShouldDefaultToObserveMode = shouldDefaultToObserveMode;
+ }
+
+ /**
* Returns description of service.
* @return user readable description of service
*/
@@ -596,6 +680,29 @@ public final class ApduServiceInfo implements Parcelable {
}
/**
+ * Add a Polling Loop Filter. Custom NFC polling frames that match this filter will be
+ * delivered to {@link HostApduService#processPollingFrames(List)}. Adding a key with this
+ * multiple times will cause the value to be overwritten each time.
+ * @param pollingLoopFilter the polling loop filter to add, must be a valide hexadecimal string
+ */
+ @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public void addPollingLoopFilter(@NonNull String pollingLoopFilter,
+ boolean autoTransact) {
+ mAutoTransact.put(pollingLoopFilter, autoTransact);
+
+ }
+
+ /**
+ * Remove a Polling Loop Filter. Custom NFC polling frames that match this filter will no
+ * longer be delivered to {@link HostApduService#processPollingFrames(List)}.
+ * @param pollingLoopFilter this polling loop filter to add.
+ */
+ @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public void removePollingLoopFilter(@NonNull String pollingLoopFilter) {
+ mAutoTransact.remove(pollingLoopFilter.toUpperCase(Locale.ROOT));
+ }
+
+ /**
* 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
@@ -747,6 +854,8 @@ public final class ApduServiceInfo implements Parcelable {
dest.writeString(mSettingsActivityName);
dest.writeInt(mCategoryOtherServiceEnabled ? 1 : 0);
+ dest.writeInt(mAutoTransact.size());
+ dest.writeMap(mAutoTransact);
};
@FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
@@ -775,10 +884,15 @@ public final class ApduServiceInfo implements Parcelable {
int uid = source.readInt();
String settingsActivityName = source.readString();
boolean isEnabled = source.readInt() != 0;
+ int autoTransactSize = source.readInt();
+ HashMap<String, Boolean> autoTransact =
+ new HashMap<String, Boolean>(autoTransactSize);
+ source.readMap(autoTransact, getClass().getClassLoader(),
+ String.class, Boolean.class);
return new ApduServiceInfo(info, onHost, description, staticAidGroups,
dynamicAidGroups, requiresUnlock, requiresScreenOn, bannerResource, uid,
settingsActivityName, offHostName, staticOffHostName,
- isEnabled);
+ isEnabled, autoTransact);
}
@Override
diff --git a/nfc/java/android/nfc/cardemulation/CardEmulation.java b/nfc/java/android/nfc/cardemulation/CardEmulation.java
index ad86d70db967..61d651fa5577 100644
--- a/nfc/java/android/nfc/cardemulation/CardEmulation.java
+++ b/nfc/java/android/nfc/cardemulation/CardEmulation.java
@@ -16,6 +16,7 @@
package android.nfc.cardemulation;
+import android.Manifest;
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -23,6 +24,7 @@ import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemApi;
+import android.annotation.UserHandleAware;
import android.annotation.UserIdInt;
import android.app.Activity;
import android.content.ComponentName;
@@ -40,6 +42,7 @@ import android.provider.Settings.SettingNotFoundException;
import android.util.Log;
import java.util.HashMap;
+import java.util.HexFormat;
import java.util.List;
import java.util.regex.Pattern;
@@ -57,6 +60,7 @@ import java.util.regex.Pattern;
*/
public final class CardEmulation {
private static final Pattern AID_PATTERN = Pattern.compile("[0-9A-Fa-f]{10,32}\\*?\\#?");
+
static final String TAG = "CardEmulation";
/**
@@ -69,7 +73,12 @@ public final class CardEmulation {
* 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";
@@ -329,18 +338,21 @@ public final class CardEmulation {
}
}
/**
- * Sets whether the system should default to observe mode or not when
- * the service is in the foreground or the default payment service.
+ * Sets whether when this service becomes the preferred service, if the NFC stack
+ * should enable observe mode or disable observe mode. The default is to not enable observe
+ * mode when a service either the foreground default service or the default payment service so
+ * not calling this method will preserve that behavior.
*
* @param service The component name of the service
- * @param enable Whether the servic should default to observe mode or not
+ * @param enable Whether the service 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) {
+ public boolean setShouldDefaultToObserveModeForService(@NonNull ComponentName service,
+ boolean enable) {
try {
- return sService.setServiceObserveModeDefault(mContext.getUser().getIdentifier(),
- service, enable);
+ return sService.setShouldDefaultToObserveModeForService(
+ mContext.getUser().getIdentifier(), service, enable);
} catch (RemoteException e) {
Log.e(TAG, "Failed to reach CardEmulationService.");
}
@@ -348,6 +360,47 @@ public final class CardEmulation {
}
/**
+ * Register a polling loop filter (PLF) for a HostApduService and indicate whether it should
+ * auto-transact or not. The PLF can be sequence of an
+ * even number of at least 2 hexadecimal numbers (0-9, A-F or a-f), representing a series of
+ * bytes. When non-standard polling loop frame matches this sequence exactly, it may be
+ * delivered to {@link HostApduService#processPollingFrames(List)}. If auto-transact is set to
+ * true, then observe mode will also be disabled. if this service is currently preferred or
+ * there are no other services registered for this filter.
+ * @param service The HostApduService to register the filter for
+ * @param pollingLoopFilter The filter to register
+ * @param autoTransact true to have the NFC stack automatically disable observe mode and allow
+ * transactions to proceed when this filter matches, false otherwise
+ * @return true if the filter was registered, false otherwise
+ * @throws IllegalArgumentException if the passed in string doesn't parse to at least one byte
+ */
+ @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public boolean registerPollingLoopFilterForService(@NonNull ComponentName service,
+ @NonNull String pollingLoopFilter, boolean autoTransact) {
+ pollingLoopFilter = validatePollingLoopFilter(pollingLoopFilter);
+
+ try {
+ return sService.registerPollingLoopFilterForService(mContext.getUser().getIdentifier(),
+ service, pollingLoopFilter, autoTransact);
+ } catch (RemoteException e) {
+ // Try one more time
+ recoverService();
+ if (sService == null) {
+ Log.e(TAG, "Failed to recover CardEmulationService.");
+ return false;
+ }
+ try {
+ return sService.registerPollingLoopFilterForService(
+ mContext.getUser().getIdentifier(), service,
+ pollingLoopFilter, autoTransact);
+ } catch (RemoteException ee) {
+ Log.e(TAG, "Failed to reach CardEmulationService.");
+ return false;
+ }
+ }
+ }
+
+ /**
* Registers a list of AIDs for a specific category for the
* specified service.
*
@@ -928,6 +981,23 @@ public final class CardEmulation {
}
/**
+ * Tests the validity of the polling loop filter.
+ * @param pollingLoopFilter The polling loop filter to test.
+ *
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static @NonNull String validatePollingLoopFilter(@NonNull String pollingLoopFilter) {
+ // Verify hex characters
+ byte[] plfBytes = HexFormat.of().parseHex(pollingLoopFilter);
+ if (plfBytes.length == 0) {
+ throw new IllegalArgumentException(
+ "Polling loop filter must contain at least one byte.");
+ }
+ return HexFormat.of().withUpperCase().formatHex(plfBytes);
+ }
+
+ /**
* A valid AID according to ISO/IEC 7816-4:
* <ul>
* <li>Has >= 5 bytes and <=16 bytes (>=10 hex chars and <= 32 hex chars)
@@ -970,16 +1040,16 @@ public final class CardEmulation {
*
* @param service The ComponentName of the service
* @param status true to enable, false to disable
+ * @param userId the user handle of the user whose information is being requested.
* @return set service for the category and true if service is already set return false.
*
* @hide
*/
- public boolean setServiceEnabledForCategoryOther(ComponentName service, boolean status) {
+ public boolean setServiceEnabledForCategoryOther(ComponentName service, boolean status,
+ int userId) {
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) {
@@ -1084,4 +1154,29 @@ public final class CardEmulation {
sService = adapter.getCardEmulationService();
}
+ /**
+ * Returns the value of {@link Settings.Secure#NFC_PAYMENT_DEFAULT_COMPONENT}.
+ *
+ * @param context A context
+ * @return A ComponentName for the setting value, or null.
+ *
+ * @hide
+ */
+ @SystemApi
+ @UserHandleAware
+ @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO)
+ @SuppressWarnings("AndroidFrameworkClientSidePermissionCheck")
+ @FlaggedApi(android.permission.flags.Flags.FLAG_WALLET_ROLE_ENABLED)
+ @Nullable
+ public static ComponentName getPreferredPaymentService(@NonNull Context context) {
+ context.checkCallingOrSelfPermission(Manifest.permission.NFC_PREFERRED_PAYMENT_INFO);
+ String defaultPaymentComponent = Settings.Secure.getString(context.getContentResolver(),
+ Constants.SETTINGS_SECURE_NFC_PAYMENT_DEFAULT_COMPONENT);
+
+ if (defaultPaymentComponent == null) {
+ return null;
+ }
+
+ return ComponentName.unflattenFromString(defaultPaymentComponent);
+ }
}
diff --git a/nfc/java/android/nfc/cardemulation/HostApduService.java b/nfc/java/android/nfc/cardemulation/HostApduService.java
index 7cd2533a7dbf..f3ba2d0098ce 100644
--- a/nfc/java/android/nfc/cardemulation/HostApduService.java
+++ b/nfc/java/android/nfc/cardemulation/HostApduService.java
@@ -20,6 +20,7 @@ import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SuppressLint;
import android.app.Service;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -244,88 +245,9 @@ public abstract class HostApduService extends Service {
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 =
+ public static final String KEY_POLLING_LOOP_FRAMES_BUNDLE =
"android.nfc.cardemulation.POLLING_FRAMES";
/**
@@ -405,9 +327,14 @@ public abstract class HostApduService extends Service {
break;
case MSG_POLLING_LOOP:
ArrayList<Bundle> frames =
- msg.getData().getParcelableArrayList(POLLING_LOOP_FRAMES_BUNDLE_KEY,
+ msg.getData().getParcelableArrayList(KEY_POLLING_LOOP_FRAMES_BUNDLE,
Bundle.class);
- processPollingFrames(frames);
+ ArrayList<PollingFrame> pollingFrames =
+ new ArrayList<PollingFrame>(frames.size());
+ for (Bundle frame : frames) {
+ pollingFrames.add(new PollingFrame(frame));
+ }
+ processPollingFrames(pollingFrames);
break;
default:
super.handleMessage(msg);
@@ -470,7 +397,7 @@ public abstract class HostApduService extends Service {
}
/**
- * This method is called when a polling frame has been received from a
+ * This method is called when polling frames have 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
@@ -481,8 +408,9 @@ public abstract class HostApduService extends Service {
*
* @param frame A description of the polling frame.
*/
+ @SuppressLint("OnNameExpected")
@FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
- public void processPollingFrames(@NonNull List<Bundle> frame) {
+ public void processPollingFrames(@NonNull List<PollingFrame> frame) {
}
/**
diff --git a/nfc/java/android/nfc/cardemulation/PollingFrame.java b/nfc/java/android/nfc/cardemulation/PollingFrame.java
new file mode 100644
index 000000000000..994f4ae1c2e3
--- /dev/null
+++ b/nfc/java/android/nfc/cardemulation/PollingFrame.java
@@ -0,0 +1,243 @@
+/*
+ * 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.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.HexFormat;
+import java.util.List;
+
+/**
+ * Polling Frames represent data about individual frames of an NFC polling loop. These frames will
+ * be deliverd to subclasses of {@link HostApduService} that have registered filters with
+ * {@link CardEmulation#registerPollingLoopFilterForService(ComponentName, String)} that match a
+ * given frame in a loop and will be delivered through calls to
+ * {@link HostApduService#processPollingFrames(List)}.
+ */
+@FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+public final class PollingFrame implements Parcelable{
+
+ /**
+ * @hide
+ */
+ @IntDef(prefix = { "POLLING_LOOP_TYPE_"}, value = { POLLING_LOOP_TYPE_A, POLLING_LOOP_TYPE_B,
+ POLLING_LOOP_TYPE_F, POLLING_LOOP_TYPE_OFF, POLLING_LOOP_TYPE_ON })
+ @Retention(RetentionPolicy.SOURCE)
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public @interface PollingFrameType {}
+
+ /**
+ * POLLING_LOOP_TYPE_A is the value associated with the key
+ * POLLING_LOOP_TYPE in the Bundle passed to {@link HostApduService#processPollingFrames(List)}
+ * when the polling loop is for NFC-A.
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final int 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 HostApduService#processPollingFrames(List)}
+ * when the polling loop is for NFC-B.
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final int 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 HostApduService#processPollingFrames(List)}
+ * when the polling loop is for NFC-F.
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final int 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 HostApduService#processPollingFrames(List)}
+ * when the polling loop turns on.
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final int 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 HostApduService#processPollingFrames(List)}
+ * when the polling loop turns off.
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final int 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 HostApduService#processPollingFrames(List)}
+ * when the polling loop frame isn't recognized.
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final int POLLING_LOOP_TYPE_UNKNOWN = 'U';
+
+ /**
+ * KEY_POLLING_LOOP_TYPE is the Bundle key for the type of
+ * polling loop frame in the Bundle included in MSG_POLLING_LOOP.
+ *
+ * @hide
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final String KEY_POLLING_LOOP_TYPE = "android.nfc.cardemulation.TYPE";
+
+ /**
+ * KEY_POLLING_LOOP_DATA is the Bundle key for the raw data of captured from
+ * the polling loop frame in the Bundle included in MSG_POLLING_LOOP.
+ *
+ * @hide
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final String KEY_POLLING_LOOP_DATA = "android.nfc.cardemulation.DATA";
+
+ /**
+ * KEY_POLLING_LOOP_GAIN is the Bundle key for the field strength of
+ * the polling loop frame in the Bundle included in MSG_POLLING_LOOP.
+ *
+ * @hide
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final String KEY_POLLING_LOOP_GAIN = "android.nfc.cardemulation.GAIN";
+
+ /**
+ * KEY_POLLING_LOOP_TIMESTAMP is the Bundle key for the timestamp of
+ * the polling loop frame in the Bundle included in MSG_POLLING_LOOP.
+ *
+ * @hide
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final String KEY_POLLING_LOOP_TIMESTAMP = "android.nfc.cardemulation.TIMESTAMP";
+
+
+ @PollingFrameType
+ private final int mType;
+ private final byte[] mData;
+ private final int mGain;
+ private final int mTimestamp;
+
+ public static final @NonNull Parcelable.Creator<PollingFrame> CREATOR =
+ new Parcelable.Creator<>() {
+ @Override
+ public PollingFrame createFromParcel(Parcel source) {
+ return new PollingFrame(source.readBundle());
+ }
+
+ @Override
+ public PollingFrame[] newArray(int size) {
+ return new PollingFrame[size];
+ }
+ };
+
+ PollingFrame(Bundle frame) {
+ mType = frame.getInt(KEY_POLLING_LOOP_TYPE);
+ byte[] data = frame.getByteArray(KEY_POLLING_LOOP_DATA);
+ mData = (data == null) ? new byte[0] : data;
+ mGain = frame.getByte(KEY_POLLING_LOOP_GAIN);
+ mTimestamp = frame.getInt(KEY_POLLING_LOOP_TIMESTAMP);
+ }
+
+ public PollingFrame(@PollingFrameType int type, @Nullable byte[] data,
+ int gain, int timestamp) {
+ mType = type;
+ mData = data == null ? new byte[0] : data;
+ mGain = gain;
+ mTimestamp = timestamp;
+ }
+
+ /**
+ * Returns the type of frame for this polling loop frame.
+ * The possible return values are:
+ * <ul>
+ * <li>{@link POLLING_LOOP_TYPE_ON}</li>
+ * <li>{@link POLLING_LOOP_TYPE_OFF}</li>
+ * <li>{@link POLLING_LOOP_TYPE_A}</li>
+ * <li>{@link POLLING_LOOP_TYPE_B}</li>
+ * <li>{@link POLLING_LOOP_TYPE_F}</li>
+ * </ul>
+ */
+ public @PollingFrameType int getType() {
+ return mType;
+ }
+
+ /**
+ * Returns the raw data from the polling type frame.
+ */
+ public @NonNull byte[] getData() {
+ return mData;
+ }
+
+ /**
+ * Returns the gain representing the field strength of the NFC field when this polling loop
+ * frame was observed.
+ */
+ public int getGain() {
+ return mGain;
+ }
+
+ /**
+ * Returns the timestamp of when the polling loop frame was observed in milliseconds. These
+ * timestamps are relative and not absolute and should only be used for comparing the timing of
+ * frames relative to each other.
+ * @return the timestamp in milliseconds
+ */
+ public int getTimestamp() {
+ return mTimestamp;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeBundle(toBundle());
+ }
+
+ /**
+ *
+ * @hide
+ * @return a Bundle representing this frame
+ */
+ public Bundle toBundle() {
+ Bundle frame = new Bundle();
+ frame.putInt(KEY_POLLING_LOOP_TYPE, getType());
+ frame.putByte(KEY_POLLING_LOOP_GAIN, (byte) getGain());
+ frame.putByteArray(KEY_POLLING_LOOP_DATA, getData());
+ frame.putInt(KEY_POLLING_LOOP_TIMESTAMP, getTimestamp());
+ return frame;
+ }
+
+ @Override
+ public String toString() {
+ return "PollingFrame { Type: " + (char) getType()
+ + ", gain: " + getGain()
+ + ", timestamp: " + Integer.toUnsignedString(getTimestamp())
+ + ", data: [" + HexFormat.ofDelimiter(" ").formatHex(getData()) + "] }";
+ }
+}
diff --git a/nfc/java/android/nfc/flags.aconfig b/nfc/java/android/nfc/flags.aconfig
index 44924ae925ef..6841d2ba96ad 100644
--- a/nfc/java/android/nfc/flags.aconfig
+++ b/nfc/java/android/nfc/flags.aconfig
@@ -1,4 +1,5 @@
package: "android.nfc"
+container: "system"
flag {
name: "enable_nfc_mainline"
@@ -62,6 +63,13 @@ flag {
}
flag {
+ name: "enable_nfc_charging"
+ namespace: "nfc"
+ description: "Flag for NFC charging changes"
+ bug: "292143899"
+}
+
+flag {
name: "enable_nfc_set_discovery_tech"
is_exported: true
namespace: "nfc"
@@ -76,3 +84,11 @@ flag {
description: "Enable NFC vendor command support"
bug: "289879306"
}
+
+flag {
+ name: "nfc_oem_extension"
+ is_exported: true
+ namespace: "nfc"
+ description: "Enable NFC OEM extension support"
+ bug: "331206243"
+}