Added new API to support UX for external device permission

The new API retrieves external device permissions and flags for a given package and persistentDeviceId

Bug: 322876542
Test: atest CtsPermissionMultiDeviceTestCases
Change-Id: I3527adab302c271ec42ce926b3e7c076ce2073d8
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index f30c8cf..2d46e85 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -11306,6 +11306,7 @@
     method public int checkPermissionForPreflight(@NonNull String, @NonNull android.content.AttributionSource);
     method @RequiresPermission(value=android.Manifest.permission.UPDATE_APP_OPS_STATS, conditional=true) public int checkPermissionForStartDataDelivery(@NonNull String, @NonNull android.content.AttributionSource, @Nullable String);
     method public void finishDataDelivery(@NonNull String, @NonNull android.content.AttributionSource);
+    method @FlaggedApi("android.permission.flags.device_aware_permission_apis_enabled") @NonNull @RequiresPermission(android.Manifest.permission.GET_RUNTIME_PERMISSIONS) public java.util.Map<java.lang.String,android.permission.PermissionManager.PermissionState> getAllPermissionStates(@NonNull String, @NonNull String);
     method @NonNull @RequiresPermission(android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY) public java.util.Set<java.lang.String> getAutoRevokeExemptionGrantedPackages();
     method @NonNull @RequiresPermission(android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY) public java.util.Set<java.lang.String> getAutoRevokeExemptionRequestedPackages();
     method @FlaggedApi("android.permission.flags.device_aware_permission_apis_enabled") @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, android.Manifest.permission.GET_RUNTIME_PERMISSIONS}) public int getPermissionFlags(@NonNull String, @NonNull String, @NonNull String);
@@ -11325,6 +11326,14 @@
     field public static final int PERMISSION_SOFT_DENIED = 1; // 0x1
   }
 
+  @FlaggedApi("android.permission.flags.device_aware_permission_apis_enabled") public static final class PermissionManager.PermissionState implements android.os.Parcelable {
+    method @FlaggedApi("android.permission.flags.device_aware_permission_apis_enabled") public int describeContents();
+    method @FlaggedApi("android.permission.flags.device_aware_permission_apis_enabled") public int getFlags();
+    method @FlaggedApi("android.permission.flags.device_aware_permission_apis_enabled") public boolean isGranted();
+    method @FlaggedApi("android.permission.flags.device_aware_permission_apis_enabled") public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @FlaggedApi("android.permission.flags.device_aware_permission_apis_enabled") @NonNull public static final android.os.Parcelable.Creator<android.permission.PermissionManager.PermissionState> CREATOR;
+  }
+
   public static final class PermissionManager.SplitPermissionInfo {
     method @NonNull public java.util.List<java.lang.String> getNewPermissions();
     method @NonNull public String getSplitPermission();
diff --git a/core/java/android/permission/IPermissionManager.aidl b/core/java/android/permission/IPermissionManager.aidl
index 380962c..4ae0a57 100644
--- a/core/java/android/permission/IPermissionManager.aidl
+++ b/core/java/android/permission/IPermissionManager.aidl
@@ -23,6 +23,7 @@
 import android.content.pm.permission.SplitPermissionInfoParcelable;
 import android.os.UserHandle;
 import android.permission.IOnPermissionsChangeListener;
+import android.permission.PermissionManager.PermissionState;
 
 /**
  * Interface to communicate directly with the permission manager service.
@@ -103,4 +104,12 @@
             int userId);
 
     int checkUidPermission(int uid, String permissionName, int deviceId);
+
+    Map<String, PermissionState> getAllPermissionStates(String packageName, String persistentDeviceId, int userId);
 }
+
+/**
+ * Data class for the state of a permission requested by a package
+ * @hide
+ */
+parcelable PermissionManager.PermissionState;
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index d6e8ce7..e6b8102 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -66,6 +66,8 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
+import android.os.Parcel;
+import android.os.Parcelable;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -89,6 +91,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 
@@ -1785,6 +1788,29 @@
     }
 
     /**
+     * Gets the permission states for requested package and persistent device.
+     *
+     * @param packageName name of the package you are checking against
+     * @param persistentDeviceId id of the persistent device you are checking against
+     * @return mapping of all permission states keyed by their permission names
+     *
+     * @hide
+     */
+    @SystemApi
+    @NonNull
+    @RequiresPermission(android.Manifest.permission.GET_RUNTIME_PERMISSIONS)
+    @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
+    public Map<String, PermissionState> getAllPermissionStates(@NonNull String packageName,
+            @NonNull String persistentDeviceId) {
+        try {
+            return mPermissionManager.getAllPermissionStates(packageName, persistentDeviceId,
+                    mContext.getUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Make checkPermission() above bypass the permission cache in this process.
      *
      * @hide
@@ -1979,4 +2005,68 @@
             }
         }
     }
