Merge "Use REVOKED_COMPAT instead of MODE_DEFAULT when syncing app ops."
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 7953c50..fc8d520 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -1302,7 +1302,7 @@
         }
 
         if (bp.isSoftRestricted() && !SoftRestrictedPermissionPolicy.forPermission(mContext,
-                pkg, UserHandle.of(userId), permName).canBeGranted()) {
+                pkg.toAppInfo(), UserHandle.of(userId), permName).mayGrantPermission()) {
             Log.e(TAG, "Cannot grant soft restricted permission " + permName + " for package "
                     + packageName);
             return;
@@ -3361,6 +3361,9 @@
         final int immutableFlags = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED
                 | PackageManager.FLAG_PERMISSION_POLICY_FIXED;
 
+        final int compatFlags = PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED
+                | PackageManager.FLAG_PERMISSION_REVOKED_COMPAT;
+
         final boolean supportsRuntimePermissions = pkg.getTargetSdkVersion()
                 >= Build.VERSION_CODES.M;
 
@@ -3384,12 +3387,11 @@
                                 callingUid, userId, callback);
                     }
                 } else {
-                    // In permission review mode we clear the review flag when we
-                    // are asked to install the app with all permissions granted.
-                    if ((flags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
-                        updatePermissionFlagsInternal(permission, pkg.getPackageName(),
-                                PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED, 0, callingUid,
-                                userId, false, callback);
+                    // In permission review mode we clear the review flag and the revoked compat
+                    // flag when we are asked to install the app with all permissions granted.
+                    if ((flags & compatFlags) != 0) {
+                        updatePermissionFlagsInternal(permission, pkg.getPackageName(), compatFlags,
+                                0, callingUid, userId, false, callback);
                     }
                 }
             }
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index a98de89..2f66713 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -17,13 +17,12 @@
 package com.android.server.policy;
 
 import static android.app.AppOpsManager.MODE_ALLOWED;
-import static android.app.AppOpsManager.MODE_DEFAULT;
-import static android.app.AppOpsManager.MODE_ERRORED;
 import static android.app.AppOpsManager.MODE_FOREGROUND;
 import static android.app.AppOpsManager.MODE_IGNORED;
 import static android.app.AppOpsManager.OP_NONE;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKED_COMPAT;
 import static android.content.pm.PackageManager.GET_PERMISSIONS;
 
 import android.annotation.NonNull;
@@ -157,14 +156,12 @@
                     appOpsService.startWatchingMode(getSwitchOp(perm.name), null, appOpsListener);
                 } else if (perm.isSoftRestricted()) {
                     appOpsService.startWatchingMode(getSwitchOp(perm.name), null, appOpsListener);
-
                     SoftRestrictedPermissionPolicy policy =
-                            SoftRestrictedPermissionPolicy.forPermission(null,
-                                    (AndroidPackage) null, null,
+                            SoftRestrictedPermissionPolicy.forPermission(null, null, null,
                                     perm.name);
-                    if (policy.resolveAppOp() != OP_NONE) {
-                        appOpsService.startWatchingMode(policy.resolveAppOp(), null,
-                                appOpsListener);
+                    int extraAppOp = policy.getExtraAppOpCode();
+                    if (extraAppOp != OP_NONE) {
+                        appOpsService.startWatchingMode(extraAppOp, null, appOpsListener);
                     }
                 }
             }
@@ -397,24 +394,6 @@
         private final @NonNull SparseIntArray mAllUids = new SparseIntArray();
 
         /**
-         * All ops that need to be set to default
-         *
-         * Currently, only used by the restricted permissions logic.
-         *
-         * @see #syncPackages
-         */
-        private final @NonNull ArrayList<OpToChange> mOpsToDefault = new ArrayList<>();
-
-        /**
-         * All ops that need to be flipped to allow if default.
-         *
-         * Currently, only used by the restricted permissions logic.
-         *
-         * @see #syncPackages
-         */
-        private final @NonNull ArrayList<OpToChange> mOpsToAllowIfDefault = new ArrayList<>();
-
-        /**
          * All ops that need to be flipped to allow.
          *
          * @see #syncPackages
@@ -422,15 +401,6 @@
         private final @NonNull ArrayList<OpToChange> mOpsToAllow = new ArrayList<>();
 
         /**
-         * All ops that need to be flipped to ignore if default.
-         *
-         * Currently, only used by the restricted permissions logic.
-         *
-         * @see #syncPackages
-         */
-        private final @NonNull ArrayList<OpToChange> mOpsToIgnoreIfDefault = new ArrayList<>();
-
-        /**
          * All ops that need to be flipped to ignore.
          *
          * @see #syncPackages
@@ -438,6 +408,15 @@
         private final @NonNull ArrayList<OpToChange> mOpsToIgnore = new ArrayList<>();
 
         /**
+         * All ops that need to be flipped to ignore if not allowed.
+         *
+         * Currently, only used by soft restricted permissions logic.
+         *
+         * @see #syncPackages
+         */
+        private final @NonNull ArrayList<OpToChange> mOpsToIgnoreIfNotAllowed = new ArrayList<>();
+
+        /**
          * All ops that need to be flipped to foreground.
          *
          * Currently, only used by the foreground/background permissions logic.
@@ -481,19 +460,6 @@
                 alreadySetAppOps.put(IntPair.of(op.uid, op.code), 1);
             }
 
-            final int allowIfDefaultCount = mOpsToAllowIfDefault.size();
-            for (int i = 0; i < allowIfDefaultCount; i++) {
-                final OpToChange op = mOpsToAllowIfDefault.get(i);
-                if (alreadySetAppOps.indexOfKey(IntPair.of(op.uid, op.code)) >= 0) {
-                    continue;
-                }
-
-                boolean wasSet = setUidModeAllowedIfDefault(op.code, op.uid, op.packageName);
-                if (wasSet) {
-                    alreadySetAppOps.put(IntPair.of(op.uid, op.code), 1);
-                }
-            }
-
             final int foregroundIfAllowedCount = mOpsToForegroundIfAllow.size();
             for (int i = 0; i < foregroundIfAllowedCount; i++) {
                 final OpToChange op = mOpsToForegroundIfAllow.get(i);
@@ -529,29 +495,18 @@
                 alreadySetAppOps.put(IntPair.of(op.uid, op.code), 1);
             }
 
-            final int ignoreIfDefaultCount = mOpsToIgnoreIfDefault.size();
-            for (int i = 0; i < ignoreIfDefaultCount; i++) {
-                final OpToChange op = mOpsToIgnoreIfDefault.get(i);
+            final int ignoreIfNotAllowedCount = mOpsToIgnoreIfNotAllowed.size();
+            for (int i = 0; i < ignoreIfNotAllowedCount; i++) {
+                final OpToChange op = mOpsToIgnoreIfNotAllowed.get(i);
                 if (alreadySetAppOps.indexOfKey(IntPair.of(op.uid, op.code)) >= 0) {
                     continue;
                 }
 
-                boolean wasSet = setUidModeIgnoredIfDefault(op.code, op.uid, op.packageName);
+                boolean wasSet = setUidModeIgnoredIfNotAllowed(op.code, op.uid, op.packageName);
                 if (wasSet) {
                     alreadySetAppOps.put(IntPair.of(op.uid, op.code), 1);
                 }
             }
-
-            final int defaultCount = mOpsToDefault.size();
-            for (int i = 0; i < defaultCount; i++) {
-                final OpToChange op = mOpsToDefault.get(i);
-                if (alreadySetAppOps.indexOfKey(IntPair.of(op.uid, op.code)) >= 0) {
-                    continue;
-                }
-
-                setUidModeDefault(op.code, op.uid, op.packageName);
-                alreadySetAppOps.put(IntPair.of(op.uid, op.code), 1);
-            }
         }
 
         /**
@@ -573,60 +528,49 @@
                 return;
             }
 
-            final boolean applyRestriction =
-                    (mPackageManager.getPermissionFlags(permission, pkg.packageName,
-                    mContext.getUser()) & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;
-
-            if (permissionInfo.isHardRestricted()) {
-                if (opCode != OP_NONE) {
-                    if (applyRestriction) {
-                        mOpsToDefault.add(new OpToChange(uid, pkg.packageName, opCode));
-                    } else {
-                        mOpsToAllowIfDefault.add(new OpToChange(uid, pkg.packageName, opCode));
+            if (opCode != OP_NONE) {
+                int permissionFlags = mPackageManager.getPermissionFlags(permission,
+                        pkg.packageName, mContext.getUser());
+                boolean isReviewRequired = (permissionFlags & FLAG_PERMISSION_REVIEW_REQUIRED) != 0;
+                if (!isReviewRequired) {
+                    boolean isRevokedCompat =
+                            (permissionFlags & FLAG_PERMISSION_REVOKED_COMPAT) != 0;
+                    if (permissionInfo.isHardRestricted()) {
+                        boolean shouldApplyRestriction =
+                                (permissionFlags & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;
+                        if (isRevokedCompat || shouldApplyRestriction) {
+                            mOpsToIgnore.add(new OpToChange(uid, pkg.packageName, opCode));
+                        } else {
+                            mOpsToAllow.add(new OpToChange(uid, pkg.packageName, opCode));
+                        }
+                    } else if (permissionInfo.isSoftRestricted()) {
+                        SoftRestrictedPermissionPolicy policy =
+                                SoftRestrictedPermissionPolicy.forPermission(mContext,
+                                        pkg.applicationInfo, mContext.getUser(), permission);
+                        if (!isRevokedCompat && policy.mayGrantPermission()) {
+                            mOpsToAllow.add(new OpToChange(uid, pkg.packageName, opCode));
+                        } else {
+                            mOpsToIgnore.add(new OpToChange(uid, pkg.packageName, opCode));
+                        }
                     }
                 }
-            } else if (permissionInfo.isSoftRestricted()) {
-                final SoftRestrictedPermissionPolicy policy =
+            }
+
+            if (permissionInfo.isSoftRestricted()) {
+                SoftRestrictedPermissionPolicy policy =
                         SoftRestrictedPermissionPolicy.forPermission(mContext, pkg.applicationInfo,
                                 mContext.getUser(), permission);
-
-                if (opCode != OP_NONE) {
-                    if (policy.canBeGranted()) {
-                        mOpsToAllowIfDefault.add(new OpToChange(uid, pkg.packageName, opCode));
+                int extraOpCode = policy.getExtraAppOpCode();
+                if (extraOpCode != OP_NONE) {
+                    if (policy.mayAllowExtraAppOp()) {
+                        mOpsToAllow.add(new OpToChange(uid, pkg.packageName, extraOpCode));
                     } else {
-                        mOpsToDefault.add(new OpToChange(uid, pkg.packageName, opCode));
-                    }
-                }
-
-                final int op = policy.resolveAppOp();
-                if (op != OP_NONE) {
-                    switch (policy.getDesiredOpMode()) {
-                        case MODE_DEFAULT:
-                            mOpsToDefault.add(new OpToChange(uid, pkg.packageName, op));
-                            break;
-                        case MODE_ALLOWED:
-                            if (policy.shouldSetAppOpIfNotDefault()) {
-                                mOpsToAllow.add(new OpToChange(uid, pkg.packageName, op));
-                            } else {
-                                mOpsToAllowIfDefault.add(
-                                        new OpToChange(uid, pkg.packageName, op));
-                            }
-                            break;
-                        case MODE_FOREGROUND:
-                            Slog.wtf(LOG_TAG,
-                                    "Setting appop to foreground is not implemented");
-                            break;
-                        case MODE_IGNORED:
-                            if (policy.shouldSetAppOpIfNotDefault()) {
-                                mOpsToIgnore.add(new OpToChange(uid, pkg.packageName, op));
-                            } else {
-                                mOpsToIgnoreIfDefault.add(
-                                        new OpToChange(uid, pkg.packageName,
-                                                op));
-                            }
-                            break;
-                        case MODE_ERRORED:
-                            Slog.wtf(LOG_TAG, "Setting appop to errored is not implemented");
+                        if (policy.mayDenyExtraAppOpIfGranted()) {
+                            mOpsToIgnore.add(new OpToChange(uid, pkg.packageName, extraOpCode));
+                        } else {
+                            mOpsToIgnoreIfNotAllowed.add(new OpToChange(uid, pkg.packageName,
+                                    extraOpCode));
+                        }
                     }
                 }
             }
@@ -745,60 +689,51 @@
             }
         }
 
-        private boolean setUidModeAllowedIfDefault(int opCode, int uid,
-                @NonNull String packageName) {
-            return setUidModeIfMode(opCode, uid, MODE_DEFAULT, MODE_ALLOWED, packageName);
-        }
-
         private void setUidModeAllowed(int opCode, int uid, @NonNull String packageName) {
             setUidMode(opCode, uid, MODE_ALLOWED, packageName);
         }
 
         private boolean setUidModeForegroundIfAllow(int opCode, int uid,
                 @NonNull String packageName) {
-            return setUidModeIfMode(opCode, uid, MODE_ALLOWED, MODE_FOREGROUND, packageName);
+            final int currentMode = mAppOpsManager.unsafeCheckOpRaw(AppOpsManager.opToPublicName(
+                    opCode), uid, packageName);
+            if (currentMode == MODE_ALLOWED) {
+                mAppOpsManager.setUidMode(opCode, uid, MODE_FOREGROUND);
+                return true;
+            }
+            return false;
         }
 
         private void setUidModeForeground(int opCode, int uid, @NonNull String packageName) {
             setUidMode(opCode, uid, MODE_FOREGROUND, packageName);
         }
 
-        private boolean setUidModeIgnoredIfDefault(int opCode, int uid,
-                @NonNull String packageName) {
-            return setUidModeIfMode(opCode, uid, MODE_DEFAULT, MODE_IGNORED, packageName);
-        }
-
         private void setUidModeIgnored(int opCode, int uid, @NonNull String packageName) {
             setUidMode(opCode, uid, MODE_IGNORED, packageName);
         }
 
+        private boolean setUidModeIgnoredIfNotAllowed(int opCode, int uid,
+                @NonNull String packageName) {
+            final int currentMode = mAppOpsManager.unsafeCheckOpRaw(AppOpsManager.opToPublicName(
+                    opCode), uid, packageName);
+            if (currentMode != MODE_ALLOWED) {
+                if (currentMode != MODE_IGNORED) {
+                    mAppOpsManager.setUidMode(opCode, uid, MODE_IGNORED);
+                }
+                return true;
+            }
+            return false;
+        }
+
         private void setUidMode(int opCode, int uid, int mode,
                 @NonNull String packageName) {
             final int currentMode = mAppOpsManager.unsafeCheckOpRaw(AppOpsManager
                     .opToPublicName(opCode), uid, packageName);
-
             if (currentMode != mode) {
                 mAppOpsManager.setUidMode(opCode, uid, mode);
             }
         }
 
-        private boolean setUidModeIfMode(int opCode, int uid, int requiredModeBefore, int newMode,
-                @NonNull String packageName) {
-            final int currentMode = mAppOpsManager.unsafeCheckOpRaw(AppOpsManager
-                    .opToPublicName(opCode), uid, packageName);
-
-            if (currentMode == requiredModeBefore) {
-                mAppOpsManager.setUidMode(opCode, uid, newMode);
-                return true;
-            }
-
-            return false;
-        }
-
-        private void setUidModeDefault(int opCode, int uid, String packageName) {
-            setUidMode(opCode, uid, MODE_DEFAULT, packageName);
-        }
-
         private class OpToChange {
             final int uid;
             final @NonNull String packageName;
diff --git a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
index 34c9258..b0f22e4 100644
--- a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
+++ b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
@@ -18,9 +18,6 @@
 
 import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
 import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
-import static android.app.AppOpsManager.MODE_ALLOWED;
-import static android.app.AppOpsManager.MODE_DEFAULT;
-import static android.app.AppOpsManager.MODE_IGNORED;
 import static android.app.AppOpsManager.OP_LEGACY_STORAGE;
 import static android.app.AppOpsManager.OP_NONE;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION;
@@ -36,7 +33,6 @@
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.parsing.AndroidPackage;
 import android.os.Build;
 import android.os.UserHandle;
 
@@ -56,22 +52,7 @@
     private static final SoftRestrictedPermissionPolicy DUMMY_POLICY =
             new SoftRestrictedPermissionPolicy() {
                 @Override
-                public int resolveAppOp() {
-                    return OP_NONE;
-                }
-
-                @Override
-                public int getDesiredOpMode() {
-                    return MODE_DEFAULT;
-                }
-
-                @Override
-                public boolean shouldSetAppOpIfNotDefault() {
-                    return false;
-                }
-
-                @Override
-                public boolean canBeGranted() {
+                public boolean mayGrantPermission() {
                     return true;
                 }
             };
@@ -115,10 +96,8 @@
      * Get the policy for a soft restricted permission.
      *
      * @param context A context to use
-     * @param appInfo The application the permission belongs to. Can be {@code null}, but then
-     *                only {@link #resolveAppOp} will work.
-     * @param user The user the app belongs to. Can be {@code null}, but then only
-     *             {@link #resolveAppOp} will work.
+     * @param appInfo The application the permission belongs to.
+     * @param user The user the app belongs to.
      * @param permission The name of the permission
      *
      * @return The policy for this permission
@@ -131,82 +110,46 @@
             // where the restricted state allows the permission but only for accessing the medial
             // collections.
             case READ_EXTERNAL_STORAGE: {
-                final int flags;
-                final boolean applyRestriction;
                 final boolean isWhiteListed;
-                final boolean hasRequestedLegacyExternalStorage;
+                boolean shouldApplyRestriction;
                 final int targetSDK;
+                final boolean hasRequestedLegacyExternalStorage;
 
                 if (appInfo != null) {
                     PackageManager pm = context.getPackageManager();
-                    flags = pm.getPermissionFlags(permission, appInfo.packageName, user);
-                    applyRestriction = (flags & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;
+                    int flags = pm.getPermissionFlags(permission, appInfo.packageName, user);
                     isWhiteListed = (flags & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0;
+                    shouldApplyRestriction = (flags & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;
                     targetSDK = getMinimumTargetSDK(context, appInfo, user);
-
-                    boolean hasAnyRequestedLegacyExternalStorage =
-                            appInfo.hasRequestedLegacyExternalStorage();
-
-                    // hasRequestedLegacyExternalStorage is per package. To make sure two apps in
-                    // the same shared UID do not fight over what to set, always compute the
-                    // combined hasRequestedLegacyExternalStorage
-                    String[] uidPkgs = pm.getPackagesForUid(appInfo.uid);
-                    if (uidPkgs != null) {
-                        for (String uidPkg : uidPkgs) {
-                            if (!uidPkg.equals(appInfo.packageName)) {
-                                ApplicationInfo uidPkgInfo;
-                                try {
-                                    uidPkgInfo = pm.getApplicationInfoAsUser(uidPkg, 0, user);
-                                } catch (PackageManager.NameNotFoundException e) {
-                                    continue;
-                                }
-
-                                hasAnyRequestedLegacyExternalStorage |=
-                                        uidPkgInfo.hasRequestedLegacyExternalStorage();
-                            }
-                        }
-                    }
-
-                    hasRequestedLegacyExternalStorage = hasAnyRequestedLegacyExternalStorage;
+                    hasRequestedLegacyExternalStorage = hasUidRequestedLegacyExternalStorage(
+                            appInfo.uid, context);
                 } else {
-                    flags = 0;
-                    applyRestriction = false;
                     isWhiteListed = false;
-                    hasRequestedLegacyExternalStorage = false;
+                    shouldApplyRestriction = false;
                     targetSDK = 0;
+                    hasRequestedLegacyExternalStorage = false;
                 }
 
+                // We have a check in PermissionPolicyService.PermissionToOpSynchroniser.setUidMode
+                // to prevent apps losing files in legacy storage, because we are holding the
+                // package manager lock here. If we ever remove this policy that check should be
+                // removed as well.
                 return new SoftRestrictedPermissionPolicy() {
                     @Override
-                    public int resolveAppOp() {
+                    public boolean mayGrantPermission() {
+                        return isWhiteListed || targetSDK >= Build.VERSION_CODES.Q;
+                    }
+                    @Override
+                    public int getExtraAppOpCode() {
                         return OP_LEGACY_STORAGE;
                     }
-
                     @Override
-                    public int getDesiredOpMode() {
-                        if (applyRestriction) {
-                            return MODE_DEFAULT;
-                        } else if (hasRequestedLegacyExternalStorage) {
-                            return MODE_ALLOWED;
-                        } else {
-                            return MODE_IGNORED;
-                        }
+                    public boolean mayAllowExtraAppOp() {
+                        return !shouldApplyRestriction && hasRequestedLegacyExternalStorage;
                     }
-
                     @Override
-                    public boolean shouldSetAppOpIfNotDefault() {
-                        // Do not switch from allowed -> ignored as this would mean to retroactively
-                        // turn on isolated storage. This will make the app loose all its files.
-                        return getDesiredOpMode() != MODE_IGNORED;
-                    }
-
-                    @Override
-                    public boolean canBeGranted() {
-                        if (isWhiteListed || targetSDK >= Build.VERSION_CODES.Q) {
-                            return true;
-                        } else {
-                            return false;
-                        }
+                    public boolean mayDenyExtraAppOpIfGranted() {
+                        return shouldApplyRestriction;
                     }
                 };
             }
@@ -226,22 +169,7 @@
 
                 return new SoftRestrictedPermissionPolicy() {
                     @Override
-                    public int resolveAppOp() {
-                        return OP_NONE;
-                    }
-
-                    @Override
-                    public int getDesiredOpMode() {
-                        return MODE_DEFAULT;
-                    }
-
-                    @Override
-                    public boolean shouldSetAppOpIfNotDefault() {
-                        return false;
-                    }
-
-                    @Override
-                    public boolean canBeGranted() {
+                    public boolean mayGrantPermission() {
                         return isWhiteListed || targetSDK >= Build.VERSION_CODES.Q;
                     }
                 };
@@ -251,106 +179,51 @@
         }
     }
 
-    /**
-     * Get the policy for a soft restricted permission.
-     *
-     * @param context A context to use
-     * @param pkg The application the permission belongs to. Can be {@code null}, but then
-     *                only {@link #resolveAppOp} will work.
-     * @param user The user the app belongs to. Can be {@code null}, but then only
-     *             {@link #resolveAppOp} will work.
-     * @param permission The name of the permission
-     *
-     * @return The policy for this permission
-     */
-    public static @NonNull SoftRestrictedPermissionPolicy forPermission(@NonNull Context context,
-            @Nullable AndroidPackage pkg, @Nullable UserHandle user,
-            @NonNull String permission) {
-        switch (permission) {
-            // Storage uses a special app op to decide the mount state and supports soft restriction
-            // where the restricted state allows the permission but only for accessing the medial
-            // collections.
-            case READ_EXTERNAL_STORAGE:
-            case WRITE_EXTERNAL_STORAGE: {
-                final int flags;
-                final boolean applyRestriction;
-                final boolean isWhiteListed;
-                final boolean hasRequestedLegacyExternalStorage;
-                final int targetSDK;
-
-                if (pkg != null) {
-                    flags = context.getPackageManager().getPermissionFlags(permission,
-                            pkg.getPackageName(), user);
-                    applyRestriction = (flags & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;
-                    isWhiteListed = (flags & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0;
-                    hasRequestedLegacyExternalStorage = pkg.hasRequestedLegacyExternalStorage();
-                    targetSDK = pkg.getTargetSdkVersion();
-                } else {
-                    flags = 0;
-                    applyRestriction = false;
-                    isWhiteListed = false;
-                    hasRequestedLegacyExternalStorage = false;
-                    targetSDK = 0;
-                }
-
-                return new SoftRestrictedPermissionPolicy() {
-                    @Override
-                    public int resolveAppOp() {
-                        return OP_LEGACY_STORAGE;
-                    }
-
-                    @Override
-                    public int getDesiredOpMode() {
-                        if (applyRestriction) {
-                            return MODE_DEFAULT;
-                        } else if (hasRequestedLegacyExternalStorage) {
-                            return MODE_ALLOWED;
-                        } else {
-                            return MODE_IGNORED;
-                        }
-                    }
-
-                    @Override
-                    public boolean shouldSetAppOpIfNotDefault() {
-                        // Do not switch from allowed -> ignored as this would mean to retroactively
-                        // turn on isolated storage. This will make the app loose all its files.
-                        return getDesiredOpMode() != MODE_IGNORED;
-                    }
-
-                    @Override
-                    public boolean canBeGranted() {
-                        if (isWhiteListed || targetSDK >= Build.VERSION_CODES.Q) {
-                            return true;
-                        } else {
-                            return false;
-                        }
-                    }
-                };
-            }
-            default:
-                return DUMMY_POLICY;
+    private static boolean hasUidRequestedLegacyExternalStorage(int uid, @NonNull Context context) {
+        PackageManager packageManager = context.getPackageManager();
+        String[] packageNames = packageManager.getPackagesForUid(uid);
+        if (packageNames == null) {
+            return false;
         }
+        UserHandle user = UserHandle.getUserHandleForUid(uid);
+        for (String packageName : packageNames) {
+            ApplicationInfo applicationInfo;
+            try {
+                applicationInfo = packageManager.getApplicationInfoAsUser(packageName, 0, user);
+            } catch (PackageManager.NameNotFoundException e) {
+                continue;
+            }
+            if (applicationInfo.hasRequestedLegacyExternalStorage()) {
+                return true;
+            }
+        }
+        return false;
     }
 
     /**
+     * @return If the permission can be granted
+     */
+    public abstract boolean mayGrantPermission();
+
+    /**
      * @return An app op to be changed based on the state of the permission or
      * {@link AppOpsManager#OP_NONE} if not app-op should be set.
      */
-    public abstract int resolveAppOp();
+    public int getExtraAppOpCode() {
+        return OP_NONE;
+    }
 
     /**
-     * @return The mode the {@link #resolveAppOp() app op} should be in.
+     * @return Whether the {@link #getExtraAppOpCode() app op} may be granted.
      */
-    public abstract @AppOpsManager.Mode int getDesiredOpMode();
+    public boolean mayAllowExtraAppOp() {
+        return false;
+    }
 
     /**
-     * @return If the {@link #resolveAppOp() app op} should be set even if the app-op is currently
-     * not {@link AppOpsManager#MODE_DEFAULT}.
+     * @return Whether the {@link #getExtraAppOpCode() app op} may be denied if was granted.
      */
-    public abstract boolean shouldSetAppOpIfNotDefault();
-
-    /**
-     * @return If the permission can be granted
-     */
-    public abstract boolean canBeGranted();
+    public boolean mayDenyExtraAppOpIfGranted() {
+        return false;
+    }
 }