diff options
Diffstat (limited to 'nfc')
22 files changed, 1654 insertions, 151 deletions
diff --git a/nfc/Android.bp b/nfc/Android.bp index 3909e1d3b807..421f06d5cf2b 100644 --- a/nfc/Android.bp +++ b/nfc/Android.bp @@ -1,4 +1,5 @@ package { + default_team: "trendy_team_fwk_nfc", // 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" @@ -40,6 +41,7 @@ java_sdk_library { ], static_libs: [ "android.nfc.flags-aconfig-java", + "android.permission.flags-aconfig-java", ], srcs: [ ":framework-nfc-updatable-sources", @@ -67,8 +69,12 @@ java_sdk_library { ], jarjar_rules: ":nfc-jarjar-rules", lint: { - strict_updatability_linting: true, + baseline_filename: "lint-baseline.xml", }, + apex_available: [ + "//apex_available:platform", + "com.android.nfcservices", + ], aconfig_declarations: [ "android.nfc.flags-aconfig", ], diff --git a/nfc/TEST_MAPPING b/nfc/TEST_MAPPING index 5b5ea3790010..49c778d22038 100644 --- a/nfc/TEST_MAPPING +++ b/nfc/TEST_MAPPING @@ -5,6 +5,9 @@ }, { "name": "CtsNfcTestCases" + }, + { + "name": "CtsNdefTestCases" } ] } diff --git a/nfc/api/current.txt b/nfc/api/current.txt index 0ab2ef7ed7cb..9d0221a3ae68 100644 --- a/nfc/api/current.txt +++ b/nfc/api/current.txt @@ -64,23 +64,25 @@ package android.nfc { } 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.WlcListenerDeviceInfo getWlcListenerDeviceInfo(); 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 isObserveModeEnabled(); 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); + method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean setObserveModeEnabled(boolean); 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"; @@ -173,6 +175,20 @@ package android.nfc { ctor public TagLostException(String); } + @FlaggedApi("android.nfc.enable_nfc_charging") public final class WlcListenerDeviceInfo implements android.os.Parcelable { + ctor public WlcListenerDeviceInfo(int, double, double, int); + method public int describeContents(); + method @FloatRange(from=0.0, to=100.0) public double getBatteryLevel(); + method public int getProductId(); + method public int getState(); + method public double getTemperature(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.nfc.WlcListenerDeviceInfo> CREATOR; + field public static final int STATE_CONNECTED_CHARGING = 2; // 0x2 + field public static final int STATE_CONNECTED_DISCHARGING = 3; // 0x3 + field public static final int STATE_DISCONNECTED = 1; // 0x1 + } + } package android.nfc.cardemulation { @@ -188,14 +204,15 @@ package android.nfc.cardemulation { 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 @FlaggedApi("android.nfc.nfc_read_polling_loop") public boolean registerPollingLoopFilterForService(@NonNull android.content.ComponentName, @NonNull String, boolean); 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 @FlaggedApi("android.nfc.nfc_observe_mode") public boolean setShouldDefaultToObserveModeForService(@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 public static final String ACTION_CHANGE_DEFAULT = "android.nfc.cardemulation.action.ACTION_CHANGE_DEFAULT"; + 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"; @@ -211,20 +228,10 @@ package android.nfc.cardemulation { 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 @FlaggedApi("android.nfc.nfc_read_polling_loop") public void processPollingFrames(@NonNull java.util.List<android.nfc.cardemulation.PollingFrame>); 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"; } @@ -257,6 +264,23 @@ package android.nfc.cardemulation { field public static final String SERVICE_META_DATA = "android.nfc.cardemulation.off_host_apdu_service"; } + @FlaggedApi("android.nfc.nfc_read_polling_loop") public final class PollingFrame implements android.os.Parcelable { + ctor public PollingFrame(int, @Nullable byte[], int, int); + method public int describeContents(); + method @NonNull public byte[] getData(); + method public int getGain(); + method public int getTimestamp(); + method public int getType(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.nfc.cardemulation.PollingFrame> CREATOR; + field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final int POLLING_LOOP_TYPE_A = 65; // 0x41 + field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final int POLLING_LOOP_TYPE_B = 66; // 0x42 + field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final int POLLING_LOOP_TYPE_F = 70; // 0x46 + field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final int POLLING_LOOP_TYPE_OFF = 88; // 0x58 + field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final int POLLING_LOOP_TYPE_ON = 79; // 0x4f + field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final int POLLING_LOOP_TYPE_UNKNOWN = 85; // 0x55 + } + } package android.nfc.tech { diff --git a/nfc/api/system-current.txt b/nfc/api/system-current.txt index ece8851df42f..310130e59bfe 100644 --- a/nfc/api/system-current.txt +++ b/nfc/api/system-current.txt @@ -9,20 +9,26 @@ package android.nfc { 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_mainline") public int getAdapterState(); + method @FlaggedApi("android.nfc.nfc_oem_extension") @NonNull public android.nfc.NfcOemExtension getNfcOemExtension(); 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.nfc_vendor_cmd") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void registerNfcVendorNciCallback(@NonNull java.util.concurrent.Executor, @NonNull android.nfc.NfcAdapter.NfcVendorNciCallback); + 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 @FlaggedApi("android.nfc.nfc_vendor_cmd") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public int sendVendorNciMessage(int, @IntRange(from=0, to=15) int, @IntRange(from=0) int, @NonNull byte[]); 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 @FlaggedApi("android.nfc.enable_nfc_mainline") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setReaderModePollingEnabled(boolean); method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public int setTagIntentAppPreferenceForUser(int, @NonNull String, boolean); + method @FlaggedApi("android.nfc.enable_nfc_charging") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean setWlcEnabled(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_mainline") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void unregisterNfcVendorNciCallback(@NonNull android.nfc.NfcAdapter.NfcVendorNciCallback); + method @FlaggedApi("android.nfc.nfc_vendor_cmd") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void unregisterNfcVendorNciCallback(@NonNull android.nfc.NfcAdapter.NfcVendorNciCallback); + 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 @FlaggedApi("android.nfc.enable_nfc_mainline") @RequiresPermission(android.Manifest.permission.SHOW_CUSTOMIZED_RESOLVER) public static final String ACTION_SHOW_NFC_RESOLVER = "android.nfc.action.SHOW_NFC_RESOLVER"; + field @FlaggedApi("android.nfc.enable_nfc_mainline") public static final String EXTRA_RESOLVE_INFOS = "android.nfc.extra.RESOLVE_INFOS"; field @FlaggedApi("android.nfc.nfc_vendor_cmd") public static final int MESSAGE_TYPE_COMMAND = 1; // 0x1 field @FlaggedApi("android.nfc.nfc_vendor_cmd") public static final int SEND_VENDOR_NCI_STATUS_FAILED = 3; // 0x3 field @FlaggedApi("android.nfc.nfc_vendor_cmd") public static final int SEND_VENDOR_NCI_STATUS_MESSAGE_CORRUPTED = 2; // 0x2 @@ -46,11 +52,26 @@ package android.nfc { method @FlaggedApi("android.nfc.nfc_vendor_cmd") public void onVendorNciResponse(@IntRange(from=0, to=15) int, int, @NonNull byte[]); } + @FlaggedApi("android.nfc.enable_nfc_charging") public static interface NfcAdapter.WlcStateListener { + method public void onWlcStateChanged(@NonNull android.nfc.WlcListenerDeviceInfo); + } + + @FlaggedApi("android.nfc.nfc_oem_extension") public final class NfcOemExtension { + method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void clearPreference(); + method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.nfc.NfcOemExtension.Callback); + method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void unregisterCallback(@NonNull android.nfc.NfcOemExtension.Callback); + } + + public static interface NfcOemExtension.Callback { + method public void onTagConnected(boolean, @NonNull android.nfc.Tag); + } + } 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 static android.content.ComponentName getPreferredPaymentService(@NonNull android.content.Context); method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public java.util.List<android.nfc.cardemulation.ApduServiceInfo> getServices(@NonNull String, int); } diff --git a/nfc/jarjar-rules.txt b/nfc/jarjar-rules.txt index 4cd652d6af7f..63a6a58d8ce2 100644 --- a/nfc/jarjar-rules.txt +++ b/nfc/jarjar-rules.txt @@ -4,6 +4,7 @@ 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.content.UriRelativeFilter* 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 @@ -26,9 +27,8 @@ 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.nfc.*Flags* 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 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" +} diff --git a/nfc/lint-baseline.xml b/nfc/lint-baseline.xml new file mode 100644 index 000000000000..d0f797e5c6b8 --- /dev/null +++ b/nfc/lint-baseline.xml @@ -0,0 +1,268 @@ +<?xml version="1.0" encoding="UTF-8"?> +<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01"> + + <issue + id="NewApi" + message="Call requires API level 35 (current min is 34): `new android.nfc.cardemulation.AidGroup`" + errorLine1=" AidGroup aidGroup = new AidGroup(aids, category);" + errorLine2=" ~~~~~~~~~~~~"> + <location + file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java" + line="377" + column="29"/> + </issue> + + <issue + id="NewApi" + message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.AidGroup#getAids`" + errorLine1=" return (group != null ? group.getAids() : null);" + errorLine2=" ~~~~~~~"> + <location + file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java" + line="537" + column="43"/> + </issue> + + <issue + id="NewApi" + message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.AidGroup#getAids`" + errorLine1=" return (group != null ? group.getAids() : null);" + errorLine2=" ~~~~~~~"> + <location + file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java" + line="547" + column="47"/> + </issue> + + <issue + id="NewApi" + message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getAids`" + errorLine1=" return (serviceInfo != null ? serviceInfo.getAids() : null);" + errorLine2=" ~~~~~~~"> + <location + file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java" + line="714" + column="55"/> + </issue> + + <issue + id="NewApi" + message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getAids`" + errorLine1=" return (serviceInfo != null ? serviceInfo.getAids() : null);" + errorLine2=" ~~~~~~~"> + <location + file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java" + line="724" + column="59"/> + </issue> + + <issue + id="NewApi" + message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#isOnHost`" + errorLine1=" if (!serviceInfo.isOnHost()) {" + errorLine2=" ~~~~~~~~"> + <location + file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java" + line="755" + column="34"/> + </issue> + + <issue + id="NewApi" + message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getOffHostSecureElement`" + errorLine1=" return serviceInfo.getOffHostSecureElement() == null ?" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~"> + <location + file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java" + line="756" + column="40"/> + </issue> + + <issue + id="NewApi" + message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getOffHostSecureElement`" + errorLine1=' "OffHost" : serviceInfo.getOffHostSecureElement();' + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~"> + <location + file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java" + line="757" + column="53"/> + </issue> + + <issue + id="NewApi" + message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#isOnHost`" + errorLine1=" if (!serviceInfo.isOnHost()) {" + errorLine2=" ~~~~~~~~"> + <location + file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java" + line="772" + column="38"/> + </issue> + + <issue + id="NewApi" + message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getOffHostSecureElement`" + errorLine1=" return serviceInfo.getOffHostSecureElement() == null ?" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~"> + <location + file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java" + line="773" + column="44"/> + </issue> + + <issue + id="NewApi" + message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getOffHostSecureElement`" + errorLine1=' "Offhost" : serviceInfo.getOffHostSecureElement();' + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~"> + <location + file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java" + line="774" + column="57"/> + </issue> + + <issue + id="NewApi" + message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getDescription`" + errorLine1=" return (serviceInfo != null ? serviceInfo.getDescription() : null);" + errorLine2=" ~~~~~~~~~~~~~~"> + <location + file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java" + line="798" + column="55"/> + </issue> + + <issue + id="NewApi" + message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getDescription`" + errorLine1=" return (serviceInfo != null ? serviceInfo.getDescription() : null);" + errorLine2=" ~~~~~~~~~~~~~~"> + <location + file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java" + line="808" + column="59"/> + </issue> + + <issue + id="NewApi" + message="Call requires API level 35 (current min is 34): `android.app.Activity#isResumed`" + errorLine1=" if (!activity.isResumed()) {" + errorLine2=" ~~~~~~~~~"> + <location + file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java" + line="1032" + column="23"/> + </issue> + + <issue + id="NewApi" + message="Call requires API level 35 (current min is 34): `android.app.Activity#isResumed`" + errorLine1=" if (!activity.isResumed()) {" + errorLine2=" ~~~~~~~~~"> + <location + file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java" + line="1066" + column="23"/> + </issue> + + <issue + id="NewApi" + message="Call requires API level 35 (current min is 34): `android.app.Activity#isResumed`" + errorLine1=" resumed = activity.isResumed();" + errorLine2=" ~~~~~~~~~"> + <location + file="frameworks/base/nfc/java/android/nfc/NfcActivityManager.java" + line="124" + column="32"/> + </issue> + + <issue + id="NewApi" + message="Call requires API level 35 (current min is 34): `android.app.Activity#isResumed`" + errorLine1=" if (!activity.isResumed()) {" + errorLine2=" ~~~~~~~~~"> + <location + file="frameworks/base/nfc/java/android/nfc/NfcAdapter.java" + line="2457" + column="23"/> + </issue> + + <issue + id="NewApi" + message="Call requires API level 35 (current min is 34): `android.app.Activity#isResumed`" + errorLine1=" if (!activity.isResumed()) {" + errorLine2=" ~~~~~~~~~"> + <location + file="frameworks/base/nfc/java/android/nfc/cardemulation/NfcFCardEmulation.java" + line="315" + column="23"/> + </issue> + + <issue + id="NewApi" + message="Call requires API level 35 (current min is 34): `android.app.Activity#isResumed`" + errorLine1=" if (!activity.isResumed()) {" + errorLine2=" ~~~~~~~~~"> + <location + file="frameworks/base/nfc/java/android/nfc/cardemulation/NfcFCardEmulation.java" + line="351" + column="23"/> + </issue> + + <issue + id="FlaggedApi" + message="Method `PollingFrame()` is a flagged API and should be inside an `if (Flags.nfcReadPollingLoop())` check (or annotate the surrounding method `handleMessage` with `@FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP) to transfer requirement to caller`)" + errorLine1=" pollingFrames.add(new PollingFrame(frame));" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~"> + <location + file="frameworks/base/nfc/java/android/nfc/cardemulation/HostApduService.java" + line="335" + column="43"/> + </issue> + + <issue + id="FlaggedApi" + message="Method `processPollingFrames()` is a flagged API and should be inside an `if (Flags.nfcReadPollingLoop())` check (or annotate the surrounding method `handleMessage` with `@FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP) to transfer requirement to caller`)" + errorLine1=" processPollingFrames(pollingFrames);" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + <location + file="frameworks/base/nfc/java/android/nfc/cardemulation/HostApduService.java" + line="337" + column="21"/> + </issue> + + <issue + id="FlaggedApi" + message="Method `NfcOemExtension()` is a flagged API and should be inside an `if (Flags.nfcOemExtension())` check (or annotate the surrounding method `NfcAdapter` with `@FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) to transfer requirement to caller`)" + errorLine1=" mNfcOemExtension = new NfcOemExtension(mContext, this);" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + <location + file="frameworks/base/nfc/java/android/nfc/NfcAdapter.java" + line="895" + column="28"/> + </issue> + + <issue + id="FlaggedApi" + message="Method `onVendorNciResponse()` is a flagged API and should be inside an `if (Flags.nfcVendorCmd())` check (or annotate the surrounding method `onVendorResponseReceived` with `@FlaggedApi(Flags.FLAG_NFC_VENDOR_CMD) to transfer requirement to caller`)" + errorLine1=" executor.execute(() -> callback.onVendorNciResponse(gid, oid, payload));" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + <location + file="frameworks/base/nfc/java/android/nfc/NfcVendorNciCallbackListener.java" + line="88" + column="44"/> + </issue> + + <issue + id="FlaggedApi" + message="Method `onVendorNciNotification()` is a flagged API and should be inside an `if (Flags.nfcVendorCmd())` check (or annotate the surrounding method `onVendorNotificationReceived` with `@FlaggedApi(Flags.FLAG_NFC_VENDOR_CMD) to transfer requirement to caller`)" + errorLine1=" executor.execute(() -> callback.onVendorNciNotification(gid, oid, payload));" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + <location + file="frameworks/base/nfc/java/android/nfc/NfcVendorNciCallbackListener.java" + line="106" + column="44"/> + </issue> + +</issues>
\ No newline at end of file diff --git a/nfc/tests/Android.bp b/nfc/tests/Android.bp index 62566ee89fb8..6ebc03cc6ffc 100644 --- a/nfc/tests/Android.bp +++ b/nfc/tests/Android.bp @@ -13,6 +13,7 @@ // limitations under the License. package { + default_team: "trendy_team_fwk_nfc", // 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" |