+
+    /**
+     * Data class for the state of a permission requested by a package
+     *
+     * @hide
+     */
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
+    public static final class PermissionState implements Parcelable {
+        private final boolean mGranted;
+        private final int mFlags;
+
+        /** @hide */
+        @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
+        public PermissionState(boolean granted, int flags) {
+            mGranted = granted;
+            mFlags = flags;
+        }
+
+        /**
+         * Returns whether this permission is granted
+         */
+        @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
+        public boolean isGranted() {
+            return mGranted;
+        }
+
+        /**
+         * Returns the flags associated with this permission state
+         * @see PackageManager#getPermissionFlags
+         */
+        @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
+        public int getFlags() {
+            return mFlags;
+        }
+
+        @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
+        @Override
+        public void writeToParcel(@NonNull Parcel parcel, int flags) {
+            parcel.writeBoolean(mGranted);
+            parcel.writeInt(mFlags);
+        }
+
+        private PermissionState(Parcel parcel) {
+            this(parcel.readBoolean(), parcel.readInt());
+        }
+
+        @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
+        public static final @NonNull Creator<PermissionState> CREATOR = new Creator<>() {
+            public PermissionState createFromParcel(Parcel source) {
+                return new PermissionState(source);
+            }
+
+            public PermissionState[] newArray(int size) {
+                return new PermissionState[size];
+            }
+        };
+    }
 }
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index f1dca77..f44fcf0 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -65,6 +65,7 @@
 import android.permission.IPermissionManager;
 import android.permission.PermissionCheckerManager;
 import android.permission.PermissionManager;
+import android.permission.PermissionManager.PermissionState;
 import android.permission.PermissionManagerInternal;
 import android.service.voice.VoiceInteractionManagerInternal;
 import android.util.ArrayMap;
@@ -258,6 +259,13 @@
     }
 
     @Override
+    public Map<String, PermissionState> getAllPermissionStates(@NonNull String packageName,
+            @NonNull String persistentDeviceId, int userId) {
+        return mPermissionManagerServiceImpl.getAllPermissionStates(packageName,
+                persistentDeviceId, userId);
+    }
+
+    @Override
     public boolean setAutoRevokeExempted(
             @NonNull String packageName, boolean exempted, int userId) {
         Objects.requireNonNull(packageName);
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
index 9afd36f..c5b637d 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -999,6 +999,13 @@
         return checkUidPermissionInternal(pkg, uid, permName);
     }
 
