Merge "[Nfc framework] API to include/exclude Other category service component" into main am: 8c7eea4061

Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/2803133

Change-Id: Id1a59849765e22203414b6cc9be843a1c7c9823c
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/core/java/android/nfc/INfcCardEmulation.aidl b/core/java/android/nfc/INfcCardEmulation.aidl
index 53843fe..c7b3b2c 100644
--- a/core/java/android/nfc/INfcCardEmulation.aidl
+++ b/core/java/android/nfc/INfcCardEmulation.aidl
@@ -40,5 +40,6 @@
     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 665b753..9cf8c4d 100644
--- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java
+++ b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
@@ -127,6 +127,11 @@
     private final String mSettingsActivityName;
 
     /**
+     * State of the service for CATEGORY_OTHER selection
+     */
+    private boolean mOtherServiceSelectionState;
+
+    /**
      * @hide
      */
     public ApduServiceInfo(ResolveInfo info, boolean onHost, String description,
@@ -134,8 +139,21 @@
             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 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 @@
         this.mBannerResourceId = bannerResource;
         this.mUid = uid;
         this.mSettingsActivityName = settingsActivityName;
+        this.mOtherServiceSelectionState = isSelected;
+
     }
 
     /**
@@ -351,6 +371,9 @@
         }
         // Set uid
         mUid = si.applicationInfo.uid;
+
+        mOtherServiceSelectionState = false;    // support other category
+
     }
 
     /**
@@ -720,43 +743,47 @@
         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 @@
         }
         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 @@
         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 d3b3a78..d048b59 100644
--- a/core/java/android/nfc/cardemulation/CardEmulation.java
+++ b/core/java/android/nfc/cardemulation/CardEmulation.java
@@ -947,6 +947,39 @@
         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();