Loading services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java +105 −49 Original line number Diff line number Diff line Loading @@ -27,16 +27,22 @@ import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYST import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static java.lang.Integer.min; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AppOpsManager; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledAfter; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.os.Build; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; import android.util.Log; import com.android.internal.compat.IPlatformCompat; /** * The behavior of soft restricted permissions is different for each permission. This class collects Loading @@ -46,6 +52,27 @@ import android.os.UserHandle; * {@link com.android.packageinstaller.permission.utils.SoftRestrictedPermissionPolicy} */ public abstract class SoftRestrictedPermissionPolicy { /** * Enables scoped storage, with exceptions for apps that explicitly request legacy access, or * apps that hold the {@code android.Manifest.permission#WRITE_MEDIA_STORAGE} permission. * See https://developer.android.com/training/data-storage#scoped-storage for more information. */ @ChangeId // This change is enabled for apps with targetSDK > {@link android.os.Build.VERSION_CODES.P} @EnabledAfter(targetSdkVersion = android.os.Build.VERSION_CODES.P) static final long ENABLE_SCOPED_STORAGE = 144914977L; /** * Enforces scoped storage for all apps, preventing individual apps from opting out. This change * has precedence over {@code ENABLE_SCOPED_STORAGE}. */ @ChangeId // This change is enabled for apps with targetSDK > {@link android.os.Build.VERSION_CODES.Q}. @EnabledAfter(targetSdkVersion = android.os.Build.VERSION_CODES.Q) static final long REQUIRE_SCOPED_STORAGE = 131432978L; private static final String LOG_TAG = SoftRestrictedPermissionPolicy.class.getSimpleName(); private static final int FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT = FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT | FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT Loading @@ -59,41 +86,6 @@ public abstract class SoftRestrictedPermissionPolicy { } }; /** * TargetSDK is per package. To make sure two apps int the same shared UID do not fight over * what to set, always compute the combined targetSDK. * * @param context A context * @param appInfo The app that is changed * @param user The user the app belongs to * * @return The minimum targetSDK of all apps sharing the uid of the app */ private static int getMinimumTargetSDK(@NonNull Context context, @NonNull ApplicationInfo appInfo, @NonNull UserHandle user) { PackageManager pm = context.getPackageManager(); int minimumTargetSDK = appInfo.targetSdkVersion; 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; } minimumTargetSDK = min(minimumTargetSDK, uidPkgInfo.targetSdkVersion); } } } return minimumTargetSDK; } /** * Get the policy for a soft restricted permission. * Loading @@ -114,27 +106,31 @@ public abstract class SoftRestrictedPermissionPolicy { case READ_EXTERNAL_STORAGE: { final boolean isWhiteListed; boolean shouldApplyRestriction; final int targetSDK; final boolean hasRequestedLegacyExternalStorage; final boolean hasWriteMediaStorageGrantedForUid; final boolean isScopedStorageEnabled; if (appInfo != null) { PackageManager pm = context.getPackageManager(); int flags = pm.getPermissionFlags(permission, appInfo.packageName, user); isWhiteListed = (flags & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0; targetSDK = getMinimumTargetSDK(context, appInfo, user); shouldApplyRestriction = (flags & FLAG_PERMISSION_APPLY_RESTRICTION) != 0 || targetSDK > Build.VERSION_CODES.Q; hasRequestedLegacyExternalStorage = hasUidRequestedLegacyExternalStorage( appInfo.uid, context); hasWriteMediaStorageGrantedForUid = hasWriteMediaStorageGrantedForUid( appInfo.uid, context); final boolean isScopedStorageRequired = isChangeEnabledForUid(context, appInfo, user, REQUIRE_SCOPED_STORAGE); isScopedStorageEnabled = isChangeEnabledForUid(context, appInfo, user, ENABLE_SCOPED_STORAGE) || isScopedStorageRequired; shouldApplyRestriction = (flags & FLAG_PERMISSION_APPLY_RESTRICTION) != 0 || isScopedStorageRequired; } else { isWhiteListed = false; shouldApplyRestriction = false; targetSDK = 0; hasRequestedLegacyExternalStorage = false; hasWriteMediaStorageGrantedForUid = false; isScopedStorageEnabled = false; } // We have a check in PermissionPolicyService.PermissionToOpSynchroniser.setUidMode Loading @@ -144,7 +140,7 @@ public abstract class SoftRestrictedPermissionPolicy { return new SoftRestrictedPermissionPolicy() { @Override public boolean mayGrantPermission() { return isWhiteListed || targetSDK >= Build.VERSION_CODES.Q; return isWhiteListed || isScopedStorageEnabled; } @Override public int getExtraAppOpCode() { Loading @@ -152,7 +148,7 @@ public abstract class SoftRestrictedPermissionPolicy { } @Override public boolean mayAllowExtraAppOp() { return !shouldApplyRestriction && targetSDK <= Build.VERSION_CODES.Q return !shouldApplyRestriction && (hasRequestedLegacyExternalStorage || hasWriteMediaStorageGrantedForUid); } Loading @@ -164,22 +160,26 @@ public abstract class SoftRestrictedPermissionPolicy { } case WRITE_EXTERNAL_STORAGE: { final boolean isWhiteListed; final int targetSDK; final boolean isScopedStorageEnabled; if (appInfo != null) { final int flags = context.getPackageManager().getPermissionFlags(permission, appInfo.packageName, user); isWhiteListed = (flags & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0; targetSDK = getMinimumTargetSDK(context, appInfo, user); final boolean isScopedStorageRequired = isChangeEnabledForUid(context, appInfo, user, REQUIRE_SCOPED_STORAGE); isScopedStorageEnabled = isChangeEnabledForUid(context, appInfo, user, ENABLE_SCOPED_STORAGE) || isScopedStorageRequired; } else { isWhiteListed = false; targetSDK = 0; isScopedStorageEnabled = false; } return new SoftRestrictedPermissionPolicy() { @Override public boolean mayGrantPermission() { return isWhiteListed || targetSDK >= Build.VERSION_CODES.Q; return isWhiteListed || isScopedStorageEnabled; } }; } Loading @@ -188,6 +188,62 @@ public abstract class SoftRestrictedPermissionPolicy { } } /** * Checks whether an AppCompat change is enabled for all packages sharing a UID with the * provided application. * * @param context A context to use. * @param appInfo The application for which to check whether the compat change is enabled. * @param user The user the app belongs to. * @param changeId A {@link android.compat.annotation.ChangeId} corresponding to the change. * * @return true if this change is enabled for all apps sharing the UID of the provided app, * false otherwise. */ private static boolean isChangeEnabledForUid(@NonNull Context context, @NonNull ApplicationInfo appInfo, @NonNull UserHandle user, long changeId) { PackageManager pm = context.getPackageManager(); String[] uidPackages = pm.getPackagesForUid(appInfo.uid); if (uidPackages != null) { for (String uidPackage : uidPackages) { ApplicationInfo uidPackageInfo; try { uidPackageInfo = pm.getApplicationInfoAsUser(uidPackage, 0, user); } catch (PackageManager.NameNotFoundException e) { continue; } if (!isChangeEnabled(uidPackageInfo, changeId)) { // At least one package sharing this UID does not have this change enabled. return false; } } // All packages sharing this UID returned true for {@link #isChangeEnabled()}. return true; } else { Log.w(LOG_TAG, "Check for change " + changeId + " for uid " + appInfo.uid + " produced no packages. Defaulting to using the information for " + appInfo.packageName + " only."); return isChangeEnabled(appInfo, changeId); } } private static boolean isChangeEnabled(@NonNull ApplicationInfo appInfo, long changeId) { IBinder binder = ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE); IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface(binder); final long callingId = Binder.clearCallingIdentity(); try { return platformCompat.isChangeEnabled(changeId, appInfo); } catch (RemoteException e) { Log.e(LOG_TAG, "Check for change " + changeId + " failed. Defaulting to enabled.", e); return true; } finally { Binder.restoreCallingIdentity(callingId); } } private static boolean hasUidRequestedLegacyExternalStorage(int uid, @NonNull Context context) { PackageManager packageManager = context.getPackageManager(); String[] packageNames = packageManager.getPackagesForUid(uid); Loading Loading
services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java +105 −49 Original line number Diff line number Diff line Loading @@ -27,16 +27,22 @@ import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYST import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static java.lang.Integer.min; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AppOpsManager; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledAfter; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.os.Build; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; import android.util.Log; import com.android.internal.compat.IPlatformCompat; /** * The behavior of soft restricted permissions is different for each permission. This class collects Loading @@ -46,6 +52,27 @@ import android.os.UserHandle; * {@link com.android.packageinstaller.permission.utils.SoftRestrictedPermissionPolicy} */ public abstract class SoftRestrictedPermissionPolicy { /** * Enables scoped storage, with exceptions for apps that explicitly request legacy access, or * apps that hold the {@code android.Manifest.permission#WRITE_MEDIA_STORAGE} permission. * See https://developer.android.com/training/data-storage#scoped-storage for more information. */ @ChangeId // This change is enabled for apps with targetSDK > {@link android.os.Build.VERSION_CODES.P} @EnabledAfter(targetSdkVersion = android.os.Build.VERSION_CODES.P) static final long ENABLE_SCOPED_STORAGE = 144914977L; /** * Enforces scoped storage for all apps, preventing individual apps from opting out. This change * has precedence over {@code ENABLE_SCOPED_STORAGE}. */ @ChangeId // This change is enabled for apps with targetSDK > {@link android.os.Build.VERSION_CODES.Q}. @EnabledAfter(targetSdkVersion = android.os.Build.VERSION_CODES.Q) static final long REQUIRE_SCOPED_STORAGE = 131432978L; private static final String LOG_TAG = SoftRestrictedPermissionPolicy.class.getSimpleName(); private static final int FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT = FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT | FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT Loading @@ -59,41 +86,6 @@ public abstract class SoftRestrictedPermissionPolicy { } }; /** * TargetSDK is per package. To make sure two apps int the same shared UID do not fight over * what to set, always compute the combined targetSDK. * * @param context A context * @param appInfo The app that is changed * @param user The user the app belongs to * * @return The minimum targetSDK of all apps sharing the uid of the app */ private static int getMinimumTargetSDK(@NonNull Context context, @NonNull ApplicationInfo appInfo, @NonNull UserHandle user) { PackageManager pm = context.getPackageManager(); int minimumTargetSDK = appInfo.targetSdkVersion; 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; } minimumTargetSDK = min(minimumTargetSDK, uidPkgInfo.targetSdkVersion); } } } return minimumTargetSDK; } /** * Get the policy for a soft restricted permission. * Loading @@ -114,27 +106,31 @@ public abstract class SoftRestrictedPermissionPolicy { case READ_EXTERNAL_STORAGE: { final boolean isWhiteListed; boolean shouldApplyRestriction; final int targetSDK; final boolean hasRequestedLegacyExternalStorage; final boolean hasWriteMediaStorageGrantedForUid; final boolean isScopedStorageEnabled; if (appInfo != null) { PackageManager pm = context.getPackageManager(); int flags = pm.getPermissionFlags(permission, appInfo.packageName, user); isWhiteListed = (flags & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0; targetSDK = getMinimumTargetSDK(context, appInfo, user); shouldApplyRestriction = (flags & FLAG_PERMISSION_APPLY_RESTRICTION) != 0 || targetSDK > Build.VERSION_CODES.Q; hasRequestedLegacyExternalStorage = hasUidRequestedLegacyExternalStorage( appInfo.uid, context); hasWriteMediaStorageGrantedForUid = hasWriteMediaStorageGrantedForUid( appInfo.uid, context); final boolean isScopedStorageRequired = isChangeEnabledForUid(context, appInfo, user, REQUIRE_SCOPED_STORAGE); isScopedStorageEnabled = isChangeEnabledForUid(context, appInfo, user, ENABLE_SCOPED_STORAGE) || isScopedStorageRequired; shouldApplyRestriction = (flags & FLAG_PERMISSION_APPLY_RESTRICTION) != 0 || isScopedStorageRequired; } else { isWhiteListed = false; shouldApplyRestriction = false; targetSDK = 0; hasRequestedLegacyExternalStorage = false; hasWriteMediaStorageGrantedForUid = false; isScopedStorageEnabled = false; } // We have a check in PermissionPolicyService.PermissionToOpSynchroniser.setUidMode Loading @@ -144,7 +140,7 @@ public abstract class SoftRestrictedPermissionPolicy { return new SoftRestrictedPermissionPolicy() { @Override public boolean mayGrantPermission() { return isWhiteListed || targetSDK >= Build.VERSION_CODES.Q; return isWhiteListed || isScopedStorageEnabled; } @Override public int getExtraAppOpCode() { Loading @@ -152,7 +148,7 @@ public abstract class SoftRestrictedPermissionPolicy { } @Override public boolean mayAllowExtraAppOp() { return !shouldApplyRestriction && targetSDK <= Build.VERSION_CODES.Q return !shouldApplyRestriction && (hasRequestedLegacyExternalStorage || hasWriteMediaStorageGrantedForUid); } Loading @@ -164,22 +160,26 @@ public abstract class SoftRestrictedPermissionPolicy { } case WRITE_EXTERNAL_STORAGE: { final boolean isWhiteListed; final int targetSDK; final boolean isScopedStorageEnabled; if (appInfo != null) { final int flags = context.getPackageManager().getPermissionFlags(permission, appInfo.packageName, user); isWhiteListed = (flags & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0; targetSDK = getMinimumTargetSDK(context, appInfo, user); final boolean isScopedStorageRequired = isChangeEnabledForUid(context, appInfo, user, REQUIRE_SCOPED_STORAGE); isScopedStorageEnabled = isChangeEnabledForUid(context, appInfo, user, ENABLE_SCOPED_STORAGE) || isScopedStorageRequired; } else { isWhiteListed = false; targetSDK = 0; isScopedStorageEnabled = false; } return new SoftRestrictedPermissionPolicy() { @Override public boolean mayGrantPermission() { return isWhiteListed || targetSDK >= Build.VERSION_CODES.Q; return isWhiteListed || isScopedStorageEnabled; } }; } Loading @@ -188,6 +188,62 @@ public abstract class SoftRestrictedPermissionPolicy { } } /** * Checks whether an AppCompat change is enabled for all packages sharing a UID with the * provided application. * * @param context A context to use. * @param appInfo The application for which to check whether the compat change is enabled. * @param user The user the app belongs to. * @param changeId A {@link android.compat.annotation.ChangeId} corresponding to the change. * * @return true if this change is enabled for all apps sharing the UID of the provided app, * false otherwise. */ private static boolean isChangeEnabledForUid(@NonNull Context context, @NonNull ApplicationInfo appInfo, @NonNull UserHandle user, long changeId) { PackageManager pm = context.getPackageManager(); String[] uidPackages = pm.getPackagesForUid(appInfo.uid); if (uidPackages != null) { for (String uidPackage : uidPackages) { ApplicationInfo uidPackageInfo; try { uidPackageInfo = pm.getApplicationInfoAsUser(uidPackage, 0, user); } catch (PackageManager.NameNotFoundException e) { continue; } if (!isChangeEnabled(uidPackageInfo, changeId)) { // At least one package sharing this UID does not have this change enabled. return false; } } // All packages sharing this UID returned true for {@link #isChangeEnabled()}. return true; } else { Log.w(LOG_TAG, "Check for change " + changeId + " for uid " + appInfo.uid + " produced no packages. Defaulting to using the information for " + appInfo.packageName + " only."); return isChangeEnabled(appInfo, changeId); } } private static boolean isChangeEnabled(@NonNull ApplicationInfo appInfo, long changeId) { IBinder binder = ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE); IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface(binder); final long callingId = Binder.clearCallingIdentity(); try { return platformCompat.isChangeEnabled(changeId, appInfo); } catch (RemoteException e) { Log.e(LOG_TAG, "Check for change " + changeId + " failed. Defaulting to enabled.", e); return true; } finally { Binder.restoreCallingIdentity(callingId); } } private static boolean hasUidRequestedLegacyExternalStorage(int uid, @NonNull Context context) { PackageManager packageManager = context.getPackageManager(); String[] packageNames = packageManager.getPackagesForUid(uid); Loading