+    @Override
+    public Map<String, PermissionManager.PermissionState> getAllPermissionStates(
+            @NonNull String packageName, @NonNull String persistentDeviceId, int userId) {
+        throw new UnsupportedOperationException(
+                "This method is supported in newer implementation only");
+    }
+
     /**
      * Checks whether or not the given package has been granted the specified
      * permission. If the given package is {@code null}, we instead check the
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
index b12d8ac..7c10425 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
@@ -25,6 +25,7 @@
 import android.content.pm.PermissionInfo;
 import android.content.pm.permission.SplitPermissionInfoParcelable;
 import android.permission.IOnPermissionsChangeListener;
+import android.permission.PermissionManager.PermissionState;
 import android.permission.PermissionManagerInternal;
 
 import com.android.server.pm.pkg.AndroidPackage;
@@ -406,6 +407,19 @@
     int checkUidPermission(int uid, String permName, int deviceId);
 
     /**
+     * Gets the permission states for requested package, persistent device and user.
+     *
+     * @param packageName name of the package you are checking against
+     * @param persistentDeviceId id of the persistent device you are checking against
+     * @param userId id of the user for which to get permission flags
+     * @return mapping of all permission states keyed by their permission names
+     *
+     * @hide
+     */
+    Map<String, PermissionState> getAllPermissionStates(@NonNull String packageName,
+            @NonNull String persistentDeviceId, @UserIdInt int userId);
+
+    /**
      * Get all the package names requesting app op permissions.
      *
      * @return a map of app op permission names to package names requesting them
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java
index 835ddcb..91a778d 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java
@@ -22,6 +22,7 @@
 import android.content.pm.PermissionInfo;
 import android.content.pm.permission.SplitPermissionInfoParcelable;
 import android.permission.IOnPermissionsChangeListener;
+import android.permission.PermissionManager.PermissionState;
 import android.util.Log;
 
 import com.android.server.pm.pkg.AndroidPackage;
@@ -243,12 +244,20 @@
 
     @Override
     public int checkUidPermission(int uid, String permName, int deviceId) {
-        Log.i(LOG_TAG, "checkUidPermission(uid = " + uid + ", permName = " + permName
-                + ", deviceId = " + deviceId + ")");
+        Log.i(LOG_TAG, "checkUidPermission(uid = " + uid + ", permName = "
+                + permName + ", deviceId = " + deviceId + ")");
         return mService.checkUidPermission(uid, permName, deviceId);
     }
 
     @Override
+    public Map<String, PermissionState> getAllPermissionStates(@NonNull String packageName,
+            @NonNull String persistentDeviceId, int userId) {
+        Log.i(LOG_TAG, "getAllPermissionStates(packageName = " + packageName
+                + ", persistentDeviceId = " + persistentDeviceId + ", userId = " + userId + ")");
+        return mService.getAllPermissionStates(packageName, persistentDeviceId, userId);
+    }
+
+    @Override
     public Map<String, Set<String>> getAllAppOpPermissionPackages() {
         Log.i(LOG_TAG, "getAllAppOpPermissionPackages()");
         return mService.getAllAppOpPermissionPackages();
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTestingShim.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTestingShim.java
index 66a6f3c..0a4ff07 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTestingShim.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTestingShim.java
@@ -24,6 +24,7 @@
 import android.content.pm.permission.SplitPermissionInfoParcelable;
 import android.os.Build;
 import android.permission.IOnPermissionsChangeListener;
+import android.permission.PermissionManager.PermissionState;
 
 import com.android.server.pm.pkg.AndroidPackage;
 import com.android.server.pm.pkg.PackageState;
@@ -327,6 +328,12 @@
     }
 
     @Override
+    public Map<String, PermissionState> getAllPermissionStates(@NonNull String packageName,
+            @NonNull String persistentDeviceId, int userId) {
+        return mNewImplementation.getAllPermissionStates(packageName, persistentDeviceId, userId);
+    }
+
+    @Override
     public Map<String, Set<String>> getAllAppOpPermissionPackages() {
         Map<String, Set<String>> oldVal = mOldImplementation.getAllAppOpPermissionPackages();
         Map<String, Set<String>> newVal = mNewImplementation.getAllAppOpPermissionPackages();
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTracingDecorator.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTracingDecorator.java
index f21993c..bc29e67 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTracingDecorator.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTracingDecorator.java
@@ -23,6 +23,7 @@
 import android.content.pm.permission.SplitPermissionInfoParcelable;
 import android.os.Trace;
 import android.permission.IOnPermissionsChangeListener;
+import android.permission.PermissionManager.PermissionState;
 
 import com.android.server.pm.pkg.AndroidPackage;
 import com.android.server.pm.pkg.PackageState;
@@ -348,6 +349,18 @@
     }
 
     @Override
+    public Map<String, PermissionState> getAllPermissionStates(@NonNull String packageName,
+            @NonNull String persistentDeviceId, int userId) {
+        Trace.traceBegin(TRACE_TAG, "TaggedTracingPermissionManagerServiceImpl"
+                + "#getAllPermissionStates");
+        try {
+            return mService.getAllPermissionStates(packageName, persistentDeviceId, userId);
+        } finally {
+            Trace.traceEnd(TRACE_TAG);
+        }
+    }
+
+    @Override
     public Map<String, Set<String>> getAllAppOpPermissionPackages() {
         Trace.traceBegin(TRACE_TAG,
                 "TaggedTracingPermissionManagerServiceImpl#getAllAppOpPermissionPackages");
diff --git a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
index 4c74878..5588276 100644
--- a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
@@ -1662,6 +1662,9 @@
     ): Int =
         state.userStates[userId]?.appIdPermissionFlags?.get(appId).getWithDefault(permissionName, 0)
 
+    fun GetStateScope.getAllPermissionFlags(appId: Int, userId: Int): IndexedMap<String, Int>? =
+        state.userStates[userId]?.appIdPermissionFlags?.get(appId)
+
     fun MutateStateScope.setPermissionFlags(
         appId: Int,
         userId: Int,
diff --git a/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt
index 3284cf1..3e3047c 100644
--- a/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt
@@ -95,8 +95,8 @@
     ) {
         packageNames.forEachIndexed { _, packageName ->
             // The package may still be removed even if it was once notified as installed.
-            val packageState = newState.externalState.packageStates[packageName]
-                ?: return@forEachIndexed
+            val packageState =
+                newState.externalState.packageStates[packageName] ?: return@forEachIndexed
             trimPermissionStates(packageState.appId)
         }
     }
@@ -226,6 +226,16 @@
         return flags
     }
 
+    fun GetStateScope.getAllPermissionFlags(
+        appId: Int,
+        persistentDeviceId: String,
+        userId: Int
+    ): IndexedMap<String, Int>? =
+        state.userStates[userId]
+            ?.appIdDevicePermissionFlags
+            ?.get(appId)
+            ?.get(persistentDeviceId)
+
     fun MutateStateScope.setPermissionFlags(
         appId: Int,
         deviceId: String,
diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
index 1241ce6..2f5c109 100644
--- a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
@@ -45,6 +45,7 @@
 import android.permission.IOnPermissionsChangeListener
 import android.permission.PermissionControllerManager
 import android.permission.PermissionManager
+import android.permission.PermissionManager.PermissionState
 import android.permission.flags.Flags
 import android.provider.Settings
 import android.util.ArrayMap
@@ -1135,6 +1136,53 @@
         }
     }
 
+    override fun getAllPermissionStates(
+        packageName: String,
+        persistentDeviceId: String,
+        userId: Int
+    ): Map<String, PermissionState> {
+        if (!userManagerInternal.exists(userId)) {
+            Slog.w(LOG_TAG, "getAllPermissionStates: Unknown user $userId")
+            return emptyMap()
+        }
+        enforceCallingOrSelfCrossUserPermission(
+            userId,
+            enforceFullPermission = true,
+            enforceShellRestriction = false,
+            "getAllPermissionStates"
+        )
+        enforceCallingOrSelfAnyPermission(
+            "getAllPermissionStates",
+            Manifest.permission.GET_RUNTIME_PERMISSIONS
+        )
+
+        val packageState =
+            packageManagerLocal.withFilteredSnapshot().use { it.getPackageState(packageName) }
+        if (packageState == null) {
+            Slog.w(LOG_TAG, "getAllPermissionStates: Unknown package $packageName")
+            return emptyMap()
+        }
+
+        val permissionFlagsMap =
+            service.getState {
+                if (persistentDeviceId == VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT) {
+                    with(policy) { getAllPermissionFlags(packageState.appId, userId) }
+                } else {
+                    with(devicePolicy) {
+                        getAllPermissionFlags(packageState.appId, persistentDeviceId, userId)
+                    }
+                }
+            } ?: return emptyMap()
+
+        val permissionStates = ArrayMap<String, PermissionState>()
+        permissionFlagsMap.forEachIndexed { _, permissionName, flags ->
+            val granted = PermissionFlags.isPermissionGranted(flags)
+            val apiFlags = PermissionFlags.toApiFlags(flags)
+            permissionStates[permissionName] = PermissionState(granted, apiFlags)
+        }
+        return permissionStates
+    }
+
     override fun isPermissionRevokedByPolicy(
         packageName: String,
         permissionName: String,