(P) Revoke permision when group changed

If a run time permission of a group is already granted we grant the
other permission of the group automatically when requested.

Hence if an already granted permission changed its group during an
update suddenly permission of a potentially not approved group will
get auto-granted.

This is undesirable, hence we revoke the permission during the update
process.

Test: atest android.permission.cts.PermissionGroupChange
Fixes: 72710897
Change-Id: Ib2165d1ae53b80455ebe02e07775853e37a2e339
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 f5b52fc..f659225 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -51,6 +51,7 @@
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.EventLog;
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -391,6 +392,82 @@
         return protectionLevel;
     }
 
+    /**
+     * We might auto-grant permissions if any permission of the group is already granted. Hence if
+     * the group of a granted permission changes we need to revoke it to avoid having permissions of
+     * the new group auto-granted.
+     *
+     * @param newPackage The new package that was installed
+     * @param oldPackage The old package that was updated
+     * @param allPackageNames All package names
+     * @param permissionCallback Callback for permission changed
+     */
+    private void revokeRuntimePermissionsIfGroupChanged(
+            @NonNull PackageParser.Package newPackage,
+            @NonNull PackageParser.Package oldPackage,
+            @NonNull ArrayList<String> allPackageNames,
+            @NonNull PermissionCallback permissionCallback) {
+        final int numOldPackagePermissions = oldPackage.permissions.size();
+        final ArrayMap<String, String> oldPermissionNameToGroupName
+                = new ArrayMap<>(numOldPackagePermissions);
+
+        for (int i = 0; i < numOldPackagePermissions; i++) {
+            final PackageParser.Permission permission = oldPackage.permissions.get(i);
+
+            if (permission.group != null) {
+                oldPermissionNameToGroupName.put(permission.info.name,
+                        permission.group.info.name);
+            }
+        }
+
+        final int numNewPackagePermissions = newPackage.permissions.size();
+        for (int newPermissionNum = 0; newPermissionNum < numNewPackagePermissions;
+                newPermissionNum++) {
+            final PackageParser.Permission newPermission =
+                    newPackage.permissions.get(newPermissionNum);
+            final int newProtection = newPermission.info.getProtection();
+
+            if ((newProtection & PermissionInfo.PROTECTION_DANGEROUS) != 0) {
+                final String permissionName = newPermission.info.name;
+                final String newPermissionGroupName =
+                        newPermission.group == null ? null : newPermission.group.info.name;
+                final String oldPermissionGroupName = oldPermissionNameToGroupName.get(
+                        permissionName);
+
+                if (newPermissionGroupName != null
+                        && !newPermissionGroupName.equals(oldPermissionGroupName)) {
+                    final int[] userIds = mUserManagerInt.getUserIds();
+                    final int numUserIds = userIds.length;
+                    for (int userIdNum = 0; userIdNum < numUserIds; userIdNum++) {
+                        final int userId = userIds[userIdNum];
+
+                        final int numPackages = allPackageNames.size();
+                        for (int packageNum = 0; packageNum < numPackages; packageNum++) {
+                            final String packageName = allPackageNames.get(packageNum);
+
+                            if (checkPermission(permissionName, packageName, UserHandle.USER_SYSTEM,
+                                    userId) == PackageManager.PERMISSION_GRANTED) {
+                                EventLog.writeEvent(0x534e4554, "72710897",
+                                        newPackage.applicationInfo.uid,
+                                        "Revoking permission", permissionName, "from package",
+                                        packageName, "as the group changed from",
+                                        oldPermissionGroupName, "to", newPermissionGroupName);
+
+                                try {
+                                    revokeRuntimePermission(permissionName, packageName, false,
+                                            Process.SYSTEM_UID, userId, permissionCallback);
+                                } catch (IllegalArgumentException e) {
+                                    Slog.e(TAG, "Could not revoke " + permissionName + " from "
+                                            + packageName, e);
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
     private void addAllPermissions(PackageParser.Package pkg, boolean chatty) {
         final int N = pkg.permissions.size();
         for (int i=0; i<N; i++) {
@@ -1968,6 +2045,15 @@
             return PermissionManagerService.this.isPermissionsReviewRequired(pkg, userId);
         }
         @Override
+        public void revokeRuntimePermissionsIfGroupChanged(
+                @NonNull PackageParser.Package newPackage,
+                @NonNull PackageParser.Package oldPackage,
+                @NonNull ArrayList<String> allPackageNames,
+                @NonNull PermissionCallback permissionCallback) {
+            PermissionManagerService.this.revokeRuntimePermissionsIfGroupChanged(newPackage,
+                    oldPackage, allPackageNames, permissionCallback);
+        }
+        @Override
         public void addAllPermissions(Package pkg, boolean chatty) {
             PermissionManagerService.this.addAllPermissions(pkg, chatty);
         }