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;