diff --git a/core/api/system-current.txt b/core/api/system-current.txt index c3bf05813b900..90257aaeb7af9 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -2291,6 +2291,7 @@ package android.content.pm { field public static final int PROTECTION_FLAG_OEM = 16384; // 0x4000 field public static final int PROTECTION_FLAG_RECENTS = 33554432; // 0x2000000 field public static final int PROTECTION_FLAG_RETAIL_DEMO = 16777216; // 0x1000000 + field public static final int PROTECTION_FLAG_ROLE = 67108864; // 0x4000000 field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000 field public static final int PROTECTION_FLAG_WELLBEING = 131072; // 0x20000 field @Nullable public final String backgroundPermission; diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java index cd9ba6a5b4516..e59a8383f344c 100644 --- a/core/java/android/content/pm/PermissionInfo.java +++ b/core/java/android/content/pm/PermissionInfo.java @@ -261,6 +261,15 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { @SystemApi public static final int PROTECTION_FLAG_RECENTS = 0x2000000; + /** + * Additional flag for {@link #protectionLevel}, corresponding to the role value of + * {@link android.R.attr#protectionLevel}. + * + * @hide + */ + @SystemApi + public static final int PROTECTION_FLAG_ROLE = 0x4000000; + /** @hide */ @IntDef(flag = true, prefix = { "PROTECTION_FLAG_" }, value = { PROTECTION_FLAG_PRIVILEGED, @@ -285,6 +294,7 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { PROTECTION_FLAG_COMPANION, PROTECTION_FLAG_RETAIL_DEMO, PROTECTION_FLAG_RECENTS, + PROTECTION_FLAG_ROLE, }) @Retention(RetentionPolicy.SOURCE) public @interface ProtectionFlags {} @@ -546,6 +556,9 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { if ((level & PermissionInfo.PROTECTION_FLAG_RECENTS) != 0) { protLevel.append("|recents"); } + if ((level & PermissionInfo.PROTECTION_FLAG_ROLE) != 0) { + protLevel.append("|role"); + } return protLevel.toString(); } diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 85b19e7de04bb..f7f6d4b6ad076 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -306,6 +306,8 @@ + + diff --git a/services/core/java/com/android/server/pm/permission/Permission.java b/services/core/java/com/android/server/pm/permission/Permission.java index 995b59e97e912..b281e5b40fb40 100644 --- a/services/core/java/com/android/server/pm/permission/Permission.java +++ b/services/core/java/com/android/server/pm/permission/Permission.java @@ -335,6 +335,10 @@ public final class Permission { return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_RECENTS) != 0; } + public boolean isRole() { + return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_ROLE) != 0; + } + public void transfer(@NonNull String oldPackageName, @NonNull String newPackageName) { if (!oldPackageName.equals(mPermissionInfo.packageName)) { return; 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 791efd022fe9b..3af24bc78563a 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -160,6 +160,7 @@ import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -1550,18 +1551,24 @@ public class PermissionManagerService extends IPermissionManager.Stub { throw new IllegalArgumentException("Unknown package: " + packageName); } + final boolean isRolePermission; final boolean isSoftRestrictedPermission; synchronized (mLock) { final Permission permission = mRegistry.getPermission(permName); - isSoftRestrictedPermission = permission != null && permission.isSoftRestricted(); + if (permission == null) { + throw new IllegalArgumentException("Unknown permission: " + permName); + } + isRolePermission = permission.isRole(); + isSoftRestrictedPermission = permission.isSoftRestricted(); } + final boolean mayGrantRolePermission = isRolePermission + && mayManageRolePermission(callingUid); final boolean mayGrantSoftRestrictedPermission = isSoftRestrictedPermission && SoftRestrictedPermissionPolicy.forPermission(mContext, pkg.toAppInfoWithoutState(), pkg, UserHandle.of(userId), permName) .mayGrantPermission(); final boolean isRuntimePermission; - final boolean isDevelopmentPermission; final boolean permissionHasGids; synchronized (mLock) { final Permission bp = mRegistry.getPermission(permName); @@ -1570,9 +1577,14 @@ public class PermissionManagerService extends IPermissionManager.Stub { } isRuntimePermission = bp.isRuntime(); - isDevelopmentPermission = bp.isDevelopment(); permissionHasGids = bp.hasGids(); - if (!(isRuntimePermission || isDevelopmentPermission)) { + if (isRuntimePermission || bp.isDevelopment()) { + // Good. + } else if (bp.isRole()) { + if (!mayGrantRolePermission) { + throw new SecurityException("Permission " + permName + " is managed by role"); + } + } else { throw new SecurityException("Permission " + permName + " requested by " + pkg.getPackageName() + " is not a changeable permission type"); } @@ -1623,7 +1635,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { return; } - if (bp.isDevelopment()) { + if (bp.isDevelopment() || bp.isRole()) { // Development permissions must be handled specially, since they are not // normal runtime permissions. For now they apply to all users. // TODO(zhanghai): We are breaking the behavior above by making all permission state @@ -1654,10 +1666,10 @@ public class PermissionManagerService extends IPermissionManager.Stub { final int uid = UserHandle.getUid(userId, pkg.getUid()); if (callback != null) { - if (isDevelopmentPermission) { - callback.onInstallPermissionGranted(); - } else { + if (isRuntimePermission) { callback.onPermissionGranted(uid, userId); + } else { + callback.onInstallPermissionGranted(); } if (permissionHasGids) { callback.onGidsChanged(UserHandle.getAppId(pkg.getUid()), userId); @@ -1715,8 +1727,18 @@ public class PermissionManagerService extends IPermissionManager.Stub { throw new IllegalArgumentException("Unknown package: " + packageName); } + final boolean isRolePermission; + synchronized (mLock) { + final Permission permission = mRegistry.getPermission(permName); + if (permission == null) { + throw new IllegalArgumentException("Unknown permission: " + permName); + } + isRolePermission = permission.isRole(); + } + final boolean mayRevokeRolePermission = isRolePermission + && mayManageRolePermission(callingUid); + final boolean isRuntimePermission; - final boolean isDevelopmentPermission; synchronized (mLock) { final Permission bp = mRegistry.getPermission(permName); if (bp == null) { @@ -1724,8 +1746,13 @@ public class PermissionManagerService extends IPermissionManager.Stub { } isRuntimePermission = bp.isRuntime(); - isDevelopmentPermission = bp.isDevelopment(); - if (!(isRuntimePermission || isDevelopmentPermission)) { + if (isRuntimePermission || bp.isDevelopment()) { + // Good. + } else if (bp.isRole()) { + if (!mayRevokeRolePermission) { + throw new SecurityException("Permission " + permName + " is managed by role"); + } + } else { throw new SecurityException("Permission " + permName + " requested by " + pkg.getPackageName() + " is not a changeable permission type"); } @@ -1777,11 +1804,11 @@ public class PermissionManagerService extends IPermissionManager.Stub { } if (callback != null) { - if (isDevelopmentPermission) { - mDefaultPermissionCallback.onInstallPermissionRevoked(); - } else { + if (isRuntimePermission) { callback.onPermissionRevoked(UserHandle.getUid(userId, pkg.getUid()), userId, reason); + } else { + mDefaultPermissionCallback.onInstallPermissionRevoked(); } } @@ -1790,6 +1817,17 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } + private boolean mayManageRolePermission(int uid) { + final PackageManager packageManager = mContext.getPackageManager(); + final String[] packageNames = packageManager.getPackagesForUid(uid); + if (packageNames == null) { + return false; + } + final String permissionControllerPackageName = + packageManager.getPermissionControllerPackageName(); + return Arrays.asList(packageNames).contains(permissionControllerPackageName); + } + @Override public void resetRuntimePermissions() { mContext.enforceCallingOrSelfPermission( @@ -2906,7 +2944,8 @@ public class PermissionManagerService extends IPermissionManager.Stub { if ((bp.isNormal() && shouldGrantNormalPermission) || (bp.isSignature() && (shouldGrantSignaturePermission.contains(permName) - || (bp.isDevelopment() && origState.isPermissionGranted(permName))))) { + || ((bp.isDevelopment() || bp.isRole()) + && origState.isPermissionGranted(permName))))) { // Grant an install permission. if (uidState.grantPermission(bp)) { changedInstallPermission = true; @@ -3472,12 +3511,12 @@ public class PermissionManagerService extends IPermissionManager.Stub { // need a separate flag anymore. Hence we need to check which // permissions are needed by the permission controller if (!allowed && bp.isInstaller() - && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames( + && (ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames( PackageManagerInternal.PACKAGE_INSTALLER, UserHandle.USER_SYSTEM), pkg.getPackageName()) || ArrayUtils.contains( mPackageManagerInt.getKnownPackageNames( PackageManagerInternal.PACKAGE_PERMISSION_CONTROLLER, - UserHandle.USER_SYSTEM), pkg.getPackageName())) { + UserHandle.USER_SYSTEM), pkg.getPackageName()))) { // If this permission is to be granted to the system installer and // this app is an installer, then it gets the permission. allowed = true;