diff --git a/core/java/android/nfc/INfcCardEmulation.aidl b/core/java/android/nfc/INfcCardEmulation.aidl index 53843fe73d33e18fd715ae841b8d1f5a1e2a0d21..c7b3b2c03f652a15953ac82f94cadc0590cf8dc2 100644 --- a/core/java/android/nfc/INfcCardEmulation.aidl +++ b/core/java/android/nfc/INfcCardEmulation.aidl @@ -40,5 +40,6 @@ interface INfcCardEmulation boolean unsetPreferredService(); boolean supportsAidPrefixRegistration(); ApduServiceInfo getPreferredPaymentService(int userHandle); + boolean setServiceEnabledForCategoryOther(int userHandle, in ComponentName app, boolean status); boolean isDefaultPaymentRegistered(); } diff --git a/core/java/android/nfc/cardemulation/ApduServiceInfo.java b/core/java/android/nfc/cardemulation/ApduServiceInfo.java index 665b7531d3ce21e1645ffbafdb7f315a2d6baf2a..9cf8c4ddc53bdb14d39a630a0d46ad56c776e594 100644 --- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java +++ b/core/java/android/nfc/cardemulation/ApduServiceInfo.java @@ -126,6 +126,11 @@ public final class ApduServiceInfo implements Parcelable { */ private final String mSettingsActivityName; + /** + * State of the service for CATEGORY_OTHER selection + */ + private boolean mOtherServiceSelectionState; + /** * @hide */ @@ -133,9 +138,22 @@ public final class ApduServiceInfo implements Parcelable { List<AidGroup> staticAidGroups, List<AidGroup> dynamicAidGroups, boolean requiresUnlock, int bannerResource, int uid, String settingsActivityName, String offHost, String staticOffHost) { + this(info, onHost, description, staticAidGroups, dynamicAidGroups, + requiresUnlock, bannerResource, uid, settingsActivityName, + offHost, staticOffHost, false); + } + + /** + * @hide + */ + public ApduServiceInfo(ResolveInfo info, boolean onHost, String description, + List<AidGroup> staticAidGroups, List<AidGroup> dynamicAidGroups, + boolean requiresUnlock, int bannerResource, int uid, + String settingsActivityName, String offHost, String staticOffHost, + boolean isSelected) { this(info, onHost, description, staticAidGroups, dynamicAidGroups, requiresUnlock, onHost ? true : false, bannerResource, uid, - settingsActivityName, offHost, staticOffHost); + settingsActivityName, offHost, staticOffHost, isSelected); } /** @@ -144,7 +162,7 @@ public final class ApduServiceInfo implements Parcelable { public ApduServiceInfo(ResolveInfo info, boolean onHost, String description, List<AidGroup> staticAidGroups, List<AidGroup> dynamicAidGroups, boolean requiresUnlock, boolean requiresScreenOn, int bannerResource, int uid, - String settingsActivityName, String offHost, String staticOffHost) { + String settingsActivityName, String offHost, String staticOffHost, boolean isSelected) { this.mService = info; this.mDescription = description; this.mStaticAidGroups = new HashMap<String, AidGroup>(); @@ -163,6 +181,8 @@ public final class ApduServiceInfo implements Parcelable { this.mBannerResourceId = bannerResource; this.mUid = uid; this.mSettingsActivityName = settingsActivityName; + this.mOtherServiceSelectionState = isSelected; + } /** @@ -351,6 +371,9 @@ public final class ApduServiceInfo implements Parcelable { } // Set uid mUid = si.applicationInfo.uid; + + mOtherServiceSelectionState = false; // support other category + } /** @@ -720,43 +743,47 @@ public final class ApduServiceInfo implements Parcelable { dest.writeInt(mBannerResourceId); dest.writeInt(mUid); dest.writeString(mSettingsActivityName); + + dest.writeInt(mOtherServiceSelectionState ? 1 : 0); }; @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public static final @NonNull Parcelable.Creator<ApduServiceInfo> CREATOR = new Parcelable.Creator<ApduServiceInfo>() { - @Override - public ApduServiceInfo createFromParcel(Parcel source) { - ResolveInfo info = ResolveInfo.CREATOR.createFromParcel(source); - String description = source.readString(); - boolean onHost = source.readInt() != 0; - String offHostName = source.readString(); - String staticOffHostName = source.readString(); - ArrayList<AidGroup> staticAidGroups = new ArrayList<AidGroup>(); - int numStaticGroups = source.readInt(); - if (numStaticGroups > 0) { - source.readTypedList(staticAidGroups, AidGroup.CREATOR); - } - ArrayList<AidGroup> dynamicAidGroups = new ArrayList<AidGroup>(); - int numDynamicGroups = source.readInt(); - if (numDynamicGroups > 0) { - source.readTypedList(dynamicAidGroups, AidGroup.CREATOR); - } - boolean requiresUnlock = source.readInt() != 0; - boolean requiresScreenOn = source.readInt() != 0; - int bannerResource = source.readInt(); - int uid = source.readInt(); - String settingsActivityName = source.readString(); - return new ApduServiceInfo(info, onHost, description, staticAidGroups, - dynamicAidGroups, requiresUnlock, requiresScreenOn, bannerResource, uid, - settingsActivityName, offHostName, staticOffHostName); - } + @Override + public ApduServiceInfo createFromParcel(Parcel source) { + ResolveInfo info = ResolveInfo.CREATOR.createFromParcel(source); + String description = source.readString(); + boolean onHost = source.readInt() != 0; + String offHostName = source.readString(); + String staticOffHostName = source.readString(); + ArrayList<AidGroup> staticAidGroups = new ArrayList<AidGroup>(); + int numStaticGroups = source.readInt(); + if (numStaticGroups > 0) { + source.readTypedList(staticAidGroups, AidGroup.CREATOR); + } + ArrayList<AidGroup> dynamicAidGroups = new ArrayList<AidGroup>(); + int numDynamicGroups = source.readInt(); + if (numDynamicGroups > 0) { + source.readTypedList(dynamicAidGroups, AidGroup.CREATOR); + } + boolean requiresUnlock = source.readInt() != 0; + boolean requiresScreenOn = source.readInt() != 0; + int bannerResource = source.readInt(); + int uid = source.readInt(); + String settingsActivityName = source.readString(); + boolean isSelected = source.readInt() != 0; + return new ApduServiceInfo(info, onHost, description, staticAidGroups, + dynamicAidGroups, requiresUnlock, requiresScreenOn, bannerResource, uid, + settingsActivityName, offHostName, staticOffHostName, + isSelected); + } - @Override - public ApduServiceInfo[] newArray(int size) { - return new ApduServiceInfo[size]; - } - }; + @Override + public ApduServiceInfo[] newArray(int size) { + return new ApduServiceInfo[size]; + } + }; /** * Dump contents for debugging. @@ -779,14 +806,16 @@ public final class ApduServiceInfo implements Parcelable { } pw.println(" Static AID groups:"); for (AidGroup group : mStaticAidGroups.values()) { - pw.println(" Category: " + group.getCategory()); + pw.println(" Category: " + group.getCategory() + + "(selected: " + mOtherServiceSelectionState + ")"); for (String aid : group.getAids()) { pw.println(" AID: " + aid); } } pw.println(" Dynamic AID groups:"); for (AidGroup group : mDynamicAidGroups.values()) { - pw.println(" Category: " + group.getCategory()); + pw.println(" Category: " + group.getCategory() + + "(selected: " + mOtherServiceSelectionState + ")"); for (String aid : group.getAids()) { pw.println(" AID: " + aid); } @@ -796,6 +825,22 @@ public final class ApduServiceInfo implements Parcelable { pw.println(" Requires Device ScreenOn: " + mRequiresDeviceScreenOn); } + + /** + * @hide + */ + public void setOtherServiceState(boolean selected) { + mOtherServiceSelectionState = selected; + } + + + /** + * @hide + */ + public boolean isSelectedOtherService() { + return mOtherServiceSelectionState; + } + /** * Dump debugging info as ApduServiceInfoProto. * diff --git a/core/java/android/nfc/cardemulation/CardEmulation.java b/core/java/android/nfc/cardemulation/CardEmulation.java index d3b3a78a92cc105d67bf60fed710c32a6a583d9d..d048b595ad1e38b58563b6dafd2ca4a67f2b4c36 100644 --- a/core/java/android/nfc/cardemulation/CardEmulation.java +++ b/core/java/android/nfc/cardemulation/CardEmulation.java @@ -947,6 +947,39 @@ public final class CardEmulation { return true; } + /** + * Allows to set or unset preferred service (category other) to avoid AID Collision. + * + * @param service The ComponentName of the service + * @param status true to enable, false to disable + * @return set service for the category and true if service is already set return false. + * + * @hide + */ + public boolean setServiceEnabledForCategoryOther(ComponentName service, boolean status) { + if (service == null) { + throw new NullPointerException("activity or service or category is null"); + } + int userId = mContext.getUser().getIdentifier(); + + try { + return sService.setServiceEnabledForCategoryOther(userId, service, status); + } catch (RemoteException e) { + // Try one more time + recoverService(); + if (sService == null) { + Log.e(TAG, "Failed to recover CardEmulationService."); + return false; + } + try { + return sService.setServiceEnabledForCategoryOther(userId, service, status); + } catch (RemoteException ee) { + Log.e(TAG, "Failed to reach CardEmulationService."); + return false; + } + } + } + void recoverService() { NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext); sService = adapter.getCardEmulationService();