From 24106379a58f1bc2d06a7770a88bcdcc42b3edf2 Mon Sep 17 00:00:00 2001 From: Roshan Pius Date: Fri, 13 Dec 2024 17:19:03 -0800 Subject: Revert "Revert "nfc(framework): Split non-updatable portion to a..." Revert submission 3414164-revert-3411958-framework-nfc-code-split-TOOKKZAIEM Reason for revert: Reland with some missing changes from aosp/main merged to avoid automerger conflicts. Reverted changes: /q/submissionid:3414164-revert-3411958-framework-nfc-code-split-TOOKKZAIEM Bug: 367426693 Change-Id: I620692a29e21d4a1d1083521894a4731648bbc3f Test: Compiles --- AconfigFlags.bp | 2 +- nfc-non-updatable/Android.bp | 23 + nfc-non-updatable/OWNERS | 2 + nfc-non-updatable/flags/flags.aconfig | 191 ++++ .../java/android/nfc/NfcServiceManager.java | 126 +++ .../java/android/nfc/cardemulation/AidGroup.aidl | 19 + .../java/android/nfc/cardemulation/AidGroup.java | 298 +++++ .../android/nfc/cardemulation/ApduServiceInfo.aidl | 19 + .../android/nfc/cardemulation/ApduServiceInfo.java | 1195 ++++++++++++++++++++ .../android/nfc/cardemulation/NfcFServiceInfo.aidl | 19 + .../android/nfc/cardemulation/NfcFServiceInfo.java | 497 ++++++++ nfc/Android.bp | 17 - nfc/java/android/nfc/NfcServiceManager.java | 126 --- nfc/java/android/nfc/cardemulation/AidGroup.aidl | 19 - nfc/java/android/nfc/cardemulation/AidGroup.java | 298 ----- .../android/nfc/cardemulation/ApduServiceInfo.aidl | 19 - .../android/nfc/cardemulation/ApduServiceInfo.java | 1195 -------------------- .../android/nfc/cardemulation/NfcFServiceInfo.aidl | 19 - .../android/nfc/cardemulation/NfcFServiceInfo.java | 497 -------- nfc/java/android/nfc/flags.aconfig | 191 ---- nfc/tests/Android.bp | 12 +- 21 files changed, 2400 insertions(+), 2384 deletions(-) create mode 100644 nfc-non-updatable/Android.bp create mode 100644 nfc-non-updatable/OWNERS create mode 100644 nfc-non-updatable/flags/flags.aconfig create mode 100644 nfc-non-updatable/java/android/nfc/NfcServiceManager.java create mode 100644 nfc-non-updatable/java/android/nfc/cardemulation/AidGroup.aidl create mode 100644 nfc-non-updatable/java/android/nfc/cardemulation/AidGroup.java create mode 100644 nfc-non-updatable/java/android/nfc/cardemulation/ApduServiceInfo.aidl create mode 100644 nfc-non-updatable/java/android/nfc/cardemulation/ApduServiceInfo.java create mode 100644 nfc-non-updatable/java/android/nfc/cardemulation/NfcFServiceInfo.aidl create mode 100644 nfc-non-updatable/java/android/nfc/cardemulation/NfcFServiceInfo.java delete mode 100644 nfc/java/android/nfc/NfcServiceManager.java delete mode 100644 nfc/java/android/nfc/cardemulation/AidGroup.aidl delete mode 100644 nfc/java/android/nfc/cardemulation/AidGroup.java delete mode 100644 nfc/java/android/nfc/cardemulation/ApduServiceInfo.aidl delete mode 100644 nfc/java/android/nfc/cardemulation/ApduServiceInfo.java delete mode 100644 nfc/java/android/nfc/cardemulation/NfcFServiceInfo.aidl delete mode 100644 nfc/java/android/nfc/cardemulation/NfcFServiceInfo.java delete mode 100644 nfc/java/android/nfc/flags.aconfig diff --git a/AconfigFlags.bp b/AconfigFlags.bp index abf5288ea532..b43053bda1f2 100644 --- a/AconfigFlags.bp +++ b/AconfigFlags.bp @@ -294,7 +294,7 @@ aconfig_declarations { name: "android.nfc.flags-aconfig", package: "android.nfc", container: "system", - srcs: ["nfc/java/android/nfc/*.aconfig"], + srcs: ["nfc-non-updatable/flags/*.aconfig"], } cc_aconfig_library { diff --git a/nfc-non-updatable/Android.bp b/nfc-non-updatable/Android.bp new file mode 100644 index 000000000000..ff987bb84b17 --- /dev/null +++ b/nfc-non-updatable/Android.bp @@ -0,0 +1,23 @@ +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" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +filegroup { + name: "framework-nfc-non-updatable-sources", + path: "java", + srcs: [ + "java/android/nfc/NfcServiceManager.java", + "java/android/nfc/cardemulation/ApduServiceInfo.aidl", + "java/android/nfc/cardemulation/ApduServiceInfo.java", + "java/android/nfc/cardemulation/NfcFServiceInfo.aidl", + "java/android/nfc/cardemulation/NfcFServiceInfo.java", + "java/android/nfc/cardemulation/AidGroup.aidl", + "java/android/nfc/cardemulation/AidGroup.java", + ], +} diff --git a/nfc-non-updatable/OWNERS b/nfc-non-updatable/OWNERS new file mode 100644 index 000000000000..f46dccd97974 --- /dev/null +++ b/nfc-non-updatable/OWNERS @@ -0,0 +1,2 @@ +# Bug component: 48448 +include platform/packages/apps/Nfc:/OWNERS \ No newline at end of file diff --git a/nfc-non-updatable/flags/flags.aconfig b/nfc-non-updatable/flags/flags.aconfig new file mode 100644 index 000000000000..ee287aba709f --- /dev/null +++ b/nfc-non-updatable/flags/flags.aconfig @@ -0,0 +1,191 @@ +package: "android.nfc" +container: "system" + +flag { + name: "nfc_event_listener" + is_exported: true + namespace: "nfc" + description: "Enable NFC Event listener APIs" + bug: "356447790" +} + +flag { + name: "enable_nfc_mainline" + is_exported: true + namespace: "nfc" + description: "Flag for NFC mainline changes" + bug: "292140387" +} + +flag { + name: "enable_nfc_reader_option" + is_exported: true + namespace: "nfc" + description: "Flag for NFC reader option API changes" + bug: "291187960" +} + +flag { + name: "enable_nfc_user_restriction" + is_exported: true + namespace: "nfc" + description: "Flag for NFC user restriction" + bug: "291187960" +} + +flag { + name: "nfc_observe_mode" + is_exported: true + namespace: "nfc" + description: "Enable NFC Observe Mode" + bug: "294217286" +} + +flag { + name: "nfc_read_polling_loop" + is_exported: true + namespace: "nfc" + description: "Enable NFC Polling Loop Notifications" + bug: "294217286" +} + +flag { + name: "nfc_observe_mode_st_shim" + namespace: "nfc" + description: "Enable NFC Observe Mode ST shim" + bug: "294217286" +} + +flag { + name: "nfc_read_polling_loop_st_shim" + namespace: "nfc" + description: "Enable NFC Polling Loop Notifications ST shim" + bug: "294217286" +} + +flag { + name: "enable_tag_detection_broadcasts" + namespace: "nfc" + description: "Enable sending broadcasts to Wallet role holder when a tag enters/leaves the field." + bug: "306203494" +} + +flag { + name: "enable_nfc_charging" + is_exported: true + namespace: "nfc" + description: "Flag for NFC charging changes" + bug: "292143899" +} + +flag { + name: "enable_nfc_set_discovery_tech" + is_exported: true + namespace: "nfc" + description: "Flag for NFC set discovery tech API" + bug: "300351519" +} + +flag { + name: "nfc_vendor_cmd" + is_exported: true + namespace: "nfc" + 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" +} + +flag { + name: "nfc_state_change" + is_exported: true + namespace: "nfc" + description: "Enable nfc state change API" + bug: "319934052" +} + +flag { + name: "nfc_set_default_disc_tech" + is_exported: true + namespace: "nfc" + description: "Flag for NFC set default disc tech API" + bug: "321311407" +} + +flag { + name: "nfc_persist_log" + is_exported: true + namespace: "nfc" + description: "Enable NFC persistent log support" + bug: "321310044" +} + +flag { + name: "nfc_action_manage_services_settings" + is_exported: true + namespace: "nfc" + description: "Add Settings.ACTION_MANAGE_OTHER_NFC_SERVICES_SETTINGS" + bug: "358129872" +} + +flag { + name: "nfc_override_recover_routing_table" + is_exported: true + namespace: "nfc" + description: "Enable override and recover routing table" + bug: "329043523" +} + +flag { + name: "nfc_watchdog" + is_exported: true + namespace: "nfc" + description: "Enable watchdog for the NFC system process" + bug: "362937338" +} + +flag { + name: "enable_card_emulation_euicc" + is_exported: true + namespace: "nfc" + description: "Enable EUICC card emulation" + bug: "321314635" +} + +flag { + name: "nfc_state_change_security_log_event_enabled" + is_exported: true + namespace: "nfc" + description: "Enabling security log for nfc state change" + bug: "319934052" +} + +flag { + name: "nfc_associated_role_services" + is_exported: true + namespace: "nfc" + description: "Share wallet role routing priority with associated services" + bug: "366243361" +} + +flag { + name: "nfc_set_service_enabled_for_category_other" + is_exported: true + namespace: "nfc" + description: "Enable set service enabled for category other" + bug: "338157113" +} + +flag { + name: "nfc_check_tag_intent_preference" + is_exported: true + namespace: "nfc" + description: "App can check its tag intent preference status" + bug: "335916336" +} diff --git a/nfc-non-updatable/java/android/nfc/NfcServiceManager.java b/nfc-non-updatable/java/android/nfc/NfcServiceManager.java new file mode 100644 index 000000000000..5582f1154cad --- /dev/null +++ b/nfc-non-updatable/java/android/nfc/NfcServiceManager.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +/********************************************************************** + * This file is not a part of the NFC mainline modure * + * *******************************************************************/ + +package android.nfc; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.annotation.SystemApi.Client; +import android.content.Context; +import android.os.IBinder; +import android.os.ServiceManager; + +/** + * Provides a way to register and obtain the system service binder objects managed by the nfc + * service. + * + * @hide + */ +@SystemApi(client = Client.MODULE_LIBRARIES) +public class NfcServiceManager { + + /** + * @hide + */ + public NfcServiceManager() { + } + + /** + * A class that exposes the methods to register and obtain each system service. + */ + public static final class ServiceRegisterer { + private final String mServiceName; + + /** + * @hide + */ + public ServiceRegisterer(String serviceName) { + mServiceName = serviceName; + } + + /** + * Register a system server binding object for a service. + */ + public void register(@NonNull IBinder service) { + ServiceManager.addService(mServiceName, service); + } + + /** + * Get the system server binding object for a service. + * + *

This blocks until the service instance is ready, + * or a timeout happens, in which case it returns null. + */ + @Nullable + public IBinder get() { + return ServiceManager.getService(mServiceName); + } + + /** + * Get the system server binding object for a service. + * + *

This blocks until the service instance is ready, + * or a timeout happens, in which case it throws {@link ServiceNotFoundException}. + */ + @NonNull + public IBinder getOrThrow() throws ServiceNotFoundException { + try { + return ServiceManager.getServiceOrThrow(mServiceName); + } catch (ServiceManager.ServiceNotFoundException e) { + throw new ServiceNotFoundException(mServiceName); + } + } + + /** + * Get the system server binding object for a service. If the specified service is + * not available, it returns null. + */ + @Nullable + public IBinder tryGet() { + return ServiceManager.checkService(mServiceName); + } + } + + /** + * See {@link ServiceRegisterer#getOrThrow}. + * + */ + public static class ServiceNotFoundException extends ServiceManager.ServiceNotFoundException { + /** + * Constructor. + * + * @param name the name of the binder service that cannot be found. + * + */ + public ServiceNotFoundException(@NonNull String name) { + super(name); + } + } + + /** + * Returns {@link ServiceRegisterer} for the "nfc" service. + */ + @NonNull + public ServiceRegisterer getNfcManagerServiceRegisterer() { + return new ServiceRegisterer(Context.NFC_SERVICE); + } +} diff --git a/nfc-non-updatable/java/android/nfc/cardemulation/AidGroup.aidl b/nfc-non-updatable/java/android/nfc/cardemulation/AidGroup.aidl new file mode 100644 index 000000000000..56d6fa559677 --- /dev/null +++ b/nfc-non-updatable/java/android/nfc/cardemulation/AidGroup.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.nfc.cardemulation; + +parcelable AidGroup; diff --git a/nfc-non-updatable/java/android/nfc/cardemulation/AidGroup.java b/nfc-non-updatable/java/android/nfc/cardemulation/AidGroup.java new file mode 100644 index 000000000000..ae3e333051d7 --- /dev/null +++ b/nfc-non-updatable/java/android/nfc/cardemulation/AidGroup.java @@ -0,0 +1,298 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.nfc.cardemulation; + +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.nfc.Flags; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; +import android.util.proto.ProtoOutputStream; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.regex.Pattern; + +/********************************************************************** + * This file is not a part of the NFC mainline module * + * *******************************************************************/ + +/** + * The AidGroup class represents a group of Application Identifiers (AIDs). + * + *

The format of AIDs is defined in the ISO/IEC 7816-4 specification. This class + * requires the AIDs to be input as a hexadecimal string, with an even amount of + * hexadecimal characters, e.g. "F014811481". + * + * @hide + */ +@SystemApi +@FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) +public final class AidGroup implements Parcelable { + /** + * The maximum number of AIDs that can be present in any one group. + */ + private static final int MAX_NUM_AIDS = 256; + + private static final String TAG = "AidGroup"; + + + private final List mAids; + private final String mCategory; + @SuppressWarnings("unused") // Unused as of now, but part of the XML input. + private final String mDescription; + + /** + * Creates a new AidGroup object. + * + * @param aids list of AIDs present in the group + * @param category category of this group, e.g. {@link CardEmulation#CATEGORY_PAYMENT} + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public AidGroup(@NonNull List aids, @Nullable String category) { + if (aids == null || aids.size() == 0) { + throw new IllegalArgumentException("No AIDS in AID group."); + } + if (aids.size() > MAX_NUM_AIDS) { + throw new IllegalArgumentException("Too many AIDs in AID group."); + } + for (String aid : aids) { + if (!isValidAid(aid)) { + throw new IllegalArgumentException("AID " + aid + " is not a valid AID."); + } + } + if (isValidCategory(category)) { + this.mCategory = category; + } else { + this.mCategory = CardEmulation.CATEGORY_OTHER; + } + this.mAids = new ArrayList(aids.size()); + for (String aid : aids) { + this.mAids.add(aid.toUpperCase(Locale.US)); + } + this.mDescription = null; + } + + /** + * Creates a new AidGroup object. + * + * @param category category of this group, e.g. {@link CardEmulation#CATEGORY_PAYMENT} + * @param description description of this group + */ + AidGroup(@NonNull String category, @NonNull String description) { + this.mAids = new ArrayList(); + this.mCategory = category; + this.mDescription = description; + } + + /** + * Returns the category of this group. + * @return the category of this AID group + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull + public String getCategory() { + return mCategory; + } + + /** + * Returns the list of AIDs in this group. + * + * @return the list of AIDs in this group + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull + public List getAids() { + return mAids; + } + + @Override + public String toString() { + StringBuilder out = new StringBuilder("Category: " + mCategory + + ", AIDs:"); + for (String aid : mAids) { + out.append(aid); + out.append(", "); + } + return out.toString(); + } + + /** + * Dump debugging info as AidGroupProto. + * + * If the output belongs to a sub message, the caller is responsible for wrapping this function + * between {@link ProtoOutputStream#start(long)} and {@link ProtoOutputStream#end(long)}. + * + * @param proto the ProtoOutputStream to write to + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public void dump(@NonNull ProtoOutputStream proto) { + proto.write(AidGroupProto.CATEGORY, mCategory); + for (String aid : mAids) { + proto.write(AidGroupProto.AIDS, aid); + } + } + + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @Override + public int describeContents() { + return 0; + } + + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeString8(mCategory); + dest.writeInt(mAids.size()); + if (mAids.size() > 0) { + dest.writeStringList(mAids); + } + } + + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public static final @NonNull Parcelable.Creator CREATOR = + new Parcelable.Creator() { + + @Override + public AidGroup createFromParcel(Parcel source) { + String category = source.readString8(); + int listSize = source.readInt(); + ArrayList aidList = new ArrayList(); + if (listSize > 0) { + source.readStringList(aidList); + } + return new AidGroup(aidList, category); + } + + @Override + public AidGroup[] newArray(int size) { + return new AidGroup[size]; + } + }; + + /** + * Create an instance of AID group from XML file. + * + * @param parser input xml parser stream + * @throws XmlPullParserException If an error occurs parsing the element. + * @throws IOException If an error occurs reading the element. + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @Nullable + public static AidGroup createFromXml(@NonNull XmlPullParser parser) + throws XmlPullParserException, IOException { + String category = null; + ArrayList aids = new ArrayList(); + AidGroup group = null; + boolean inGroup = false; + + int eventType = parser.getEventType(); + int minDepth = parser.getDepth(); + while (eventType != XmlPullParser.END_DOCUMENT && parser.getDepth() >= minDepth) { + String tagName = parser.getName(); + if (eventType == XmlPullParser.START_TAG) { + if (tagName.equals("aid")) { + if (inGroup) { + String aid = parser.getAttributeValue(null, "value"); + if (aid != null) { + aids.add(aid.toUpperCase()); + } + } else { + Log.d(TAG, "Ignoring tag while not in group"); + } + } else if (tagName.equals("aid-group")) { + category = parser.getAttributeValue(null, "category"); + if (category == null) { + Log.e(TAG, " tag without valid category"); + return null; + } + inGroup = true; + } else { + Log.d(TAG, "Ignoring unexpected tag: " + tagName); + } + } else if (eventType == XmlPullParser.END_TAG) { + if (tagName.equals("aid-group") && inGroup && aids.size() > 0) { + group = new AidGroup(aids, category); + break; + } + } + eventType = parser.next(); + } + return group; + } + + /** + * Serialize instance of AID group to XML file. + * @param out XML serializer stream + * @throws IOException If an error occurs reading the element. + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public void writeAsXml(@NonNull XmlSerializer out) throws IOException { + out.startTag(null, "aid-group"); + out.attribute(null, "category", mCategory); + for (String aid : mAids) { + out.startTag(null, "aid"); + out.attribute(null, "value", aid); + out.endTag(null, "aid"); + } + out.endTag(null, "aid-group"); + } + + private static boolean isValidCategory(String category) { + return CardEmulation.CATEGORY_PAYMENT.equals(category) || + CardEmulation.CATEGORY_OTHER.equals(category); + } + + private static final Pattern AID_PATTERN = Pattern.compile("[0-9A-Fa-f]{10,32}\\*?\\#?"); + /** + * Copied over from {@link CardEmulation#isValidAid(String)} + * @hide + */ + private static boolean isValidAid(String aid) { + if (aid == null) + return false; + + // If a prefix/subset AID, the total length must be odd (even # of AID chars + '*') + if ((aid.endsWith("*") || aid.endsWith("#")) && ((aid.length() % 2) == 0)) { + Log.e(TAG, "AID " + aid + " is not a valid AID."); + return false; + } + + // If not a prefix/subset AID, the total length must be even (even # of AID chars) + if ((!(aid.endsWith("*") || aid.endsWith("#"))) && ((aid.length() % 2) != 0)) { + Log.e(TAG, "AID " + aid + " is not a valid AID."); + return false; + } + + // Verify hex characters + if (!AID_PATTERN.matcher(aid).matches()) { + Log.e(TAG, "AID " + aid + " is not a valid AID."); + return false; + } + + return true; + } +} diff --git a/nfc-non-updatable/java/android/nfc/cardemulation/ApduServiceInfo.aidl b/nfc-non-updatable/java/android/nfc/cardemulation/ApduServiceInfo.aidl new file mode 100644 index 000000000000..a62fdd6a6c5c --- /dev/null +++ b/nfc-non-updatable/java/android/nfc/cardemulation/ApduServiceInfo.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.nfc.cardemulation; + +parcelable ApduServiceInfo; diff --git a/nfc-non-updatable/java/android/nfc/cardemulation/ApduServiceInfo.java b/nfc-non-updatable/java/android/nfc/cardemulation/ApduServiceInfo.java new file mode 100644 index 000000000000..7f64dbea0be3 --- /dev/null +++ b/nfc-non-updatable/java/android/nfc/cardemulation/ApduServiceInfo.java @@ -0,0 +1,1195 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/********************************************************************** + * This file is not a part of the NFC mainline module * + * *******************************************************************/ + +package android.nfc.cardemulation; + +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.compat.annotation.UnsupportedAppUsage; +import android.content.ComponentName; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.content.res.Resources; +import android.content.res.Resources.NotFoundException; +import android.content.res.TypedArray; +import android.content.res.XmlResourceParser; +import android.graphics.drawable.Drawable; +import android.nfc.Flags; +import android.os.Parcel; +import android.os.ParcelFileDescriptor; +import android.os.Parcelable; +import android.util.AttributeSet; +import android.util.Log; +import android.util.Xml; +import android.util.proto.ProtoOutputStream; + +import com.android.internal.R; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.TreeMap; +import java.util.regex.Pattern; + +/** + * Class holding APDU service info. + * + * @hide + */ +@SystemApi +@FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) +public final class ApduServiceInfo implements Parcelable { + private static final String TAG = "ApduServiceInfo"; + + /** + * Component level {@link android.content.pm.PackageManager.Property PackageManager + * .Property} for a system application to change its icon and label + * on the default applications page. This property should be added to an + * {@link HostApduService} declaration in the manifest. + * + *

For example: + *

+     * <service
+     *     android:apduServiceBanner="@drawable/product_logo"
+     *     android:label="@string/service_label">
+     *      <property
+     *          android:name="android.content.PROPERTY_WALLET_ICON_AND_LABEL_HOLDER"
+     *          android:value="true"/>
+     * </service>
+     * 
+ * @hide + */ + @SystemApi + @FlaggedApi(android.permission.flags.Flags.FLAG_WALLET_ROLE_ICON_PROPERTY_ENABLED) + public static final String PROPERTY_WALLET_PREFERRED_BANNER_AND_LABEL = + "android.nfc.cardemulation.PROPERTY_WALLET_PREFERRED_BANNER_AND_LABEL"; + + /** + * The service that implements this + */ + private final ResolveInfo mService; + + /** + * Description of the service + */ + private final String mDescription; + + /** + * Whether this service represents AIDs running on the host CPU + */ + private final boolean mOnHost; + + /** + * Offhost reader name. + * eg: SIM, eSE etc + */ + private String mOffHostName; + + /** + * Offhost reader name from manifest file. + * Used for resetOffHostSecureElement() + */ + private final String mStaticOffHostName; + + /** + * Mapping from category to static AID group + */ + private final HashMap mStaticAidGroups; + + /** + * Mapping from category to dynamic AID group + */ + private final HashMap mDynamicAidGroups; + + + private final Map mAutoTransact; + + private final Map mAutoTransactPatterns; + + /** + * Whether this service should only be started when the device is unlocked. + */ + private final boolean mRequiresDeviceUnlock; + + /** + * Whether this service should only be started when the device is screen on. + */ + private final boolean mRequiresDeviceScreenOn; + + /** + * The id of the service banner specified in XML. + */ + private final int mBannerResourceId; + + /** + * The uid of the package the service belongs to + */ + private final int mUid; + + /** + * Settings Activity for this service + */ + private final String mSettingsActivityName; + + /** + * State of the service for CATEGORY_OTHER selection + */ + private boolean mCategoryOtherServiceEnabled; + + /** + * Whether the NFC stack should default to Observe Mode when this preferred service. + */ + private boolean mShouldDefaultToObserveMode; + + /** + * Whether or not this service wants to share the same routing priority as the + * Wallet role owner. + */ + private boolean mWantsRoleHolderPriority; + + /** + * @hide + */ + @UnsupportedAppUsage + public ApduServiceInfo(ResolveInfo info, boolean onHost, String description, + ArrayList staticAidGroups, ArrayList dynamicAidGroups, + boolean requiresUnlock, int bannerResource, int uid, + String settingsActivityName, String offHost, String staticOffHost) { + this(info, onHost, description, staticAidGroups, dynamicAidGroups, + requiresUnlock, bannerResource, uid, settingsActivityName, + offHost, staticOffHost, false); + } + + /** + * @hide + */ + public ApduServiceInfo(ResolveInfo info, boolean onHost, String description, + ArrayList staticAidGroups, ArrayList dynamicAidGroups, + boolean requiresUnlock, int bannerResource, int uid, + String settingsActivityName, String offHost, String staticOffHost, + boolean isEnabled) { + this(info, onHost, description, staticAidGroups, dynamicAidGroups, + requiresUnlock, onHost ? true : false, bannerResource, uid, + settingsActivityName, offHost, staticOffHost, isEnabled); + } + + /** + * @hide + */ + public ApduServiceInfo(ResolveInfo info, boolean onHost, String description, + List staticAidGroups, List 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(), new TreeMap<>( + Comparator.comparing(Pattern::toString))); + } + + /** + * @hide + */ + public ApduServiceInfo(ResolveInfo info, boolean onHost, String description, + List staticAidGroups, List dynamicAidGroups, + boolean requiresUnlock, boolean requiresScreenOn, int bannerResource, int uid, + String settingsActivityName, String offHost, String staticOffHost, boolean isEnabled, + Map autoTransact, Map autoTransactPatterns) { + this.mService = info; + this.mDescription = description; + this.mStaticAidGroups = new HashMap(); + this.mDynamicAidGroups = new HashMap(); + this.mAutoTransact = autoTransact; + this.mAutoTransactPatterns = autoTransactPatterns; + this.mOffHostName = offHost; + this.mStaticOffHostName = staticOffHost; + this.mOnHost = onHost; + this.mRequiresDeviceUnlock = requiresUnlock; + this.mRequiresDeviceScreenOn = requiresScreenOn; + for (AidGroup aidGroup : staticAidGroups) { + this.mStaticAidGroups.put(aidGroup.getCategory(), aidGroup); + } + for (AidGroup aidGroup : dynamicAidGroups) { + this.mDynamicAidGroups.put(aidGroup.getCategory(), aidGroup); + } + this.mBannerResourceId = bannerResource; + this.mUid = uid; + this.mSettingsActivityName = settingsActivityName; + this.mCategoryOtherServiceEnabled = isEnabled; + } + + /** + * Creates a new ApduServiceInfo object. + * + * @param pm packageManager instance + * @param info app component info + * @param onHost whether service is on host or not (secure element) + * @throws XmlPullParserException If an error occurs parsing the element. + * @throws IOException If an error occurs reading the element. + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public ApduServiceInfo(@NonNull PackageManager pm, @NonNull ResolveInfo info, boolean onHost) + throws XmlPullParserException, IOException { + ServiceInfo si = info.serviceInfo; + XmlResourceParser parser = null; + try { + if (onHost) { + parser = si.loadXmlMetaData(pm, HostApduService.SERVICE_META_DATA); + if (parser == null) { + throw new XmlPullParserException("No " + HostApduService.SERVICE_META_DATA + + " meta-data"); + } + } else { + parser = si.loadXmlMetaData(pm, OffHostApduService.SERVICE_META_DATA); + if (parser == null) { + throw new XmlPullParserException("No " + OffHostApduService.SERVICE_META_DATA + + " meta-data"); + } + } + + int eventType = parser.getEventType(); + while (eventType != XmlPullParser.START_TAG && eventType != XmlPullParser.END_DOCUMENT) { + eventType = parser.next(); + } + + String tagName = parser.getName(); + if (onHost && !"host-apdu-service".equals(tagName)) { + throw new XmlPullParserException( + "Meta-data does not start with tag"); + } else if (!onHost && !"offhost-apdu-service".equals(tagName)) { + throw new XmlPullParserException( + "Meta-data does not start with tag"); + } + + Resources res = pm.getResourcesForApplication(si.applicationInfo); + AttributeSet attrs = Xml.asAttributeSet(parser); + if (onHost) { + TypedArray sa = res.obtainAttributes(attrs, + com.android.internal.R.styleable.HostApduService); + mService = info; + mDescription = sa.getString( + com.android.internal.R.styleable.HostApduService_description); + mRequiresDeviceUnlock = sa.getBoolean( + com.android.internal.R.styleable.HostApduService_requireDeviceUnlock, + false); + mRequiresDeviceScreenOn = sa.getBoolean( + com.android.internal.R.styleable.HostApduService_requireDeviceScreenOn, + true); + mBannerResourceId = sa.getResourceId( + com.android.internal.R.styleable.HostApduService_apduServiceBanner, -1); + mSettingsActivityName = sa.getString( + com.android.internal.R.styleable.HostApduService_settingsActivity); + mOffHostName = null; + mStaticOffHostName = mOffHostName; + mShouldDefaultToObserveMode = sa.getBoolean( + R.styleable.HostApduService_shouldDefaultToObserveMode, + false); + if (Flags.nfcAssociatedRoleServices()) { + mWantsRoleHolderPriority = sa.getBoolean( + R.styleable.HostApduService_wantsRoleHolderPriority, + false + ); + } + sa.recycle(); + } else { + TypedArray sa = res.obtainAttributes(attrs, + com.android.internal.R.styleable.OffHostApduService); + mService = info; + mDescription = sa.getString( + com.android.internal.R.styleable.OffHostApduService_description); + mRequiresDeviceUnlock = sa.getBoolean( + com.android.internal.R.styleable.OffHostApduService_requireDeviceUnlock, + false); + mRequiresDeviceScreenOn = sa.getBoolean( + com.android.internal.R.styleable.OffHostApduService_requireDeviceScreenOn, + false); + mBannerResourceId = sa.getResourceId( + com.android.internal.R.styleable.OffHostApduService_apduServiceBanner, -1); + mSettingsActivityName = sa.getString( + com.android.internal.R.styleable.HostApduService_settingsActivity); + mOffHostName = sa.getString( + com.android.internal.R.styleable.OffHostApduService_secureElementName); + mShouldDefaultToObserveMode = sa.getBoolean( + R.styleable.OffHostApduService_shouldDefaultToObserveMode, + false); + if (mOffHostName != null) { + if (mOffHostName.equals("eSE")) { + mOffHostName = "eSE1"; + } else if (mOffHostName.equals("SIM")) { + mOffHostName = "SIM1"; + } + } + mStaticOffHostName = mOffHostName; + if (Flags.nfcAssociatedRoleServices()) { + mWantsRoleHolderPriority = sa.getBoolean( + R.styleable.OffHostApduService_wantsRoleHolderPriority, + false + ); + } + sa.recycle(); + } + + mStaticAidGroups = new HashMap(); + mDynamicAidGroups = new HashMap(); + mAutoTransact = new HashMap(); + mAutoTransactPatterns = new TreeMap( + Comparator.comparing(Pattern::toString)); + mOnHost = onHost; + + final int depth = parser.getDepth(); + AidGroup currentGroup = null; + + // Parsed values for the current AID group + while (((eventType = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth) + && eventType != XmlPullParser.END_DOCUMENT) { + tagName = parser.getName(); + if (eventType == XmlPullParser.START_TAG && "aid-group".equals(tagName) && + currentGroup == null) { + final TypedArray groupAttrs = res.obtainAttributes(attrs, + com.android.internal.R.styleable.AidGroup); + // Get category of AID group + String groupCategory = groupAttrs.getString( + com.android.internal.R.styleable.AidGroup_category); + String groupDescription = groupAttrs.getString( + com.android.internal.R.styleable.AidGroup_description); + if (!CardEmulation.CATEGORY_PAYMENT.equals(groupCategory)) { + groupCategory = CardEmulation.CATEGORY_OTHER; + } + currentGroup = mStaticAidGroups.get(groupCategory); + if (currentGroup != null) { + if (!CardEmulation.CATEGORY_OTHER.equals(groupCategory)) { + Log.e(TAG, "Not allowing multiple aid-groups in the " + + groupCategory + " category"); + currentGroup = null; + } + } else { + currentGroup = new AidGroup(groupCategory, groupDescription); + } + groupAttrs.recycle(); + } else if (eventType == XmlPullParser.END_TAG && "aid-group".equals(tagName) && + currentGroup != null) { + if (currentGroup.getAids().size() > 0) { + if (!mStaticAidGroups.containsKey(currentGroup.getCategory())) { + mStaticAidGroups.put(currentGroup.getCategory(), currentGroup); + } + } else { + Log.e(TAG, "Not adding with empty or invalid AIDs"); + } + currentGroup = null; + } else if (eventType == XmlPullParser.START_TAG && "aid-filter".equals(tagName) && + currentGroup != null) { + final TypedArray a = res.obtainAttributes(attrs, + com.android.internal.R.styleable.AidFilter); + String aid = a.getString(com.android.internal.R.styleable.AidFilter_name). + toUpperCase(); + if (isValidAid(aid) && !currentGroup.getAids().contains(aid)) { + currentGroup.getAids().add(aid); + } else { + Log.e(TAG, "Ignoring invalid or duplicate aid: " + aid); + } + a.recycle(); + } else if (eventType == XmlPullParser.START_TAG && + "aid-prefix-filter".equals(tagName) && currentGroup != null) { + final TypedArray a = res.obtainAttributes(attrs, + com.android.internal.R.styleable.AidFilter); + String aid = a.getString(com.android.internal.R.styleable.AidFilter_name). + toUpperCase(); + // Add wildcard char to indicate prefix + aid = aid.concat("*"); + if (isValidAid(aid) && !currentGroup.getAids().contains(aid)) { + currentGroup.getAids().add(aid); + } else { + Log.e(TAG, "Ignoring invalid or duplicate aid: " + aid); + } + a.recycle(); + } else if (eventType == XmlPullParser.START_TAG && + tagName.equals("aid-suffix-filter") && currentGroup != null) { + final TypedArray a = res.obtainAttributes(attrs, + com.android.internal.R.styleable.AidFilter); + String aid = a.getString(com.android.internal.R.styleable.AidFilter_name). + toUpperCase(); + // Add wildcard char to indicate suffix + aid = aid.concat("#"); + if (isValidAid(aid) && !currentGroup.getAids().contains(aid)) { + currentGroup.getAids().add(aid); + } else { + Log.e(TAG, "Ignoring invalid or duplicate aid: " + aid); + } + a.recycle(); + } 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); + if (!mOnHost && !autoTransact) { + Log.e(TAG, "Ignoring polling-loop-filter " + plf + + " for offhost service that isn't autoTransact"); + } else { + mAutoTransact.put(plf, autoTransact); + } + a.recycle(); + } else if (eventType == XmlPullParser.START_TAG + && "polling-loop-pattern-filter".equals(tagName) && currentGroup == null) { + final TypedArray a = res.obtainAttributes(attrs, + com.android.internal.R.styleable.PollingLoopPatternFilter); + String plf = a.getString( + com.android.internal.R.styleable.PollingLoopPatternFilter_name) + .toUpperCase(Locale.ROOT); + boolean autoTransact = a.getBoolean( + com.android.internal.R.styleable.PollingLoopFilter_autoTransact, + false); + if (!mOnHost && !autoTransact) { + Log.e(TAG, "Ignoring polling-loop-filter " + plf + + " for offhost service that isn't autoTransact"); + } else { + mAutoTransactPatterns.put(Pattern.compile(plf), autoTransact); + } + a.recycle(); + } + } + } catch (NameNotFoundException e) { + throw new XmlPullParserException("Unable to create context for: " + si.packageName); + } finally { + if (parser != null) parser.close(); + } + // Set uid + mUid = si.applicationInfo.uid; + + mCategoryOtherServiceEnabled = true; // support other category + + } + + /** + * Returns the app component corresponding to this APDU service. + * + * @return app component for this service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull + public ComponentName getComponent() { + return new ComponentName(mService.serviceInfo.packageName, + mService.serviceInfo.name); + } + + /** + * Returns the offhost secure element name (if the service is offhost). + * + * @return offhost secure element name for offhost services + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @Nullable + public String getOffHostSecureElement() { + return mOffHostName; + } + + /** + * Returns a consolidated list of AIDs from the AID groups + * registered by this service. Note that if a service has both + * a static (manifest-based) AID group for a category and a dynamic + * AID group, only the dynamically registered AIDs will be returned + * for that category. + * @return List of AIDs registered by the service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull + public List getAids() { + final ArrayList aids = new ArrayList(); + for (AidGroup group : getAidGroups()) { + aids.addAll(group.getAids()); + } + return aids; + } + + /** + * 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 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) { + if (mAutoTransact.getOrDefault(plf.toUpperCase(Locale.ROOT), false)) { + return true; + } + List patternMatches = mAutoTransactPatterns.keySet().stream() + .filter(p -> p.matcher(plf).matches()).toList(); + if (patternMatches == null || patternMatches.size() == 0) { + return false; + } + for (Pattern patternMatch : patternMatches) { + if (mAutoTransactPatterns.get(patternMatch)) { + return true; + } + } + return false; + } + + /** + * Returns the current polling loop pattern filters for this service. + * @return List of polling loop pattern filters. + */ + @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP) + @NonNull + public List getPollingLoopPatternFilters() { + return new ArrayList<>(mAutoTransactPatterns.keySet()); + } + + /** + * Returns a consolidated list of AIDs with prefixes from the AID groups + * registered by this service. Note that if a service has both + * a static (manifest-based) AID group for a category and a dynamic + * AID group, only the dynamically registered AIDs will be returned + * for that category. + * @return List of prefix AIDs registered by the service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull + public List getPrefixAids() { + final ArrayList prefixAids = new ArrayList(); + for (AidGroup group : getAidGroups()) { + for (String aid : group.getAids()) { + if (aid.endsWith("*")) { + prefixAids.add(aid); + } + } + } + return prefixAids; + } + + /** + * Returns a consolidated list of AIDs with subsets from the AID groups + * registered by this service. Note that if a service has both + * a static (manifest-based) AID group for a category and a dynamic + * AID group, only the dynamically registered AIDs will be returned + * for that category. + * @return List of prefix AIDs registered by the service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull + public List getSubsetAids() { + final ArrayList subsetAids = new ArrayList(); + for (AidGroup group : getAidGroups()) { + for (String aid : group.getAids()) { + if (aid.endsWith("#")) { + subsetAids.add(aid); + } + } + } + return subsetAids; + } + + /** + * Returns the registered AID group for this category. + * + * @param category category name + * @return {@link AidGroup} instance for the provided category + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull + public AidGroup getDynamicAidGroupForCategory(@NonNull String category) { + return mDynamicAidGroups.get(category); + } + + /** + * Removes the registered AID group for this category. + * + * @param category category name + * @return {@code true} if an AID group existed + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull + public boolean removeDynamicAidGroupForCategory(@NonNull String category) { + return (mDynamicAidGroups.remove(category) != null); + } + + /** + * Returns a consolidated list of AID groups + * registered by this service. Note that if a service has both + * a static (manifest-based) AID group for a category and a dynamic + * AID group, only the dynamically registered AID group will be returned + * for that category. + * @return List of AIDs registered by the service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull + public List getAidGroups() { + final ArrayList groups = new ArrayList(); + for (Map.Entry entry : mDynamicAidGroups.entrySet()) { + groups.add(entry.getValue()); + } + for (Map.Entry entry : mStaticAidGroups.entrySet()) { + if (!mDynamicAidGroups.containsKey(entry.getKey())) { + // Consolidate AID groups - don't return static ones + // if a dynamic group exists for the category. + groups.add(entry.getValue()); + } + } + return groups; + } + + /** + * Returns the category to which this service has attributed the AID that is passed in, + * or null if we don't know this AID. + * @param aid AID to lookup for + * @return category name corresponding to this AID + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull + public String getCategoryForAid(@NonNull String aid) { + List groups = getAidGroups(); + for (AidGroup group : groups) { + if (group.getAids().contains(aid.toUpperCase())) { + return group.getCategory(); + } + } + return null; + } + + /** + * Returns whether there is any AID group for this category. + * @param category category name + * @return {@code true} if an AID group exists + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public boolean hasCategory(@NonNull String category) { + return (mStaticAidGroups.containsKey(category) || mDynamicAidGroups.containsKey(category)); + } + + /** + * Returns whether the service is on host or not. + * @return true if the service is on host (not secure element) + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public boolean isOnHost() { + return mOnHost; + } + + /** + * Returns whether the service requires device unlock. + * @return whether the service requires device unlock + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public boolean requiresUnlock() { + return mRequiresDeviceUnlock; + } + + /** + * Returns whether this service should only be started when the device is screen on. + * @return whether the service requires screen on + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public boolean requiresScreenOn() { + return mRequiresDeviceScreenOn; + } + + /** + * Returns 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 whether or not this service wants to share the Wallet role holder priority + * with other packages/services with the same signature. + * + * @return whether or not this service wants to share priority + */ + @FlaggedApi(Flags.FLAG_NFC_ASSOCIATED_ROLE_SERVICES) + public boolean wantsRoleHolderPriority() { + return mWantsRoleHolderPriority; + } + + /** + * Returns description of service. + * @return user readable description of service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull + public String getDescription() { + return mDescription; + } + + /** + * Returns uid of service. + * @return uid of the service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public int getUid() { + return mUid; + } + + /** + * Add or replace an AID group to this service. + * @param aidGroup instance of aid group to set or replace + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public void setDynamicAidGroup(@NonNull AidGroup aidGroup) { + mDynamicAidGroups.put(aidGroup.getCategory(), aidGroup); + } + + /** + * 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 valid hexadecimal string + * @param autoTransact when true, disable observe mode when this filter matches, when false, + * matching does not change the observe mode state + */ + @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP) + public void addPollingLoopFilter(@NonNull String pollingLoopFilter, + boolean autoTransact) { + if (!mOnHost && !autoTransact) { + return; + } + 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)); + } + + /** + * Add a Polling Loop Pattern 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 pollingLoopPatternFilter the polling loop pattern filter to add, must be a valid + * regex to match a hexadecimal string + * @param autoTransact when true, disable observe mode when this filter matches, when false, + * matching does not change the observe mode state + */ + @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP) + public void addPollingLoopPatternFilter(@NonNull String pollingLoopPatternFilter, + boolean autoTransact) { + if (!mOnHost && !autoTransact) { + return; + } + mAutoTransactPatterns.put(Pattern.compile(pollingLoopPatternFilter), autoTransact); + } + + /** + * Remove a Polling Loop Pattern Filter. Custom NFC polling frames that match this filter will + * no longer be delivered to {@link HostApduService#processPollingFrames(List)}. + * @param pollingLoopPatternFilter this polling loop filter to add. + */ + @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP) + public void removePollingLoopPatternFilter(@NonNull String pollingLoopPatternFilter) { + mAutoTransactPatterns.remove( + Pattern.compile(pollingLoopPatternFilter.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 + * TS26_NFC_REQ_069: For UICC, Secure Element Name SHALL be SIM[smartcard slot] + * (e.g. SIM/SIM1, SIM2… SIMn). + * TS26_NFC_REQ_070: For embedded SE, Secure Element Name SHALL be eSE[number] + * (e.g. eSE/eSE1, eSE2, etc.). + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public void setOffHostSecureElement(@NonNull String offHost) { + mOffHostName = offHost; + } + + /** + * Resets the off host Secure Element to statically defined + * by the service in the manifest file. + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public void resetOffHostSecureElement() { + mOffHostName = mStaticOffHostName; + } + + /** + * Load label for this service. + * @param pm packagemanager instance + * @return label name corresponding to service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull + public CharSequence loadLabel(@NonNull PackageManager pm) { + return mService.loadLabel(pm); + } + + /** + * Load application label for this service. + * @param pm packagemanager instance + * @return app label name corresponding to service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull + public CharSequence loadAppLabel(@NonNull PackageManager pm) { + try { + return pm.getApplicationLabel(pm.getApplicationInfo( + mService.resolvePackageName, PackageManager.GET_META_DATA)); + } catch (PackageManager.NameNotFoundException e) { + return null; + } + } + + /** + * Load application icon for this service. + * @param pm packagemanager instance + * @return app icon corresponding to service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull + public Drawable loadIcon(@NonNull PackageManager pm) { + return mService.loadIcon(pm); + } + + /** + * Load application banner for this service. + * @param pm packagemanager instance + * @return app banner corresponding to service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull + public Drawable loadBanner(@NonNull PackageManager pm) { + Resources res; + try { + res = pm.getResourcesForApplication(mService.serviceInfo.packageName); + Drawable banner = res.getDrawable(mBannerResourceId); + return banner; + } catch (NotFoundException e) { + Log.e(TAG, "Could not load banner."); + return null; + } catch (NameNotFoundException e) { + Log.e(TAG, "Could not load banner."); + return null; + } + } + + /** + * Load activity name for this service. + * @return activity name for this service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull + public String getSettingsActivityName() { return mSettingsActivityName; } + + @Override + public String toString() { + StringBuilder out = new StringBuilder("ApduService: "); + out.append(getComponent()); + out.append(", UID: " + mUid); + out.append(", description: " + mDescription); + out.append(", Static AID Groups: "); + for (AidGroup aidGroup : mStaticAidGroups.values()) { + out.append(aidGroup.toString()); + } + out.append(", Dynamic AID Groups: "); + for (AidGroup aidGroup : mDynamicAidGroups.values()) { + out.append(aidGroup.toString()); + } + return out.toString(); + } + + @Override + public boolean equals(@Nullable Object o) { + if (this == o) return true; + if (!(o instanceof ApduServiceInfo)) return false; + ApduServiceInfo thatService = (ApduServiceInfo) o; + + return thatService.getComponent().equals(this.getComponent()) + && thatService.getUid() == this.getUid(); + } + + @Override + public int hashCode() { + return getComponent().hashCode(); + } + + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @Override + public int describeContents() { + return 0; + } + + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + mService.writeToParcel(dest, flags); + dest.writeString(mDescription); + dest.writeInt(mOnHost ? 1 : 0); + dest.writeString(mOffHostName); + dest.writeString(mStaticOffHostName); + dest.writeInt(mStaticAidGroups.size()); + if (mStaticAidGroups.size() > 0) { + dest.writeTypedList(new ArrayList(mStaticAidGroups.values())); + } + dest.writeInt(mDynamicAidGroups.size()); + if (mDynamicAidGroups.size() > 0) { + dest.writeTypedList(new ArrayList(mDynamicAidGroups.values())); + } + dest.writeInt(mRequiresDeviceUnlock ? 1 : 0); + dest.writeInt(mRequiresDeviceScreenOn ? 1 : 0); + dest.writeInt(mBannerResourceId); + dest.writeInt(mUid); + dest.writeString(mSettingsActivityName); + + dest.writeInt(mCategoryOtherServiceEnabled ? 1 : 0); + dest.writeInt(mAutoTransact.size()); + dest.writeMap(mAutoTransact); + dest.writeInt(mAutoTransactPatterns.size()); + dest.writeMap(mAutoTransactPatterns); + }; + + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public static final @NonNull Parcelable.Creator CREATOR = + new Parcelable.Creator() { + @Override + public ApduServiceInfo createFromParcel(Parcel source) { + ResolveInfo info = ResolveInfo.CREATOR.createFromParcel(source); + String description = source.readString(); + boolean onHost = source.readInt() != 0; + String offHostName = source.readString(); + String staticOffHostName = source.readString(); + ArrayList staticAidGroups = new ArrayList(); + int numStaticGroups = source.readInt(); + if (numStaticGroups > 0) { + source.readTypedList(staticAidGroups, AidGroup.CREATOR); + } + ArrayList dynamicAidGroups = new ArrayList(); + int numDynamicGroups = source.readInt(); + if (numDynamicGroups > 0) { + source.readTypedList(dynamicAidGroups, AidGroup.CREATOR); + } + boolean requiresUnlock = source.readInt() != 0; + boolean requiresScreenOn = source.readInt() != 0; + int bannerResource = source.readInt(); + int uid = source.readInt(); + String settingsActivityName = source.readString(); + boolean isEnabled = source.readInt() != 0; + int autoTransactSize = source.readInt(); + HashMap autoTransact = + new HashMap(autoTransactSize); + source.readMap(autoTransact, getClass().getClassLoader(), + String.class, Boolean.class); + int autoTransactPatternSize = source.readInt(); + HashMap autoTransactPatterns = + new HashMap(autoTransactSize); + source.readMap(autoTransactPatterns, getClass().getClassLoader(), + Pattern.class, Boolean.class); + return new ApduServiceInfo(info, onHost, description, staticAidGroups, + dynamicAidGroups, requiresUnlock, requiresScreenOn, bannerResource, uid, + settingsActivityName, offHostName, staticOffHostName, + isEnabled, autoTransact, autoTransactPatterns); + } + + @Override + public ApduServiceInfo[] newArray(int size) { + return new ApduServiceInfo[size]; + } + }; + + /** + * Dump contents for debugging. + * @param fd parcelfiledescriptor instance + * @param pw printwriter instance + * @param args args for dumping + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public void dump(@NonNull ParcelFileDescriptor fd, @NonNull PrintWriter pw, + @NonNull String[] args) { + pw.println(" " + getComponent() + + " (Description: " + getDescription() + ")" + + " (UID: " + getUid() + ")"); + if (mOnHost) { + pw.println(" On Host Service"); + } else { + pw.println(" Off-host Service"); + pw.println(" " + "Current off-host SE:" + mOffHostName + + " static off-host SE:" + mStaticOffHostName); + } + pw.println(" Static AID groups:"); + for (AidGroup group : mStaticAidGroups.values()) { + pw.println(" Category: " + group.getCategory() + + "(enabled: " + mCategoryOtherServiceEnabled + ")"); + for (String aid : group.getAids()) { + pw.println(" AID: " + aid); + } + } + pw.println(" Dynamic AID groups:"); + for (AidGroup group : mDynamicAidGroups.values()) { + pw.println(" Category: " + group.getCategory() + + "(enabled: " + mCategoryOtherServiceEnabled + ")"); + for (String aid : group.getAids()) { + pw.println(" AID: " + aid); + } + } + pw.println(" Settings Activity: " + mSettingsActivityName); + pw.println(" Requires Device Unlock: " + mRequiresDeviceUnlock); + pw.println(" Requires Device ScreenOn: " + mRequiresDeviceScreenOn); + pw.println(" Should Default to Observe Mode: " + mShouldDefaultToObserveMode); + pw.println(" Auto-Transact Mapping: " + mAutoTransact); + pw.println(" Auto-Transact Patterns: " + mAutoTransactPatterns); + } + + + /** + * Enable or disable this CATEGORY_OTHER service. + * + * @param enabled true to indicate if user has enabled this service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public void setCategoryOtherServiceEnabled(boolean enabled) { + mCategoryOtherServiceEnabled = enabled; + } + + + /** + * Returns whether this CATEGORY_OTHER service is enabled or not. + * + * @return true to indicate if user has enabled this service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public boolean isCategoryOtherServiceEnabled() { + return mCategoryOtherServiceEnabled; + } + + /** + * Dump debugging info as ApduServiceInfoProto. + * + * If the output belongs to a sub message, the caller is responsible for wrapping this function + * between {@link ProtoOutputStream#start(long)} and {@link ProtoOutputStream#end(long)}. + * See proto definition in frameworks/base/core/proto/android/nfc/apdu_service_info.proto + * + * @param proto the ProtoOutputStream to write to + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public void dumpDebug(@NonNull ProtoOutputStream proto) { + getComponent().dumpDebug(proto, ApduServiceInfoProto.COMPONENT_NAME); + proto.write(ApduServiceInfoProto.DESCRIPTION, getDescription()); + proto.write(ApduServiceInfoProto.ON_HOST, mOnHost); + if (!mOnHost) { + proto.write(ApduServiceInfoProto.OFF_HOST_NAME, mOffHostName); + proto.write(ApduServiceInfoProto.STATIC_OFF_HOST_NAME, mStaticOffHostName); + } + for (AidGroup group : mStaticAidGroups.values()) { + long token = proto.start(ApduServiceInfoProto.STATIC_AID_GROUPS); + group.dump(proto); + proto.end(token); + } + for (AidGroup group : mDynamicAidGroups.values()) { + long token = proto.start(ApduServiceInfoProto.STATIC_AID_GROUPS); + group.dump(proto); + proto.end(token); + } + proto.write(ApduServiceInfoProto.SETTINGS_ACTIVITY_NAME, mSettingsActivityName); + proto.write(ApduServiceInfoProto.SHOULD_DEFAULT_TO_OBSERVE_MODE, + mShouldDefaultToObserveMode); + { + long token = proto.start(ApduServiceInfoProto.AUTO_TRANSACT_MAPPING); + for (Map.Entry entry : mAutoTransact.entrySet()) { + proto.write(ApduServiceInfoProto.AutoTransactMapping.AID, entry.getKey()); + proto.write(ApduServiceInfoProto.AutoTransactMapping.SHOULD_AUTO_TRANSACT, + entry.getValue()); + } + proto.end(token); + } + { + long token = proto.start(ApduServiceInfoProto.AUTO_TRANSACT_PATTERNS); + for (Map.Entry entry : mAutoTransactPatterns.entrySet()) { + proto.write(ApduServiceInfoProto.AutoTransactPattern.REGEXP_PATTERN, + entry.getKey().pattern()); + proto.write(ApduServiceInfoProto.AutoTransactPattern.SHOULD_AUTO_TRANSACT, + entry.getValue()); + } + proto.end(token); + } + } + + private static final Pattern AID_PATTERN = Pattern.compile("[0-9A-Fa-f]{10,32}\\*?\\#?"); + /** + * Copied over from {@link CardEmulation#isValidAid(String)} + * @hide + */ + private static boolean isValidAid(String aid) { + if (aid == null) + return false; + + // If a prefix/subset AID, the total length must be odd (even # of AID chars + '*') + if ((aid.endsWith("*") || aid.endsWith("#")) && ((aid.length() % 2) == 0)) { + Log.e(TAG, "AID " + aid + " is not a valid AID."); + return false; + } + + // If not a prefix/subset AID, the total length must be even (even # of AID chars) + if ((!(aid.endsWith("*") || aid.endsWith("#"))) && ((aid.length() % 2) != 0)) { + Log.e(TAG, "AID " + aid + " is not a valid AID."); + return false; + } + + // Verify hex characters + if (!AID_PATTERN.matcher(aid).matches()) { + Log.e(TAG, "AID " + aid + " is not a valid AID."); + return false; + } + + return true; + } +} diff --git a/nfc-non-updatable/java/android/nfc/cardemulation/NfcFServiceInfo.aidl b/nfc-non-updatable/java/android/nfc/cardemulation/NfcFServiceInfo.aidl new file mode 100644 index 000000000000..56b98ebd90fa --- /dev/null +++ b/nfc-non-updatable/java/android/nfc/cardemulation/NfcFServiceInfo.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.nfc.cardemulation; + +parcelable NfcFServiceInfo; diff --git a/nfc-non-updatable/java/android/nfc/cardemulation/NfcFServiceInfo.java b/nfc-non-updatable/java/android/nfc/cardemulation/NfcFServiceInfo.java new file mode 100644 index 000000000000..33bc16978721 --- /dev/null +++ b/nfc-non-updatable/java/android/nfc/cardemulation/NfcFServiceInfo.java @@ -0,0 +1,497 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/********************************************************************** + * This file is not a part of the NFC mainline module * + * *******************************************************************/ + +package android.nfc.cardemulation; + +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.content.ComponentName; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.content.res.XmlResourceParser; +import android.graphics.drawable.Drawable; +import android.nfc.Flags; +import android.os.Parcel; +import android.os.ParcelFileDescriptor; +import android.os.Parcelable; +import android.util.AttributeSet; +import android.util.Log; +import android.util.Xml; +import android.util.proto.ProtoOutputStream; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.io.PrintWriter; + +/** + * Class to hold NfcF service info. + * + * @hide + */ +@SystemApi +@FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) +public final class NfcFServiceInfo implements Parcelable { + static final String TAG = "NfcFServiceInfo"; + + private static final String DEFAULT_T3T_PMM = "FFFFFFFFFFFFFFFF"; + + /** + * The service that implements this + */ + private final ResolveInfo mService; + + /** + * Description of the service + */ + private final String mDescription; + + /** + * System Code of the service + */ + private final String mSystemCode; + + /** + * System Code of the service registered by API + */ + private String mDynamicSystemCode; + + /** + * NFCID2 of the service + */ + private final String mNfcid2; + + /** + * NFCID2 of the service registered by API + */ + private String mDynamicNfcid2; + + /** + * The uid of the package the service belongs to + */ + private final int mUid; + + /** + * LF_T3T_PMM of the service + */ + private final String mT3tPmm; + + /** + * @hide + */ + public NfcFServiceInfo(ResolveInfo info, String description, + String systemCode, String dynamicSystemCode, String nfcid2, String dynamicNfcid2, + int uid, String t3tPmm) { + this.mService = info; + this.mDescription = description; + this.mSystemCode = systemCode; + this.mDynamicSystemCode = dynamicSystemCode; + this.mNfcid2 = nfcid2; + this.mDynamicNfcid2 = dynamicNfcid2; + this.mUid = uid; + this.mT3tPmm = t3tPmm; + } + + /** + * Creates a new NfcFServiceInfo object. + * + * @param pm packageManager instance + * @param info app component info + * @throws XmlPullParserException If an error occurs parsing the element. + * @throws IOException If an error occurs reading the element. + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public NfcFServiceInfo(@NonNull PackageManager pm, @NonNull ResolveInfo info) + throws XmlPullParserException, IOException { + ServiceInfo si = info.serviceInfo; + XmlResourceParser parser = null; + try { + parser = si.loadXmlMetaData(pm, HostNfcFService.SERVICE_META_DATA); + if (parser == null) { + throw new XmlPullParserException("No " + HostNfcFService.SERVICE_META_DATA + + " meta-data"); + } + + int eventType = parser.getEventType(); + while (eventType != XmlPullParser.START_TAG && + eventType != XmlPullParser.END_DOCUMENT) { + eventType = parser.next(); + } + + String tagName = parser.getName(); + if (!"host-nfcf-service".equals(tagName)) { + throw new XmlPullParserException( + "Meta-data does not start with tag"); + } + + Resources res = pm.getResourcesForApplication(si.applicationInfo); + AttributeSet attrs = Xml.asAttributeSet(parser); + TypedArray sa = res.obtainAttributes(attrs, + com.android.internal.R.styleable.HostNfcFService); + mService = info; + mDescription = sa.getString( + com.android.internal.R.styleable.HostNfcFService_description); + mDynamicSystemCode = null; + mDynamicNfcid2 = null; + sa.recycle(); + + String systemCode = null; + String nfcid2 = null; + String t3tPmm = null; + final int depth = parser.getDepth(); + + while (((eventType = parser.next()) != XmlPullParser.END_TAG || + parser.getDepth() > depth) && eventType != XmlPullParser.END_DOCUMENT) { + tagName = parser.getName(); + if (eventType == XmlPullParser.START_TAG && + "system-code-filter".equals(tagName) && systemCode == null) { + final TypedArray a = res.obtainAttributes(attrs, + com.android.internal.R.styleable.SystemCodeFilter); + systemCode = a.getString( + com.android.internal.R.styleable.SystemCodeFilter_name).toUpperCase(); + if (!isValidSystemCode(systemCode) && + !systemCode.equalsIgnoreCase("NULL")) { + Log.e(TAG, "Invalid System Code: " + systemCode); + systemCode = null; + } + a.recycle(); + } else if (eventType == XmlPullParser.START_TAG && + "nfcid2-filter".equals(tagName) && nfcid2 == null) { + final TypedArray a = res.obtainAttributes(attrs, + com.android.internal.R.styleable.Nfcid2Filter); + nfcid2 = a.getString( + com.android.internal.R.styleable.Nfcid2Filter_name).toUpperCase(); + if (!nfcid2.equalsIgnoreCase("RANDOM") && + !nfcid2.equalsIgnoreCase("NULL") && + !isValidNfcid2(nfcid2)) { + Log.e(TAG, "Invalid NFCID2: " + nfcid2); + nfcid2 = null; + } + a.recycle(); + } else if (eventType == XmlPullParser.START_TAG && tagName.equals("t3tPmm-filter") + && t3tPmm == null) { + final TypedArray a = res.obtainAttributes(attrs, + com.android.internal.R.styleable.T3tPmmFilter); + t3tPmm = a.getString( + com.android.internal.R.styleable.T3tPmmFilter_name).toUpperCase(); + a.recycle(); + } + } + mSystemCode = (systemCode == null ? "NULL" : systemCode); + mNfcid2 = (nfcid2 == null ? "NULL" : nfcid2); + mT3tPmm = (t3tPmm == null ? DEFAULT_T3T_PMM : t3tPmm); + } catch (NameNotFoundException e) { + throw new XmlPullParserException("Unable to create context for: " + si.packageName); + } finally { + if (parser != null) parser.close(); + } + // Set uid + mUid = si.applicationInfo.uid; + } + + /** + * Returns the app component corresponding to this NFCF service. + * + * @return app component for this service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull + public ComponentName getComponent() { + return new ComponentName(mService.serviceInfo.packageName, + mService.serviceInfo.name); + } + + /** + * Returns the system code corresponding to this service. + * + * @return system code for this service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull + public String getSystemCode() { + return (mDynamicSystemCode == null ? mSystemCode : mDynamicSystemCode); + } + + /** + * Add or replace a system code to this service. + * @param systemCode system code to set or replace + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public void setDynamicSystemCode(@NonNull String systemCode) { + mDynamicSystemCode = systemCode; + } + + /** + * Returns NFC ID2. + * + * @return nfc id2 to return + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull + public String getNfcid2() { + return (mDynamicNfcid2 == null ? mNfcid2 : mDynamicNfcid2); + } + + /** + * Set or replace NFC ID2 + * + * @param nfcid2 NFC ID2 string + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public void setDynamicNfcid2(@NonNull String nfcid2) { + mDynamicNfcid2 = nfcid2; + } + + /** + * Returns description of service. + * @return user readable description of service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull + public String getDescription() { + return mDescription; + } + + /** + * Returns uid of service. + * @return uid of the service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public int getUid() { + return mUid; + } + + /** + * Returns LF_T3T_PMM of the service + * @return returns LF_T3T_PMM of the service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull + public String getT3tPmm() { + return mT3tPmm; + } + + /** + * Load application label for this service. + * @param pm packagemanager instance + * @return label name corresponding to service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull + public CharSequence loadLabel(@NonNull PackageManager pm) { + return mService.loadLabel(pm); + } + + /** + * Load application icon for this service. + * @param pm packagemanager instance + * @return app icon corresponding to service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull + public Drawable loadIcon(@NonNull PackageManager pm) { + return mService.loadIcon(pm); + } + + @Override + public String toString() { + StringBuilder out = new StringBuilder("NfcFService: "); + out.append(getComponent()); + out.append(", UID: " + mUid); + out.append(", description: " + mDescription); + out.append(", System Code: " + mSystemCode); + if (mDynamicSystemCode != null) { + out.append(", dynamic System Code: " + mDynamicSystemCode); + } + out.append(", NFCID2: " + mNfcid2); + if (mDynamicNfcid2 != null) { + out.append(", dynamic NFCID2: " + mDynamicNfcid2); + } + out.append(", T3T PMM:" + mT3tPmm); + return out.toString(); + } + + @Override + public boolean equals(@Nullable Object o) { + if (this == o) return true; + if (!(o instanceof NfcFServiceInfo)) return false; + NfcFServiceInfo thatService = (NfcFServiceInfo) o; + + if (!thatService.getComponent().equals(this.getComponent())) return false; + if (thatService.getUid() != this.getUid()) return false; + if (!thatService.mSystemCode.equalsIgnoreCase(this.mSystemCode)) return false; + if (!thatService.mNfcid2.equalsIgnoreCase(this.mNfcid2)) return false; + if (!thatService.mT3tPmm.equalsIgnoreCase(this.mT3tPmm)) return false; + return true; + } + + @Override + public int hashCode() { + return getComponent().hashCode(); + } + + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @Override + public int describeContents() { + return 0; + } + + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + mService.writeToParcel(dest, flags); + dest.writeString(mDescription); + dest.writeString(mSystemCode); + dest.writeInt(mDynamicSystemCode != null ? 1 : 0); + if (mDynamicSystemCode != null) { + dest.writeString(mDynamicSystemCode); + } + dest.writeString(mNfcid2); + dest.writeInt(mDynamicNfcid2 != null ? 1 : 0); + if (mDynamicNfcid2 != null) { + dest.writeString(mDynamicNfcid2); + } + dest.writeInt(mUid); + dest.writeString(mT3tPmm); + }; + + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public static final @NonNull Parcelable.Creator CREATOR = + new Parcelable.Creator() { + @Override + public NfcFServiceInfo createFromParcel(Parcel source) { + ResolveInfo info = ResolveInfo.CREATOR.createFromParcel(source); + String description = source.readString(); + String systemCode = source.readString(); + String dynamicSystemCode = null; + if (source.readInt() != 0) { + dynamicSystemCode = source.readString(); + } + String nfcid2 = source.readString(); + String dynamicNfcid2 = null; + if (source.readInt() != 0) { + dynamicNfcid2 = source.readString(); + } + int uid = source.readInt(); + String t3tPmm = source.readString(); + NfcFServiceInfo service = new NfcFServiceInfo(info, description, + systemCode, dynamicSystemCode, nfcid2, dynamicNfcid2, uid, t3tPmm); + return service; + } + + @Override + public NfcFServiceInfo[] newArray(int size) { + return new NfcFServiceInfo[size]; + } + }; + + /** + * Dump contents of the service for debugging. + * @param fd parcelfiledescriptor instance + * @param pw printwriter instance + * @param args args for dumping + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public void dump(@NonNull ParcelFileDescriptor fd, @NonNull PrintWriter pw, + @NonNull String[] args) { + pw.println(" " + getComponent() + + " (Description: " + getDescription() + ")" + + " (UID: " + getUid() + ")"); + pw.println(" System Code: " + getSystemCode()); + pw.println(" NFCID2: " + getNfcid2()); + pw.println(" T3tPmm: " + getT3tPmm()); + } + + /** + * Dump debugging info as NfcFServiceInfoProto. + * + * If the output belongs to a sub message, the caller is responsible for wrapping this function + * between {@link ProtoOutputStream#start(long)} and {@link ProtoOutputStream#end(long)}. + * + * @param proto the ProtoOutputStream to write to + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public void dumpDebug(@NonNull ProtoOutputStream proto) { + getComponent().dumpDebug(proto, NfcFServiceInfoProto.COMPONENT_NAME); + proto.write(NfcFServiceInfoProto.DESCRIPTION, getDescription()); + proto.write(NfcFServiceInfoProto.SYSTEM_CODE, getSystemCode()); + proto.write(NfcFServiceInfoProto.NFCID2, getNfcid2()); + proto.write(NfcFServiceInfoProto.T3T_PMM, getT3tPmm()); + } + + /** + * Copied over from {@link NfcFCardEmulation#isValidSystemCode(String)} + * @hide + */ + private static boolean isValidSystemCode(String systemCode) { + if (systemCode == null) { + return false; + } + if (systemCode.length() != 4) { + Log.e(TAG, "System Code " + systemCode + " is not a valid System Code."); + return false; + } + // check if the value is between "4000" and "4FFF" (excluding "4*FF") + if (!systemCode.startsWith("4") || systemCode.toUpperCase().endsWith("FF")) { + Log.e(TAG, "System Code " + systemCode + " is not a valid System Code."); + return false; + } + try { + Integer.parseInt(systemCode, 16); + } catch (NumberFormatException e) { + Log.e(TAG, "System Code " + systemCode + " is not a valid System Code."); + return false; + } + return true; + } + + /** + * Copied over from {@link NfcFCardEmulation#isValidNfcid2(String)} + * @hide + */ + private static boolean isValidNfcid2(String nfcid2) { + if (nfcid2 == null) { + return false; + } + if (nfcid2.length() != 16) { + Log.e(TAG, "NFCID2 " + nfcid2 + " is not a valid NFCID2."); + return false; + } + // check if the the value starts with "02FE" + if (!nfcid2.toUpperCase().startsWith("02FE")) { + Log.e(TAG, "NFCID2 " + nfcid2 + " is not a valid NFCID2."); + return false; + } + try { + Long.parseLong(nfcid2, 16); + } catch (NumberFormatException e) { + Log.e(TAG, "NFCID2 " + nfcid2 + " is not a valid NFCID2."); + return false; + } + return true; + } +} diff --git a/nfc/Android.bp b/nfc/Android.bp index 9490487cfdda..0fdb3bd38db8 100644 --- a/nfc/Android.bp +++ b/nfc/Android.bp @@ -8,20 +8,6 @@ package { default_applicable_licenses: ["frameworks_base_license"], } -filegroup { - name: "framework-nfc-non-updatable-sources", - path: "java", - srcs: [ - "java/android/nfc/NfcServiceManager.java", - "java/android/nfc/cardemulation/ApduServiceInfo.aidl", - "java/android/nfc/cardemulation/ApduServiceInfo.java", - "java/android/nfc/cardemulation/NfcFServiceInfo.aidl", - "java/android/nfc/cardemulation/NfcFServiceInfo.java", - "java/android/nfc/cardemulation/AidGroup.aidl", - "java/android/nfc/cardemulation/AidGroup.java", - ], -} - filegroup { name: "framework-nfc-updatable-sources", path: "java", @@ -34,9 +20,6 @@ filegroup { "//packages/apps/Nfc:__subpackages__", "//packages/modules/Nfc:__subpackages__", ], - exclude_srcs: [ - ":framework-nfc-non-updatable-sources", - ], } java_sdk_library { diff --git a/nfc/java/android/nfc/NfcServiceManager.java b/nfc/java/android/nfc/NfcServiceManager.java deleted file mode 100644 index 5582f1154cad..000000000000 --- a/nfc/java/android/nfc/NfcServiceManager.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -/********************************************************************** - * This file is not a part of the NFC mainline modure * - * *******************************************************************/ - -package android.nfc; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.SystemApi; -import android.annotation.SystemApi.Client; -import android.content.Context; -import android.os.IBinder; -import android.os.ServiceManager; - -/** - * Provides a way to register and obtain the system service binder objects managed by the nfc - * service. - * - * @hide - */ -@SystemApi(client = Client.MODULE_LIBRARIES) -public class NfcServiceManager { - - /** - * @hide - */ - public NfcServiceManager() { - } - - /** - * A class that exposes the methods to register and obtain each system service. - */ - public static final class ServiceRegisterer { - private final String mServiceName; - - /** - * @hide - */ - public ServiceRegisterer(String serviceName) { - mServiceName = serviceName; - } - - /** - * Register a system server binding object for a service. - */ - public void register(@NonNull IBinder service) { - ServiceManager.addService(mServiceName, service); - } - - /** - * Get the system server binding object for a service. - * - *

This blocks until the service instance is ready, - * or a timeout happens, in which case it returns null. - */ - @Nullable - public IBinder get() { - return ServiceManager.getService(mServiceName); - } - - /** - * Get the system server binding object for a service. - * - *

This blocks until the service instance is ready, - * or a timeout happens, in which case it throws {@link ServiceNotFoundException}. - */ - @NonNull - public IBinder getOrThrow() throws ServiceNotFoundException { - try { - return ServiceManager.getServiceOrThrow(mServiceName); - } catch (ServiceManager.ServiceNotFoundException e) { - throw new ServiceNotFoundException(mServiceName); - } - } - - /** - * Get the system server binding object for a service. If the specified service is - * not available, it returns null. - */ - @Nullable - public IBinder tryGet() { - return ServiceManager.checkService(mServiceName); - } - } - - /** - * See {@link ServiceRegisterer#getOrThrow}. - * - */ - public static class ServiceNotFoundException extends ServiceManager.ServiceNotFoundException { - /** - * Constructor. - * - * @param name the name of the binder service that cannot be found. - * - */ - public ServiceNotFoundException(@NonNull String name) { - super(name); - } - } - - /** - * Returns {@link ServiceRegisterer} for the "nfc" service. - */ - @NonNull - public ServiceRegisterer getNfcManagerServiceRegisterer() { - return new ServiceRegisterer(Context.NFC_SERVICE); - } -} diff --git a/nfc/java/android/nfc/cardemulation/AidGroup.aidl b/nfc/java/android/nfc/cardemulation/AidGroup.aidl deleted file mode 100644 index 56d6fa559677..000000000000 --- a/nfc/java/android/nfc/cardemulation/AidGroup.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.nfc.cardemulation; - -parcelable AidGroup; diff --git a/nfc/java/android/nfc/cardemulation/AidGroup.java b/nfc/java/android/nfc/cardemulation/AidGroup.java deleted file mode 100644 index ae3e333051d7..000000000000 --- a/nfc/java/android/nfc/cardemulation/AidGroup.java +++ /dev/null @@ -1,298 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.nfc.cardemulation; - -import android.annotation.FlaggedApi; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.SystemApi; -import android.nfc.Flags; -import android.os.Parcel; -import android.os.Parcelable; -import android.util.Log; -import android.util.proto.ProtoOutputStream; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; -import java.util.regex.Pattern; - -/********************************************************************** - * This file is not a part of the NFC mainline module * - * *******************************************************************/ - -/** - * The AidGroup class represents a group of Application Identifiers (AIDs). - * - *

The format of AIDs is defined in the ISO/IEC 7816-4 specification. This class - * requires the AIDs to be input as a hexadecimal string, with an even amount of - * hexadecimal characters, e.g. "F014811481". - * - * @hide - */ -@SystemApi -@FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) -public final class AidGroup implements Parcelable { - /** - * The maximum number of AIDs that can be present in any one group. - */ - private static final int MAX_NUM_AIDS = 256; - - private static final String TAG = "AidGroup"; - - - private final List mAids; - private final String mCategory; - @SuppressWarnings("unused") // Unused as of now, but part of the XML input. - private final String mDescription; - - /** - * Creates a new AidGroup object. - * - * @param aids list of AIDs present in the group - * @param category category of this group, e.g. {@link CardEmulation#CATEGORY_PAYMENT} - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - public AidGroup(@NonNull List aids, @Nullable String category) { - if (aids == null || aids.size() == 0) { - throw new IllegalArgumentException("No AIDS in AID group."); - } - if (aids.size() > MAX_NUM_AIDS) { - throw new IllegalArgumentException("Too many AIDs in AID group."); - } - for (String aid : aids) { - if (!isValidAid(aid)) { - throw new IllegalArgumentException("AID " + aid + " is not a valid AID."); - } - } - if (isValidCategory(category)) { - this.mCategory = category; - } else { - this.mCategory = CardEmulation.CATEGORY_OTHER; - } - this.mAids = new ArrayList(aids.size()); - for (String aid : aids) { - this.mAids.add(aid.toUpperCase(Locale.US)); - } - this.mDescription = null; - } - - /** - * Creates a new AidGroup object. - * - * @param category category of this group, e.g. {@link CardEmulation#CATEGORY_PAYMENT} - * @param description description of this group - */ - AidGroup(@NonNull String category, @NonNull String description) { - this.mAids = new ArrayList(); - this.mCategory = category; - this.mDescription = description; - } - - /** - * Returns the category of this group. - * @return the category of this AID group - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - @NonNull - public String getCategory() { - return mCategory; - } - - /** - * Returns the list of AIDs in this group. - * - * @return the list of AIDs in this group - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - @NonNull - public List getAids() { - return mAids; - } - - @Override - public String toString() { - StringBuilder out = new StringBuilder("Category: " + mCategory - + ", AIDs:"); - for (String aid : mAids) { - out.append(aid); - out.append(", "); - } - return out.toString(); - } - - /** - * Dump debugging info as AidGroupProto. - * - * If the output belongs to a sub message, the caller is responsible for wrapping this function - * between {@link ProtoOutputStream#start(long)} and {@link ProtoOutputStream#end(long)}. - * - * @param proto the ProtoOutputStream to write to - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - public void dump(@NonNull ProtoOutputStream proto) { - proto.write(AidGroupProto.CATEGORY, mCategory); - for (String aid : mAids) { - proto.write(AidGroupProto.AIDS, aid); - } - } - - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - @Override - public int describeContents() { - return 0; - } - - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeString8(mCategory); - dest.writeInt(mAids.size()); - if (mAids.size() > 0) { - dest.writeStringList(mAids); - } - } - - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - public static final @NonNull Parcelable.Creator CREATOR = - new Parcelable.Creator() { - - @Override - public AidGroup createFromParcel(Parcel source) { - String category = source.readString8(); - int listSize = source.readInt(); - ArrayList aidList = new ArrayList(); - if (listSize > 0) { - source.readStringList(aidList); - } - return new AidGroup(aidList, category); - } - - @Override - public AidGroup[] newArray(int size) { - return new AidGroup[size]; - } - }; - - /** - * Create an instance of AID group from XML file. - * - * @param parser input xml parser stream - * @throws XmlPullParserException If an error occurs parsing the element. - * @throws IOException If an error occurs reading the element. - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - @Nullable - public static AidGroup createFromXml(@NonNull XmlPullParser parser) - throws XmlPullParserException, IOException { - String category = null; - ArrayList aids = new ArrayList(); - AidGroup group = null; - boolean inGroup = false; - - int eventType = parser.getEventType(); - int minDepth = parser.getDepth(); - while (eventType != XmlPullParser.END_DOCUMENT && parser.getDepth() >= minDepth) { - String tagName = parser.getName(); - if (eventType == XmlPullParser.START_TAG) { - if (tagName.equals("aid")) { - if (inGroup) { - String aid = parser.getAttributeValue(null, "value"); - if (aid != null) { - aids.add(aid.toUpperCase()); - } - } else { - Log.d(TAG, "Ignoring tag while not in group"); - } - } else if (tagName.equals("aid-group")) { - category = parser.getAttributeValue(null, "category"); - if (category == null) { - Log.e(TAG, " tag without valid category"); - return null; - } - inGroup = true; - } else { - Log.d(TAG, "Ignoring unexpected tag: " + tagName); - } - } else if (eventType == XmlPullParser.END_TAG) { - if (tagName.equals("aid-group") && inGroup && aids.size() > 0) { - group = new AidGroup(aids, category); - break; - } - } - eventType = parser.next(); - } - return group; - } - - /** - * Serialize instance of AID group to XML file. - * @param out XML serializer stream - * @throws IOException If an error occurs reading the element. - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - public void writeAsXml(@NonNull XmlSerializer out) throws IOException { - out.startTag(null, "aid-group"); - out.attribute(null, "category", mCategory); - for (String aid : mAids) { - out.startTag(null, "aid"); - out.attribute(null, "value", aid); - out.endTag(null, "aid"); - } - out.endTag(null, "aid-group"); - } - - private static boolean isValidCategory(String category) { - return CardEmulation.CATEGORY_PAYMENT.equals(category) || - CardEmulation.CATEGORY_OTHER.equals(category); - } - - private static final Pattern AID_PATTERN = Pattern.compile("[0-9A-Fa-f]{10,32}\\*?\\#?"); - /** - * Copied over from {@link CardEmulation#isValidAid(String)} - * @hide - */ - private static boolean isValidAid(String aid) { - if (aid == null) - return false; - - // If a prefix/subset AID, the total length must be odd (even # of AID chars + '*') - if ((aid.endsWith("*") || aid.endsWith("#")) && ((aid.length() % 2) == 0)) { - Log.e(TAG, "AID " + aid + " is not a valid AID."); - return false; - } - - // If not a prefix/subset AID, the total length must be even (even # of AID chars) - if ((!(aid.endsWith("*") || aid.endsWith("#"))) && ((aid.length() % 2) != 0)) { - Log.e(TAG, "AID " + aid + " is not a valid AID."); - return false; - } - - // Verify hex characters - if (!AID_PATTERN.matcher(aid).matches()) { - Log.e(TAG, "AID " + aid + " is not a valid AID."); - return false; - } - - return true; - } -} diff --git a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.aidl b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.aidl deleted file mode 100644 index a62fdd6a6c5c..000000000000 --- a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.nfc.cardemulation; - -parcelable ApduServiceInfo; diff --git a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java deleted file mode 100644 index 7f64dbea0be3..000000000000 --- a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java +++ /dev/null @@ -1,1195 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/********************************************************************** - * This file is not a part of the NFC mainline module * - * *******************************************************************/ - -package android.nfc.cardemulation; - -import android.annotation.FlaggedApi; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.SystemApi; -import android.compat.annotation.UnsupportedAppUsage; -import android.content.ComponentName; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.content.pm.ResolveInfo; -import android.content.pm.ServiceInfo; -import android.content.res.Resources; -import android.content.res.Resources.NotFoundException; -import android.content.res.TypedArray; -import android.content.res.XmlResourceParser; -import android.graphics.drawable.Drawable; -import android.nfc.Flags; -import android.os.Parcel; -import android.os.ParcelFileDescriptor; -import android.os.Parcelable; -import android.util.AttributeSet; -import android.util.Log; -import android.util.Xml; -import android.util.proto.ProtoOutputStream; - -import com.android.internal.R; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; - -import java.io.IOException; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.TreeMap; -import java.util.regex.Pattern; - -/** - * Class holding APDU service info. - * - * @hide - */ -@SystemApi -@FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) -public final class ApduServiceInfo implements Parcelable { - private static final String TAG = "ApduServiceInfo"; - - /** - * Component level {@link android.content.pm.PackageManager.Property PackageManager - * .Property} for a system application to change its icon and label - * on the default applications page. This property should be added to an - * {@link HostApduService} declaration in the manifest. - * - *

For example: - *

-     * <service
-     *     android:apduServiceBanner="@drawable/product_logo"
-     *     android:label="@string/service_label">
-     *      <property
-     *          android:name="android.content.PROPERTY_WALLET_ICON_AND_LABEL_HOLDER"
-     *          android:value="true"/>
-     * </service>
-     * 
- * @hide - */ - @SystemApi - @FlaggedApi(android.permission.flags.Flags.FLAG_WALLET_ROLE_ICON_PROPERTY_ENABLED) - public static final String PROPERTY_WALLET_PREFERRED_BANNER_AND_LABEL = - "android.nfc.cardemulation.PROPERTY_WALLET_PREFERRED_BANNER_AND_LABEL"; - - /** - * The service that implements this - */ - private final ResolveInfo mService; - - /** - * Description of the service - */ - private final String mDescription; - - /** - * Whether this service represents AIDs running on the host CPU - */ - private final boolean mOnHost; - - /** - * Offhost reader name. - * eg: SIM, eSE etc - */ - private String mOffHostName; - - /** - * Offhost reader name from manifest file. - * Used for resetOffHostSecureElement() - */ - private final String mStaticOffHostName; - - /** - * Mapping from category to static AID group - */ - private final HashMap mStaticAidGroups; - - /** - * Mapping from category to dynamic AID group - */ - private final HashMap mDynamicAidGroups; - - - private final Map mAutoTransact; - - private final Map mAutoTransactPatterns; - - /** - * Whether this service should only be started when the device is unlocked. - */ - private final boolean mRequiresDeviceUnlock; - - /** - * Whether this service should only be started when the device is screen on. - */ - private final boolean mRequiresDeviceScreenOn; - - /** - * The id of the service banner specified in XML. - */ - private final int mBannerResourceId; - - /** - * The uid of the package the service belongs to - */ - private final int mUid; - - /** - * Settings Activity for this service - */ - private final String mSettingsActivityName; - - /** - * State of the service for CATEGORY_OTHER selection - */ - private boolean mCategoryOtherServiceEnabled; - - /** - * Whether the NFC stack should default to Observe Mode when this preferred service. - */ - private boolean mShouldDefaultToObserveMode; - - /** - * Whether or not this service wants to share the same routing priority as the - * Wallet role owner. - */ - private boolean mWantsRoleHolderPriority; - - /** - * @hide - */ - @UnsupportedAppUsage - public ApduServiceInfo(ResolveInfo info, boolean onHost, String description, - ArrayList staticAidGroups, ArrayList dynamicAidGroups, - boolean requiresUnlock, int bannerResource, int uid, - String settingsActivityName, String offHost, String staticOffHost) { - this(info, onHost, description, staticAidGroups, dynamicAidGroups, - requiresUnlock, bannerResource, uid, settingsActivityName, - offHost, staticOffHost, false); - } - - /** - * @hide - */ - public ApduServiceInfo(ResolveInfo info, boolean onHost, String description, - ArrayList staticAidGroups, ArrayList dynamicAidGroups, - boolean requiresUnlock, int bannerResource, int uid, - String settingsActivityName, String offHost, String staticOffHost, - boolean isEnabled) { - this(info, onHost, description, staticAidGroups, dynamicAidGroups, - requiresUnlock, onHost ? true : false, bannerResource, uid, - settingsActivityName, offHost, staticOffHost, isEnabled); - } - - /** - * @hide - */ - public ApduServiceInfo(ResolveInfo info, boolean onHost, String description, - List staticAidGroups, List 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(), new TreeMap<>( - Comparator.comparing(Pattern::toString))); - } - - /** - * @hide - */ - public ApduServiceInfo(ResolveInfo info, boolean onHost, String description, - List staticAidGroups, List dynamicAidGroups, - boolean requiresUnlock, boolean requiresScreenOn, int bannerResource, int uid, - String settingsActivityName, String offHost, String staticOffHost, boolean isEnabled, - Map autoTransact, Map autoTransactPatterns) { - this.mService = info; - this.mDescription = description; - this.mStaticAidGroups = new HashMap(); - this.mDynamicAidGroups = new HashMap(); - this.mAutoTransact = autoTransact; - this.mAutoTransactPatterns = autoTransactPatterns; - this.mOffHostName = offHost; - this.mStaticOffHostName = staticOffHost; - this.mOnHost = onHost; - this.mRequiresDeviceUnlock = requiresUnlock; - this.mRequiresDeviceScreenOn = requiresScreenOn; - for (AidGroup aidGroup : staticAidGroups) { - this.mStaticAidGroups.put(aidGroup.getCategory(), aidGroup); - } - for (AidGroup aidGroup : dynamicAidGroups) { - this.mDynamicAidGroups.put(aidGroup.getCategory(), aidGroup); - } - this.mBannerResourceId = bannerResource; - this.mUid = uid; - this.mSettingsActivityName = settingsActivityName; - this.mCategoryOtherServiceEnabled = isEnabled; - } - - /** - * Creates a new ApduServiceInfo object. - * - * @param pm packageManager instance - * @param info app component info - * @param onHost whether service is on host or not (secure element) - * @throws XmlPullParserException If an error occurs parsing the element. - * @throws IOException If an error occurs reading the element. - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - public ApduServiceInfo(@NonNull PackageManager pm, @NonNull ResolveInfo info, boolean onHost) - throws XmlPullParserException, IOException { - ServiceInfo si = info.serviceInfo; - XmlResourceParser parser = null; - try { - if (onHost) { - parser = si.loadXmlMetaData(pm, HostApduService.SERVICE_META_DATA); - if (parser == null) { - throw new XmlPullParserException("No " + HostApduService.SERVICE_META_DATA + - " meta-data"); - } - } else { - parser = si.loadXmlMetaData(pm, OffHostApduService.SERVICE_META_DATA); - if (parser == null) { - throw new XmlPullParserException("No " + OffHostApduService.SERVICE_META_DATA + - " meta-data"); - } - } - - int eventType = parser.getEventType(); - while (eventType != XmlPullParser.START_TAG && eventType != XmlPullParser.END_DOCUMENT) { - eventType = parser.next(); - } - - String tagName = parser.getName(); - if (onHost && !"host-apdu-service".equals(tagName)) { - throw new XmlPullParserException( - "Meta-data does not start with tag"); - } else if (!onHost && !"offhost-apdu-service".equals(tagName)) { - throw new XmlPullParserException( - "Meta-data does not start with tag"); - } - - Resources res = pm.getResourcesForApplication(si.applicationInfo); - AttributeSet attrs = Xml.asAttributeSet(parser); - if (onHost) { - TypedArray sa = res.obtainAttributes(attrs, - com.android.internal.R.styleable.HostApduService); - mService = info; - mDescription = sa.getString( - com.android.internal.R.styleable.HostApduService_description); - mRequiresDeviceUnlock = sa.getBoolean( - com.android.internal.R.styleable.HostApduService_requireDeviceUnlock, - false); - mRequiresDeviceScreenOn = sa.getBoolean( - com.android.internal.R.styleable.HostApduService_requireDeviceScreenOn, - true); - mBannerResourceId = sa.getResourceId( - com.android.internal.R.styleable.HostApduService_apduServiceBanner, -1); - mSettingsActivityName = sa.getString( - com.android.internal.R.styleable.HostApduService_settingsActivity); - mOffHostName = null; - mStaticOffHostName = mOffHostName; - mShouldDefaultToObserveMode = sa.getBoolean( - R.styleable.HostApduService_shouldDefaultToObserveMode, - false); - if (Flags.nfcAssociatedRoleServices()) { - mWantsRoleHolderPriority = sa.getBoolean( - R.styleable.HostApduService_wantsRoleHolderPriority, - false - ); - } - sa.recycle(); - } else { - TypedArray sa = res.obtainAttributes(attrs, - com.android.internal.R.styleable.OffHostApduService); - mService = info; - mDescription = sa.getString( - com.android.internal.R.styleable.OffHostApduService_description); - mRequiresDeviceUnlock = sa.getBoolean( - com.android.internal.R.styleable.OffHostApduService_requireDeviceUnlock, - false); - mRequiresDeviceScreenOn = sa.getBoolean( - com.android.internal.R.styleable.OffHostApduService_requireDeviceScreenOn, - false); - mBannerResourceId = sa.getResourceId( - com.android.internal.R.styleable.OffHostApduService_apduServiceBanner, -1); - mSettingsActivityName = sa.getString( - com.android.internal.R.styleable.HostApduService_settingsActivity); - mOffHostName = sa.getString( - com.android.internal.R.styleable.OffHostApduService_secureElementName); - mShouldDefaultToObserveMode = sa.getBoolean( - R.styleable.OffHostApduService_shouldDefaultToObserveMode, - false); - if (mOffHostName != null) { - if (mOffHostName.equals("eSE")) { - mOffHostName = "eSE1"; - } else if (mOffHostName.equals("SIM")) { - mOffHostName = "SIM1"; - } - } - mStaticOffHostName = mOffHostName; - if (Flags.nfcAssociatedRoleServices()) { - mWantsRoleHolderPriority = sa.getBoolean( - R.styleable.OffHostApduService_wantsRoleHolderPriority, - false - ); - } - sa.recycle(); - } - - mStaticAidGroups = new HashMap(); - mDynamicAidGroups = new HashMap(); - mAutoTransact = new HashMap(); - mAutoTransactPatterns = new TreeMap( - Comparator.comparing(Pattern::toString)); - mOnHost = onHost; - - final int depth = parser.getDepth(); - AidGroup currentGroup = null; - - // Parsed values for the current AID group - while (((eventType = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth) - && eventType != XmlPullParser.END_DOCUMENT) { - tagName = parser.getName(); - if (eventType == XmlPullParser.START_TAG && "aid-group".equals(tagName) && - currentGroup == null) { - final TypedArray groupAttrs = res.obtainAttributes(attrs, - com.android.internal.R.styleable.AidGroup); - // Get category of AID group - String groupCategory = groupAttrs.getString( - com.android.internal.R.styleable.AidGroup_category); - String groupDescription = groupAttrs.getString( - com.android.internal.R.styleable.AidGroup_description); - if (!CardEmulation.CATEGORY_PAYMENT.equals(groupCategory)) { - groupCategory = CardEmulation.CATEGORY_OTHER; - } - currentGroup = mStaticAidGroups.get(groupCategory); - if (currentGroup != null) { - if (!CardEmulation.CATEGORY_OTHER.equals(groupCategory)) { - Log.e(TAG, "Not allowing multiple aid-groups in the " + - groupCategory + " category"); - currentGroup = null; - } - } else { - currentGroup = new AidGroup(groupCategory, groupDescription); - } - groupAttrs.recycle(); - } else if (eventType == XmlPullParser.END_TAG && "aid-group".equals(tagName) && - currentGroup != null) { - if (currentGroup.getAids().size() > 0) { - if (!mStaticAidGroups.containsKey(currentGroup.getCategory())) { - mStaticAidGroups.put(currentGroup.getCategory(), currentGroup); - } - } else { - Log.e(TAG, "Not adding with empty or invalid AIDs"); - } - currentGroup = null; - } else if (eventType == XmlPullParser.START_TAG && "aid-filter".equals(tagName) && - currentGroup != null) { - final TypedArray a = res.obtainAttributes(attrs, - com.android.internal.R.styleable.AidFilter); - String aid = a.getString(com.android.internal.R.styleable.AidFilter_name). - toUpperCase(); - if (isValidAid(aid) && !currentGroup.getAids().contains(aid)) { - currentGroup.getAids().add(aid); - } else { - Log.e(TAG, "Ignoring invalid or duplicate aid: " + aid); - } - a.recycle(); - } else if (eventType == XmlPullParser.START_TAG && - "aid-prefix-filter".equals(tagName) && currentGroup != null) { - final TypedArray a = res.obtainAttributes(attrs, - com.android.internal.R.styleable.AidFilter); - String aid = a.getString(com.android.internal.R.styleable.AidFilter_name). - toUpperCase(); - // Add wildcard char to indicate prefix - aid = aid.concat("*"); - if (isValidAid(aid) && !currentGroup.getAids().contains(aid)) { - currentGroup.getAids().add(aid); - } else { - Log.e(TAG, "Ignoring invalid or duplicate aid: " + aid); - } - a.recycle(); - } else if (eventType == XmlPullParser.START_TAG && - tagName.equals("aid-suffix-filter") && currentGroup != null) { - final TypedArray a = res.obtainAttributes(attrs, - com.android.internal.R.styleable.AidFilter); - String aid = a.getString(com.android.internal.R.styleable.AidFilter_name). - toUpperCase(); - // Add wildcard char to indicate suffix - aid = aid.concat("#"); - if (isValidAid(aid) && !currentGroup.getAids().contains(aid)) { - currentGroup.getAids().add(aid); - } else { - Log.e(TAG, "Ignoring invalid or duplicate aid: " + aid); - } - a.recycle(); - } 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); - if (!mOnHost && !autoTransact) { - Log.e(TAG, "Ignoring polling-loop-filter " + plf - + " for offhost service that isn't autoTransact"); - } else { - mAutoTransact.put(plf, autoTransact); - } - a.recycle(); - } else if (eventType == XmlPullParser.START_TAG - && "polling-loop-pattern-filter".equals(tagName) && currentGroup == null) { - final TypedArray a = res.obtainAttributes(attrs, - com.android.internal.R.styleable.PollingLoopPatternFilter); - String plf = a.getString( - com.android.internal.R.styleable.PollingLoopPatternFilter_name) - .toUpperCase(Locale.ROOT); - boolean autoTransact = a.getBoolean( - com.android.internal.R.styleable.PollingLoopFilter_autoTransact, - false); - if (!mOnHost && !autoTransact) { - Log.e(TAG, "Ignoring polling-loop-filter " + plf - + " for offhost service that isn't autoTransact"); - } else { - mAutoTransactPatterns.put(Pattern.compile(plf), autoTransact); - } - a.recycle(); - } - } - } catch (NameNotFoundException e) { - throw new XmlPullParserException("Unable to create context for: " + si.packageName); - } finally { - if (parser != null) parser.close(); - } - // Set uid - mUid = si.applicationInfo.uid; - - mCategoryOtherServiceEnabled = true; // support other category - - } - - /** - * Returns the app component corresponding to this APDU service. - * - * @return app component for this service - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - @NonNull - public ComponentName getComponent() { - return new ComponentName(mService.serviceInfo.packageName, - mService.serviceInfo.name); - } - - /** - * Returns the offhost secure element name (if the service is offhost). - * - * @return offhost secure element name for offhost services - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - @Nullable - public String getOffHostSecureElement() { - return mOffHostName; - } - - /** - * Returns a consolidated list of AIDs from the AID groups - * registered by this service. Note that if a service has both - * a static (manifest-based) AID group for a category and a dynamic - * AID group, only the dynamically registered AIDs will be returned - * for that category. - * @return List of AIDs registered by the service - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - @NonNull - public List getAids() { - final ArrayList aids = new ArrayList(); - for (AidGroup group : getAidGroups()) { - aids.addAll(group.getAids()); - } - return aids; - } - - /** - * 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 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) { - if (mAutoTransact.getOrDefault(plf.toUpperCase(Locale.ROOT), false)) { - return true; - } - List patternMatches = mAutoTransactPatterns.keySet().stream() - .filter(p -> p.matcher(plf).matches()).toList(); - if (patternMatches == null || patternMatches.size() == 0) { - return false; - } - for (Pattern patternMatch : patternMatches) { - if (mAutoTransactPatterns.get(patternMatch)) { - return true; - } - } - return false; - } - - /** - * Returns the current polling loop pattern filters for this service. - * @return List of polling loop pattern filters. - */ - @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP) - @NonNull - public List getPollingLoopPatternFilters() { - return new ArrayList<>(mAutoTransactPatterns.keySet()); - } - - /** - * Returns a consolidated list of AIDs with prefixes from the AID groups - * registered by this service. Note that if a service has both - * a static (manifest-based) AID group for a category and a dynamic - * AID group, only the dynamically registered AIDs will be returned - * for that category. - * @return List of prefix AIDs registered by the service - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - @NonNull - public List getPrefixAids() { - final ArrayList prefixAids = new ArrayList(); - for (AidGroup group : getAidGroups()) { - for (String aid : group.getAids()) { - if (aid.endsWith("*")) { - prefixAids.add(aid); - } - } - } - return prefixAids; - } - - /** - * Returns a consolidated list of AIDs with subsets from the AID groups - * registered by this service. Note that if a service has both - * a static (manifest-based) AID group for a category and a dynamic - * AID group, only the dynamically registered AIDs will be returned - * for that category. - * @return List of prefix AIDs registered by the service - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - @NonNull - public List getSubsetAids() { - final ArrayList subsetAids = new ArrayList(); - for (AidGroup group : getAidGroups()) { - for (String aid : group.getAids()) { - if (aid.endsWith("#")) { - subsetAids.add(aid); - } - } - } - return subsetAids; - } - - /** - * Returns the registered AID group for this category. - * - * @param category category name - * @return {@link AidGroup} instance for the provided category - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - @NonNull - public AidGroup getDynamicAidGroupForCategory(@NonNull String category) { - return mDynamicAidGroups.get(category); - } - - /** - * Removes the registered AID group for this category. - * - * @param category category name - * @return {@code true} if an AID group existed - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - @NonNull - public boolean removeDynamicAidGroupForCategory(@NonNull String category) { - return (mDynamicAidGroups.remove(category) != null); - } - - /** - * Returns a consolidated list of AID groups - * registered by this service. Note that if a service has both - * a static (manifest-based) AID group for a category and a dynamic - * AID group, only the dynamically registered AID group will be returned - * for that category. - * @return List of AIDs registered by the service - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - @NonNull - public List getAidGroups() { - final ArrayList groups = new ArrayList(); - for (Map.Entry entry : mDynamicAidGroups.entrySet()) { - groups.add(entry.getValue()); - } - for (Map.Entry entry : mStaticAidGroups.entrySet()) { - if (!mDynamicAidGroups.containsKey(entry.getKey())) { - // Consolidate AID groups - don't return static ones - // if a dynamic group exists for the category. - groups.add(entry.getValue()); - } - } - return groups; - } - - /** - * Returns the category to which this service has attributed the AID that is passed in, - * or null if we don't know this AID. - * @param aid AID to lookup for - * @return category name corresponding to this AID - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - @NonNull - public String getCategoryForAid(@NonNull String aid) { - List groups = getAidGroups(); - for (AidGroup group : groups) { - if (group.getAids().contains(aid.toUpperCase())) { - return group.getCategory(); - } - } - return null; - } - - /** - * Returns whether there is any AID group for this category. - * @param category category name - * @return {@code true} if an AID group exists - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - public boolean hasCategory(@NonNull String category) { - return (mStaticAidGroups.containsKey(category) || mDynamicAidGroups.containsKey(category)); - } - - /** - * Returns whether the service is on host or not. - * @return true if the service is on host (not secure element) - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - public boolean isOnHost() { - return mOnHost; - } - - /** - * Returns whether the service requires device unlock. - * @return whether the service requires device unlock - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - public boolean requiresUnlock() { - return mRequiresDeviceUnlock; - } - - /** - * Returns whether this service should only be started when the device is screen on. - * @return whether the service requires screen on - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - public boolean requiresScreenOn() { - return mRequiresDeviceScreenOn; - } - - /** - * Returns 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 whether or not this service wants to share the Wallet role holder priority - * with other packages/services with the same signature. - * - * @return whether or not this service wants to share priority - */ - @FlaggedApi(Flags.FLAG_NFC_ASSOCIATED_ROLE_SERVICES) - public boolean wantsRoleHolderPriority() { - return mWantsRoleHolderPriority; - } - - /** - * Returns description of service. - * @return user readable description of service - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - @NonNull - public String getDescription() { - return mDescription; - } - - /** - * Returns uid of service. - * @return uid of the service - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - public int getUid() { - return mUid; - } - - /** - * Add or replace an AID group to this service. - * @param aidGroup instance of aid group to set or replace - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - public void setDynamicAidGroup(@NonNull AidGroup aidGroup) { - mDynamicAidGroups.put(aidGroup.getCategory(), aidGroup); - } - - /** - * 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 valid hexadecimal string - * @param autoTransact when true, disable observe mode when this filter matches, when false, - * matching does not change the observe mode state - */ - @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP) - public void addPollingLoopFilter(@NonNull String pollingLoopFilter, - boolean autoTransact) { - if (!mOnHost && !autoTransact) { - return; - } - 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)); - } - - /** - * Add a Polling Loop Pattern 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 pollingLoopPatternFilter the polling loop pattern filter to add, must be a valid - * regex to match a hexadecimal string - * @param autoTransact when true, disable observe mode when this filter matches, when false, - * matching does not change the observe mode state - */ - @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP) - public void addPollingLoopPatternFilter(@NonNull String pollingLoopPatternFilter, - boolean autoTransact) { - if (!mOnHost && !autoTransact) { - return; - } - mAutoTransactPatterns.put(Pattern.compile(pollingLoopPatternFilter), autoTransact); - } - - /** - * Remove a Polling Loop Pattern Filter. Custom NFC polling frames that match this filter will - * no longer be delivered to {@link HostApduService#processPollingFrames(List)}. - * @param pollingLoopPatternFilter this polling loop filter to add. - */ - @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP) - public void removePollingLoopPatternFilter(@NonNull String pollingLoopPatternFilter) { - mAutoTransactPatterns.remove( - Pattern.compile(pollingLoopPatternFilter.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 - * TS26_NFC_REQ_069: For UICC, Secure Element Name SHALL be SIM[smartcard slot] - * (e.g. SIM/SIM1, SIM2… SIMn). - * TS26_NFC_REQ_070: For embedded SE, Secure Element Name SHALL be eSE[number] - * (e.g. eSE/eSE1, eSE2, etc.). - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - public void setOffHostSecureElement(@NonNull String offHost) { - mOffHostName = offHost; - } - - /** - * Resets the off host Secure Element to statically defined - * by the service in the manifest file. - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - public void resetOffHostSecureElement() { - mOffHostName = mStaticOffHostName; - } - - /** - * Load label for this service. - * @param pm packagemanager instance - * @return label name corresponding to service - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - @NonNull - public CharSequence loadLabel(@NonNull PackageManager pm) { - return mService.loadLabel(pm); - } - - /** - * Load application label for this service. - * @param pm packagemanager instance - * @return app label name corresponding to service - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - @NonNull - public CharSequence loadAppLabel(@NonNull PackageManager pm) { - try { - return pm.getApplicationLabel(pm.getApplicationInfo( - mService.resolvePackageName, PackageManager.GET_META_DATA)); - } catch (PackageManager.NameNotFoundException e) { - return null; - } - } - - /** - * Load application icon for this service. - * @param pm packagemanager instance - * @return app icon corresponding to service - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - @NonNull - public Drawable loadIcon(@NonNull PackageManager pm) { - return mService.loadIcon(pm); - } - - /** - * Load application banner for this service. - * @param pm packagemanager instance - * @return app banner corresponding to service - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - @NonNull - public Drawable loadBanner(@NonNull PackageManager pm) { - Resources res; - try { - res = pm.getResourcesForApplication(mService.serviceInfo.packageName); - Drawable banner = res.getDrawable(mBannerResourceId); - return banner; - } catch (NotFoundException e) { - Log.e(TAG, "Could not load banner."); - return null; - } catch (NameNotFoundException e) { - Log.e(TAG, "Could not load banner."); - return null; - } - } - - /** - * Load activity name for this service. - * @return activity name for this service - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - @NonNull - public String getSettingsActivityName() { return mSettingsActivityName; } - - @Override - public String toString() { - StringBuilder out = new StringBuilder("ApduService: "); - out.append(getComponent()); - out.append(", UID: " + mUid); - out.append(", description: " + mDescription); - out.append(", Static AID Groups: "); - for (AidGroup aidGroup : mStaticAidGroups.values()) { - out.append(aidGroup.toString()); - } - out.append(", Dynamic AID Groups: "); - for (AidGroup aidGroup : mDynamicAidGroups.values()) { - out.append(aidGroup.toString()); - } - return out.toString(); - } - - @Override - public boolean equals(@Nullable Object o) { - if (this == o) return true; - if (!(o instanceof ApduServiceInfo)) return false; - ApduServiceInfo thatService = (ApduServiceInfo) o; - - return thatService.getComponent().equals(this.getComponent()) - && thatService.getUid() == this.getUid(); - } - - @Override - public int hashCode() { - return getComponent().hashCode(); - } - - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - @Override - public int describeContents() { - return 0; - } - - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - mService.writeToParcel(dest, flags); - dest.writeString(mDescription); - dest.writeInt(mOnHost ? 1 : 0); - dest.writeString(mOffHostName); - dest.writeString(mStaticOffHostName); - dest.writeInt(mStaticAidGroups.size()); - if (mStaticAidGroups.size() > 0) { - dest.writeTypedList(new ArrayList(mStaticAidGroups.values())); - } - dest.writeInt(mDynamicAidGroups.size()); - if (mDynamicAidGroups.size() > 0) { - dest.writeTypedList(new ArrayList(mDynamicAidGroups.values())); - } - dest.writeInt(mRequiresDeviceUnlock ? 1 : 0); - dest.writeInt(mRequiresDeviceScreenOn ? 1 : 0); - dest.writeInt(mBannerResourceId); - dest.writeInt(mUid); - dest.writeString(mSettingsActivityName); - - dest.writeInt(mCategoryOtherServiceEnabled ? 1 : 0); - dest.writeInt(mAutoTransact.size()); - dest.writeMap(mAutoTransact); - dest.writeInt(mAutoTransactPatterns.size()); - dest.writeMap(mAutoTransactPatterns); - }; - - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - public static final @NonNull Parcelable.Creator CREATOR = - new Parcelable.Creator() { - @Override - public ApduServiceInfo createFromParcel(Parcel source) { - ResolveInfo info = ResolveInfo.CREATOR.createFromParcel(source); - String description = source.readString(); - boolean onHost = source.readInt() != 0; - String offHostName = source.readString(); - String staticOffHostName = source.readString(); - ArrayList staticAidGroups = new ArrayList(); - int numStaticGroups = source.readInt(); - if (numStaticGroups > 0) { - source.readTypedList(staticAidGroups, AidGroup.CREATOR); - } - ArrayList dynamicAidGroups = new ArrayList(); - int numDynamicGroups = source.readInt(); - if (numDynamicGroups > 0) { - source.readTypedList(dynamicAidGroups, AidGroup.CREATOR); - } - boolean requiresUnlock = source.readInt() != 0; - boolean requiresScreenOn = source.readInt() != 0; - int bannerResource = source.readInt(); - int uid = source.readInt(); - String settingsActivityName = source.readString(); - boolean isEnabled = source.readInt() != 0; - int autoTransactSize = source.readInt(); - HashMap autoTransact = - new HashMap(autoTransactSize); - source.readMap(autoTransact, getClass().getClassLoader(), - String.class, Boolean.class); - int autoTransactPatternSize = source.readInt(); - HashMap autoTransactPatterns = - new HashMap(autoTransactSize); - source.readMap(autoTransactPatterns, getClass().getClassLoader(), - Pattern.class, Boolean.class); - return new ApduServiceInfo(info, onHost, description, staticAidGroups, - dynamicAidGroups, requiresUnlock, requiresScreenOn, bannerResource, uid, - settingsActivityName, offHostName, staticOffHostName, - isEnabled, autoTransact, autoTransactPatterns); - } - - @Override - public ApduServiceInfo[] newArray(int size) { - return new ApduServiceInfo[size]; - } - }; - - /** - * Dump contents for debugging. - * @param fd parcelfiledescriptor instance - * @param pw printwriter instance - * @param args args for dumping - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - public void dump(@NonNull ParcelFileDescriptor fd, @NonNull PrintWriter pw, - @NonNull String[] args) { - pw.println(" " + getComponent() - + " (Description: " + getDescription() + ")" - + " (UID: " + getUid() + ")"); - if (mOnHost) { - pw.println(" On Host Service"); - } else { - pw.println(" Off-host Service"); - pw.println(" " + "Current off-host SE:" + mOffHostName - + " static off-host SE:" + mStaticOffHostName); - } - pw.println(" Static AID groups:"); - for (AidGroup group : mStaticAidGroups.values()) { - pw.println(" Category: " + group.getCategory() - + "(enabled: " + mCategoryOtherServiceEnabled + ")"); - for (String aid : group.getAids()) { - pw.println(" AID: " + aid); - } - } - pw.println(" Dynamic AID groups:"); - for (AidGroup group : mDynamicAidGroups.values()) { - pw.println(" Category: " + group.getCategory() - + "(enabled: " + mCategoryOtherServiceEnabled + ")"); - for (String aid : group.getAids()) { - pw.println(" AID: " + aid); - } - } - pw.println(" Settings Activity: " + mSettingsActivityName); - pw.println(" Requires Device Unlock: " + mRequiresDeviceUnlock); - pw.println(" Requires Device ScreenOn: " + mRequiresDeviceScreenOn); - pw.println(" Should Default to Observe Mode: " + mShouldDefaultToObserveMode); - pw.println(" Auto-Transact Mapping: " + mAutoTransact); - pw.println(" Auto-Transact Patterns: " + mAutoTransactPatterns); - } - - - /** - * Enable or disable this CATEGORY_OTHER service. - * - * @param enabled true to indicate if user has enabled this service - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - public void setCategoryOtherServiceEnabled(boolean enabled) { - mCategoryOtherServiceEnabled = enabled; - } - - - /** - * Returns whether this CATEGORY_OTHER service is enabled or not. - * - * @return true to indicate if user has enabled this service - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - public boolean isCategoryOtherServiceEnabled() { - return mCategoryOtherServiceEnabled; - } - - /** - * Dump debugging info as ApduServiceInfoProto. - * - * If the output belongs to a sub message, the caller is responsible for wrapping this function - * between {@link ProtoOutputStream#start(long)} and {@link ProtoOutputStream#end(long)}. - * See proto definition in frameworks/base/core/proto/android/nfc/apdu_service_info.proto - * - * @param proto the ProtoOutputStream to write to - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - public void dumpDebug(@NonNull ProtoOutputStream proto) { - getComponent().dumpDebug(proto, ApduServiceInfoProto.COMPONENT_NAME); - proto.write(ApduServiceInfoProto.DESCRIPTION, getDescription()); - proto.write(ApduServiceInfoProto.ON_HOST, mOnHost); - if (!mOnHost) { - proto.write(ApduServiceInfoProto.OFF_HOST_NAME, mOffHostName); - proto.write(ApduServiceInfoProto.STATIC_OFF_HOST_NAME, mStaticOffHostName); - } - for (AidGroup group : mStaticAidGroups.values()) { - long token = proto.start(ApduServiceInfoProto.STATIC_AID_GROUPS); - group.dump(proto); - proto.end(token); - } - for (AidGroup group : mDynamicAidGroups.values()) { - long token = proto.start(ApduServiceInfoProto.STATIC_AID_GROUPS); - group.dump(proto); - proto.end(token); - } - proto.write(ApduServiceInfoProto.SETTINGS_ACTIVITY_NAME, mSettingsActivityName); - proto.write(ApduServiceInfoProto.SHOULD_DEFAULT_TO_OBSERVE_MODE, - mShouldDefaultToObserveMode); - { - long token = proto.start(ApduServiceInfoProto.AUTO_TRANSACT_MAPPING); - for (Map.Entry entry : mAutoTransact.entrySet()) { - proto.write(ApduServiceInfoProto.AutoTransactMapping.AID, entry.getKey()); - proto.write(ApduServiceInfoProto.AutoTransactMapping.SHOULD_AUTO_TRANSACT, - entry.getValue()); - } - proto.end(token); - } - { - long token = proto.start(ApduServiceInfoProto.AUTO_TRANSACT_PATTERNS); - for (Map.Entry entry : mAutoTransactPatterns.entrySet()) { - proto.write(ApduServiceInfoProto.AutoTransactPattern.REGEXP_PATTERN, - entry.getKey().pattern()); - proto.write(ApduServiceInfoProto.AutoTransactPattern.SHOULD_AUTO_TRANSACT, - entry.getValue()); - } - proto.end(token); - } - } - - private static final Pattern AID_PATTERN = Pattern.compile("[0-9A-Fa-f]{10,32}\\*?\\#?"); - /** - * Copied over from {@link CardEmulation#isValidAid(String)} - * @hide - */ - private static boolean isValidAid(String aid) { - if (aid == null) - return false; - - // If a prefix/subset AID, the total length must be odd (even # of AID chars + '*') - if ((aid.endsWith("*") || aid.endsWith("#")) && ((aid.length() % 2) == 0)) { - Log.e(TAG, "AID " + aid + " is not a valid AID."); - return false; - } - - // If not a prefix/subset AID, the total length must be even (even # of AID chars) - if ((!(aid.endsWith("*") || aid.endsWith("#"))) && ((aid.length() % 2) != 0)) { - Log.e(TAG, "AID " + aid + " is not a valid AID."); - return false; - } - - // Verify hex characters - if (!AID_PATTERN.matcher(aid).matches()) { - Log.e(TAG, "AID " + aid + " is not a valid AID."); - return false; - } - - return true; - } -} diff --git a/nfc/java/android/nfc/cardemulation/NfcFServiceInfo.aidl b/nfc/java/android/nfc/cardemulation/NfcFServiceInfo.aidl deleted file mode 100644 index 56b98ebd90fa..000000000000 --- a/nfc/java/android/nfc/cardemulation/NfcFServiceInfo.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.nfc.cardemulation; - -parcelable NfcFServiceInfo; diff --git a/nfc/java/android/nfc/cardemulation/NfcFServiceInfo.java b/nfc/java/android/nfc/cardemulation/NfcFServiceInfo.java deleted file mode 100644 index 33bc16978721..000000000000 --- a/nfc/java/android/nfc/cardemulation/NfcFServiceInfo.java +++ /dev/null @@ -1,497 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/********************************************************************** - * This file is not a part of the NFC mainline module * - * *******************************************************************/ - -package android.nfc.cardemulation; - -import android.annotation.FlaggedApi; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.SystemApi; -import android.content.ComponentName; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.content.pm.ResolveInfo; -import android.content.pm.ServiceInfo; -import android.content.res.Resources; -import android.content.res.TypedArray; -import android.content.res.XmlResourceParser; -import android.graphics.drawable.Drawable; -import android.nfc.Flags; -import android.os.Parcel; -import android.os.ParcelFileDescriptor; -import android.os.Parcelable; -import android.util.AttributeSet; -import android.util.Log; -import android.util.Xml; -import android.util.proto.ProtoOutputStream; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; - -import java.io.IOException; -import java.io.PrintWriter; - -/** - * Class to hold NfcF service info. - * - * @hide - */ -@SystemApi -@FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) -public final class NfcFServiceInfo implements Parcelable { - static final String TAG = "NfcFServiceInfo"; - - private static final String DEFAULT_T3T_PMM = "FFFFFFFFFFFFFFFF"; - - /** - * The service that implements this - */ - private final ResolveInfo mService; - - /** - * Description of the service - */ - private final String mDescription; - - /** - * System Code of the service - */ - private final String mSystemCode; - - /** - * System Code of the service registered by API - */ - private String mDynamicSystemCode; - - /** - * NFCID2 of the service - */ - private final String mNfcid2; - - /** - * NFCID2 of the service registered by API - */ - private String mDynamicNfcid2; - - /** - * The uid of the package the service belongs to - */ - private final int mUid; - - /** - * LF_T3T_PMM of the service - */ - private final String mT3tPmm; - - /** - * @hide - */ - public NfcFServiceInfo(ResolveInfo info, String description, - String systemCode, String dynamicSystemCode, String nfcid2, String dynamicNfcid2, - int uid, String t3tPmm) { - this.mService = info; - this.mDescription = description; - this.mSystemCode = systemCode; - this.mDynamicSystemCode = dynamicSystemCode; - this.mNfcid2 = nfcid2; - this.mDynamicNfcid2 = dynamicNfcid2; - this.mUid = uid; - this.mT3tPmm = t3tPmm; - } - - /** - * Creates a new NfcFServiceInfo object. - * - * @param pm packageManager instance - * @param info app component info - * @throws XmlPullParserException If an error occurs parsing the element. - * @throws IOException If an error occurs reading the element. - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - public NfcFServiceInfo(@NonNull PackageManager pm, @NonNull ResolveInfo info) - throws XmlPullParserException, IOException { - ServiceInfo si = info.serviceInfo; - XmlResourceParser parser = null; - try { - parser = si.loadXmlMetaData(pm, HostNfcFService.SERVICE_META_DATA); - if (parser == null) { - throw new XmlPullParserException("No " + HostNfcFService.SERVICE_META_DATA + - " meta-data"); - } - - int eventType = parser.getEventType(); - while (eventType != XmlPullParser.START_TAG && - eventType != XmlPullParser.END_DOCUMENT) { - eventType = parser.next(); - } - - String tagName = parser.getName(); - if (!"host-nfcf-service".equals(tagName)) { - throw new XmlPullParserException( - "Meta-data does not start with tag"); - } - - Resources res = pm.getResourcesForApplication(si.applicationInfo); - AttributeSet attrs = Xml.asAttributeSet(parser); - TypedArray sa = res.obtainAttributes(attrs, - com.android.internal.R.styleable.HostNfcFService); - mService = info; - mDescription = sa.getString( - com.android.internal.R.styleable.HostNfcFService_description); - mDynamicSystemCode = null; - mDynamicNfcid2 = null; - sa.recycle(); - - String systemCode = null; - String nfcid2 = null; - String t3tPmm = null; - final int depth = parser.getDepth(); - - while (((eventType = parser.next()) != XmlPullParser.END_TAG || - parser.getDepth() > depth) && eventType != XmlPullParser.END_DOCUMENT) { - tagName = parser.getName(); - if (eventType == XmlPullParser.START_TAG && - "system-code-filter".equals(tagName) && systemCode == null) { - final TypedArray a = res.obtainAttributes(attrs, - com.android.internal.R.styleable.SystemCodeFilter); - systemCode = a.getString( - com.android.internal.R.styleable.SystemCodeFilter_name).toUpperCase(); - if (!isValidSystemCode(systemCode) && - !systemCode.equalsIgnoreCase("NULL")) { - Log.e(TAG, "Invalid System Code: " + systemCode); - systemCode = null; - } - a.recycle(); - } else if (eventType == XmlPullParser.START_TAG && - "nfcid2-filter".equals(tagName) && nfcid2 == null) { - final TypedArray a = res.obtainAttributes(attrs, - com.android.internal.R.styleable.Nfcid2Filter); - nfcid2 = a.getString( - com.android.internal.R.styleable.Nfcid2Filter_name).toUpperCase(); - if (!nfcid2.equalsIgnoreCase("RANDOM") && - !nfcid2.equalsIgnoreCase("NULL") && - !isValidNfcid2(nfcid2)) { - Log.e(TAG, "Invalid NFCID2: " + nfcid2); - nfcid2 = null; - } - a.recycle(); - } else if (eventType == XmlPullParser.START_TAG && tagName.equals("t3tPmm-filter") - && t3tPmm == null) { - final TypedArray a = res.obtainAttributes(attrs, - com.android.internal.R.styleable.T3tPmmFilter); - t3tPmm = a.getString( - com.android.internal.R.styleable.T3tPmmFilter_name).toUpperCase(); - a.recycle(); - } - } - mSystemCode = (systemCode == null ? "NULL" : systemCode); - mNfcid2 = (nfcid2 == null ? "NULL" : nfcid2); - mT3tPmm = (t3tPmm == null ? DEFAULT_T3T_PMM : t3tPmm); - } catch (NameNotFoundException e) { - throw new XmlPullParserException("Unable to create context for: " + si.packageName); - } finally { - if (parser != null) parser.close(); - } - // Set uid - mUid = si.applicationInfo.uid; - } - - /** - * Returns the app component corresponding to this NFCF service. - * - * @return app component for this service - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - @NonNull - public ComponentName getComponent() { - return new ComponentName(mService.serviceInfo.packageName, - mService.serviceInfo.name); - } - - /** - * Returns the system code corresponding to this service. - * - * @return system code for this service - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - @NonNull - public String getSystemCode() { - return (mDynamicSystemCode == null ? mSystemCode : mDynamicSystemCode); - } - - /** - * Add or replace a system code to this service. - * @param systemCode system code to set or replace - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - public void setDynamicSystemCode(@NonNull String systemCode) { - mDynamicSystemCode = systemCode; - } - - /** - * Returns NFC ID2. - * - * @return nfc id2 to return - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - @NonNull - public String getNfcid2() { - return (mDynamicNfcid2 == null ? mNfcid2 : mDynamicNfcid2); - } - - /** - * Set or replace NFC ID2 - * - * @param nfcid2 NFC ID2 string - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - public void setDynamicNfcid2(@NonNull String nfcid2) { - mDynamicNfcid2 = nfcid2; - } - - /** - * Returns description of service. - * @return user readable description of service - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - @NonNull - public String getDescription() { - return mDescription; - } - - /** - * Returns uid of service. - * @return uid of the service - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - public int getUid() { - return mUid; - } - - /** - * Returns LF_T3T_PMM of the service - * @return returns LF_T3T_PMM of the service - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - @NonNull - public String getT3tPmm() { - return mT3tPmm; - } - - /** - * Load application label for this service. - * @param pm packagemanager instance - * @return label name corresponding to service - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - @NonNull - public CharSequence loadLabel(@NonNull PackageManager pm) { - return mService.loadLabel(pm); - } - - /** - * Load application icon for this service. - * @param pm packagemanager instance - * @return app icon corresponding to service - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - @NonNull - public Drawable loadIcon(@NonNull PackageManager pm) { - return mService.loadIcon(pm); - } - - @Override - public String toString() { - StringBuilder out = new StringBuilder("NfcFService: "); - out.append(getComponent()); - out.append(", UID: " + mUid); - out.append(", description: " + mDescription); - out.append(", System Code: " + mSystemCode); - if (mDynamicSystemCode != null) { - out.append(", dynamic System Code: " + mDynamicSystemCode); - } - out.append(", NFCID2: " + mNfcid2); - if (mDynamicNfcid2 != null) { - out.append(", dynamic NFCID2: " + mDynamicNfcid2); - } - out.append(", T3T PMM:" + mT3tPmm); - return out.toString(); - } - - @Override - public boolean equals(@Nullable Object o) { - if (this == o) return true; - if (!(o instanceof NfcFServiceInfo)) return false; - NfcFServiceInfo thatService = (NfcFServiceInfo) o; - - if (!thatService.getComponent().equals(this.getComponent())) return false; - if (thatService.getUid() != this.getUid()) return false; - if (!thatService.mSystemCode.equalsIgnoreCase(this.mSystemCode)) return false; - if (!thatService.mNfcid2.equalsIgnoreCase(this.mNfcid2)) return false; - if (!thatService.mT3tPmm.equalsIgnoreCase(this.mT3tPmm)) return false; - return true; - } - - @Override - public int hashCode() { - return getComponent().hashCode(); - } - - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - @Override - public int describeContents() { - return 0; - } - - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - mService.writeToParcel(dest, flags); - dest.writeString(mDescription); - dest.writeString(mSystemCode); - dest.writeInt(mDynamicSystemCode != null ? 1 : 0); - if (mDynamicSystemCode != null) { - dest.writeString(mDynamicSystemCode); - } - dest.writeString(mNfcid2); - dest.writeInt(mDynamicNfcid2 != null ? 1 : 0); - if (mDynamicNfcid2 != null) { - dest.writeString(mDynamicNfcid2); - } - dest.writeInt(mUid); - dest.writeString(mT3tPmm); - }; - - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - public static final @NonNull Parcelable.Creator CREATOR = - new Parcelable.Creator() { - @Override - public NfcFServiceInfo createFromParcel(Parcel source) { - ResolveInfo info = ResolveInfo.CREATOR.createFromParcel(source); - String description = source.readString(); - String systemCode = source.readString(); - String dynamicSystemCode = null; - if (source.readInt() != 0) { - dynamicSystemCode = source.readString(); - } - String nfcid2 = source.readString(); - String dynamicNfcid2 = null; - if (source.readInt() != 0) { - dynamicNfcid2 = source.readString(); - } - int uid = source.readInt(); - String t3tPmm = source.readString(); - NfcFServiceInfo service = new NfcFServiceInfo(info, description, - systemCode, dynamicSystemCode, nfcid2, dynamicNfcid2, uid, t3tPmm); - return service; - } - - @Override - public NfcFServiceInfo[] newArray(int size) { - return new NfcFServiceInfo[size]; - } - }; - - /** - * Dump contents of the service for debugging. - * @param fd parcelfiledescriptor instance - * @param pw printwriter instance - * @param args args for dumping - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - public void dump(@NonNull ParcelFileDescriptor fd, @NonNull PrintWriter pw, - @NonNull String[] args) { - pw.println(" " + getComponent() - + " (Description: " + getDescription() + ")" - + " (UID: " + getUid() + ")"); - pw.println(" System Code: " + getSystemCode()); - pw.println(" NFCID2: " + getNfcid2()); - pw.println(" T3tPmm: " + getT3tPmm()); - } - - /** - * Dump debugging info as NfcFServiceInfoProto. - * - * If the output belongs to a sub message, the caller is responsible for wrapping this function - * between {@link ProtoOutputStream#start(long)} and {@link ProtoOutputStream#end(long)}. - * - * @param proto the ProtoOutputStream to write to - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - public void dumpDebug(@NonNull ProtoOutputStream proto) { - getComponent().dumpDebug(proto, NfcFServiceInfoProto.COMPONENT_NAME); - proto.write(NfcFServiceInfoProto.DESCRIPTION, getDescription()); - proto.write(NfcFServiceInfoProto.SYSTEM_CODE, getSystemCode()); - proto.write(NfcFServiceInfoProto.NFCID2, getNfcid2()); - proto.write(NfcFServiceInfoProto.T3T_PMM, getT3tPmm()); - } - - /** - * Copied over from {@link NfcFCardEmulation#isValidSystemCode(String)} - * @hide - */ - private static boolean isValidSystemCode(String systemCode) { - if (systemCode == null) { - return false; - } - if (systemCode.length() != 4) { - Log.e(TAG, "System Code " + systemCode + " is not a valid System Code."); - return false; - } - // check if the value is between "4000" and "4FFF" (excluding "4*FF") - if (!systemCode.startsWith("4") || systemCode.toUpperCase().endsWith("FF")) { - Log.e(TAG, "System Code " + systemCode + " is not a valid System Code."); - return false; - } - try { - Integer.parseInt(systemCode, 16); - } catch (NumberFormatException e) { - Log.e(TAG, "System Code " + systemCode + " is not a valid System Code."); - return false; - } - return true; - } - - /** - * Copied over from {@link NfcFCardEmulation#isValidNfcid2(String)} - * @hide - */ - private static boolean isValidNfcid2(String nfcid2) { - if (nfcid2 == null) { - return false; - } - if (nfcid2.length() != 16) { - Log.e(TAG, "NFCID2 " + nfcid2 + " is not a valid NFCID2."); - return false; - } - // check if the the value starts with "02FE" - if (!nfcid2.toUpperCase().startsWith("02FE")) { - Log.e(TAG, "NFCID2 " + nfcid2 + " is not a valid NFCID2."); - return false; - } - try { - Long.parseLong(nfcid2, 16); - } catch (NumberFormatException e) { - Log.e(TAG, "NFCID2 " + nfcid2 + " is not a valid NFCID2."); - return false; - } - return true; - } -} diff --git a/nfc/java/android/nfc/flags.aconfig b/nfc/java/android/nfc/flags.aconfig deleted file mode 100644 index ee287aba709f..000000000000 --- a/nfc/java/android/nfc/flags.aconfig +++ /dev/null @@ -1,191 +0,0 @@ -package: "android.nfc" -container: "system" - -flag { - name: "nfc_event_listener" - is_exported: true - namespace: "nfc" - description: "Enable NFC Event listener APIs" - bug: "356447790" -} - -flag { - name: "enable_nfc_mainline" - is_exported: true - namespace: "nfc" - description: "Flag for NFC mainline changes" - bug: "292140387" -} - -flag { - name: "enable_nfc_reader_option" - is_exported: true - namespace: "nfc" - description: "Flag for NFC reader option API changes" - bug: "291187960" -} - -flag { - name: "enable_nfc_user_restriction" - is_exported: true - namespace: "nfc" - description: "Flag for NFC user restriction" - bug: "291187960" -} - -flag { - name: "nfc_observe_mode" - is_exported: true - namespace: "nfc" - description: "Enable NFC Observe Mode" - bug: "294217286" -} - -flag { - name: "nfc_read_polling_loop" - is_exported: true - namespace: "nfc" - description: "Enable NFC Polling Loop Notifications" - bug: "294217286" -} - -flag { - name: "nfc_observe_mode_st_shim" - namespace: "nfc" - description: "Enable NFC Observe Mode ST shim" - bug: "294217286" -} - -flag { - name: "nfc_read_polling_loop_st_shim" - namespace: "nfc" - description: "Enable NFC Polling Loop Notifications ST shim" - bug: "294217286" -} - -flag { - name: "enable_tag_detection_broadcasts" - namespace: "nfc" - description: "Enable sending broadcasts to Wallet role holder when a tag enters/leaves the field." - bug: "306203494" -} - -flag { - name: "enable_nfc_charging" - is_exported: true - namespace: "nfc" - description: "Flag for NFC charging changes" - bug: "292143899" -} - -flag { - name: "enable_nfc_set_discovery_tech" - is_exported: true - namespace: "nfc" - description: "Flag for NFC set discovery tech API" - bug: "300351519" -} - -flag { - name: "nfc_vendor_cmd" - is_exported: true - namespace: "nfc" - 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" -} - -flag { - name: "nfc_state_change" - is_exported: true - namespace: "nfc" - description: "Enable nfc state change API" - bug: "319934052" -} - -flag { - name: "nfc_set_default_disc_tech" - is_exported: true - namespace: "nfc" - description: "Flag for NFC set default disc tech API" - bug: "321311407" -} - -flag { - name: "nfc_persist_log" - is_exported: true - namespace: "nfc" - description: "Enable NFC persistent log support" - bug: "321310044" -} - -flag { - name: "nfc_action_manage_services_settings" - is_exported: true - namespace: "nfc" - description: "Add Settings.ACTION_MANAGE_OTHER_NFC_SERVICES_SETTINGS" - bug: "358129872" -} - -flag { - name: "nfc_override_recover_routing_table" - is_exported: true - namespace: "nfc" - description: "Enable override and recover routing table" - bug: "329043523" -} - -flag { - name: "nfc_watchdog" - is_exported: true - namespace: "nfc" - description: "Enable watchdog for the NFC system process" - bug: "362937338" -} - -flag { - name: "enable_card_emulation_euicc" - is_exported: true - namespace: "nfc" - description: "Enable EUICC card emulation" - bug: "321314635" -} - -flag { - name: "nfc_state_change_security_log_event_enabled" - is_exported: true - namespace: "nfc" - description: "Enabling security log for nfc state change" - bug: "319934052" -} - -flag { - name: "nfc_associated_role_services" - is_exported: true - namespace: "nfc" - description: "Share wallet role routing priority with associated services" - bug: "366243361" -} - -flag { - name: "nfc_set_service_enabled_for_category_other" - is_exported: true - namespace: "nfc" - description: "Enable set service enabled for category other" - bug: "338157113" -} - -flag { - name: "nfc_check_tag_intent_preference" - is_exported: true - namespace: "nfc" - description: "App can check its tag intent preference status" - bug: "335916336" -} diff --git a/nfc/tests/Android.bp b/nfc/tests/Android.bp index b6090e853158..17fb810c626b 100644 --- a/nfc/tests/Android.bp +++ b/nfc/tests/Android.bp @@ -29,7 +29,6 @@ android_test { "androidx.test.rules", "androidx.test.runner", "androidx.test.ext.junit", - "framework-nfc.impl", "mockito-target-extended-minus-junit4", "frameworks-base-testutils", "truth", @@ -40,16 +39,25 @@ android_test { "testables", ], libs: [ + "androidx.annotation_annotation", + "unsupportedappusage", // for android.compat.annotation.UnsupportedAppUsage + "framework-permission-s.stubs.module_lib", + "framework-permission.stubs.module_lib", "android.test.base.stubs.system", "android.test.mock.stubs.system", "android.test.runner.stubs.system", + "framework-nfc.impl", ], jni_libs: [ // Required for ExtendedMockito "libdexmakerjvmtiagent", "libstaticjvmtiagent", ], - srcs: ["src/**/*.java"], + srcs: [ + "src/**/*.java", + ":framework-nfc-updatable-sources", + ":framework-nfc-non-updatable-sources", + ], platform_apis: true, certificate: "platform", test_suites: [ -- cgit v1.2.3-59-g8ed1b