summaryrefslogtreecommitdiff
path: root/services/permission/java
diff options
context:
space:
mode:
Diffstat (limited to 'services/permission/java')
-rw-r--r--services/permission/java/com/android/server/permission/access/collection/IndexedList.kt7
-rw-r--r--services/permission/java/com/android/server/permission/access/permission/Permission.kt70
-rw-r--r--services/permission/java/com/android/server/permission/access/permission/PermissionService.kt542
-rw-r--r--services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt46
4 files changed, 586 insertions, 79 deletions
diff --git a/services/permission/java/com/android/server/permission/access/collection/IndexedList.kt b/services/permission/java/com/android/server/permission/access/collection/IndexedList.kt
index c4d07fe28c94..f4eccebfe957 100644
--- a/services/permission/java/com/android/server/permission/access/collection/IndexedList.kt
+++ b/services/permission/java/com/android/server/permission/access/collection/IndexedList.kt
@@ -99,3 +99,10 @@ inline fun <T> IndexedList<T>.retainAllIndexed(predicate: (Int, T) -> Boolean):
}
return isChanged
}
+
+inline fun <T, R> IndexedList<T>.mapNotNullIndexed(transform: (T) -> R?): IndexedList<R> =
+ IndexedList<R>().also { destination ->
+ forEachIndexed { _, element ->
+ transform(element)?.let { destination += it }
+ }
+ }
diff --git a/services/permission/java/com/android/server/permission/access/permission/Permission.kt b/services/permission/java/com/android/server/permission/access/permission/Permission.kt
index 88989c4bf201..35f00a72d3ea 100644
--- a/services/permission/java/com/android/server/permission/access/permission/Permission.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/Permission.kt
@@ -46,83 +46,86 @@ data class Permission(
@Suppress("DEPRECATION")
get() = permissionInfo.protectionLevel
+ inline val protection: Int
+ get() = permissionInfo.protection
+
inline val isInternal: Boolean
- get() = permissionInfo.protection == PermissionInfo.PROTECTION_INTERNAL
+ get() = protection == PermissionInfo.PROTECTION_INTERNAL
inline val isNormal: Boolean
- get() = permissionInfo.protection == PermissionInfo.PROTECTION_NORMAL
+ get() = protection == PermissionInfo.PROTECTION_NORMAL
inline val isRuntime: Boolean
- get() = permissionInfo.protection == PermissionInfo.PROTECTION_DANGEROUS
+ get() = protection == PermissionInfo.PROTECTION_DANGEROUS
inline val isSignature: Boolean
- get() = permissionInfo.protection == PermissionInfo.PROTECTION_SIGNATURE
+ get() = protection == PermissionInfo.PROTECTION_SIGNATURE
+
+ inline val protectionFlags: Int
+ get() = permissionInfo.protectionFlags
inline val isAppOp: Boolean
- get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_APPOP)
+ get() = protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_APPOP)
inline val isAppPredictor: Boolean
- get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_APP_PREDICTOR)
+ get() = protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_APP_PREDICTOR)
inline val isCompanion: Boolean
- get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_COMPANION)
+ get() = protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_COMPANION)
inline val isConfigurator: Boolean
- get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_CONFIGURATOR)
+ get() = protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_CONFIGURATOR)
inline val isDevelopment: Boolean
- get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_DEVELOPMENT)
+ get() = protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_DEVELOPMENT)
inline val isIncidentReportApprover: Boolean
- get() = permissionInfo.protectionFlags
- .hasBits(PermissionInfo.PROTECTION_FLAG_INCIDENT_REPORT_APPROVER)
+ get() = protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_INCIDENT_REPORT_APPROVER)
inline val isInstaller: Boolean
- get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_INSTALLER)
+ get() = protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_INSTALLER)
inline val isInstant: Boolean
- get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_INSTANT)
+ get() = protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_INSTANT)
inline val isKnownSigner: Boolean
- get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_KNOWN_SIGNER)
+ get() = protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_KNOWN_SIGNER)
inline val isOem: Boolean
- get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_OEM)
+ get() = protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_OEM)
inline val isPre23: Boolean
- get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_PRE23)
+ get() = protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_PRE23)
inline val isPreInstalled: Boolean
- get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_PREINSTALLED)
+ get() = protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_PREINSTALLED)
inline val isPrivileged: Boolean
- get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_PRIVILEGED)
+ get() = protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_PRIVILEGED)
inline val isRecents: Boolean
- get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_RECENTS)
+ get() = protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_RECENTS)
inline val isRetailDemo: Boolean
- get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_RETAIL_DEMO)
+ get() = protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_RETAIL_DEMO)
inline val isRole: Boolean
- get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_ROLE)
+ get() = protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_ROLE)
inline val isRuntimeOnly: Boolean
- get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY)
+ get() = protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY)
inline val isSetup: Boolean
- get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_SETUP)
+ get() = protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_SETUP)
inline val isSystemTextClassifier: Boolean
- get() = permissionInfo.protectionFlags
- .hasBits(PermissionInfo.PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER)
+ get() = protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER)
inline val isVendorPrivileged: Boolean
- get() = permissionInfo.protectionFlags
- .hasBits(PermissionInfo.PROTECTION_FLAG_VENDOR_PRIVILEGED)
+ get() = protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_VENDOR_PRIVILEGED)
inline val isVerifier: Boolean
- get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_VERIFIER)
+ get() = protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_VERIFIER)
inline val isHardRestricted: Boolean
get() = permissionInfo.flags.hasBits(PermissionInfo.FLAG_HARD_RESTRICTED)
@@ -133,12 +136,23 @@ data class Permission(
inline val isSoftRestricted: Boolean
get() = permissionInfo.flags.hasBits(PermissionInfo.FLAG_SOFT_RESTRICTED)
+ inline val isHardOrSoftRestricted: Boolean
+ get() = permissionInfo.flags.hasBits(
+ PermissionInfo.FLAG_HARD_RESTRICTED or PermissionInfo.FLAG_SOFT_RESTRICTED
+ )
+
+ inline val isImmutablyRestricted: Boolean
+ get() = permissionInfo.flags.hasBits(PermissionInfo.FLAG_IMMUTABLY_RESTRICTED)
+
inline val knownCerts: Set<String>
get() = permissionInfo.knownCerts
inline val hasGids: Boolean
get() = gids.isNotEmpty()
+ inline val footprint: Int
+ get() = name.length + permissionInfo.calculateFootprint()
+
fun getGidsForUser(userId: Int): IntArray =
if (areGidsPerUser) {
IntArray(gids.size) { i -> UserHandle.getUid(userId, gids[i]) }
diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
index 89a5ed482d26..e2c2c498192e 100644
--- a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
@@ -40,6 +40,7 @@ import android.os.ServiceManager
import android.os.UserHandle
import android.os.UserManager
import android.permission.IOnPermissionsChangeListener
+import android.permission.PermissionControllerManager
import android.permission.PermissionManager
import android.provider.Settings
import android.util.DebugUtils
@@ -48,10 +49,12 @@ import android.util.Log
import com.android.internal.compat.IPlatformCompat
import com.android.internal.logging.MetricsLogger
import com.android.internal.logging.nano.MetricsProto
+import com.android.internal.util.DumpUtils
import com.android.internal.util.Preconditions
import com.android.server.FgThread
import com.android.server.LocalManagerRegistry
import com.android.server.LocalServices
+import com.android.server.PermissionThread
import com.android.server.ServiceThread
import com.android.server.SystemConfig
import com.android.server.permission.access.AccessCheckingService
@@ -80,6 +83,10 @@ import com.android.server.policy.SoftRestrictedPermissionPolicy
import libcore.util.EmptyArray
import java.io.FileDescriptor
import java.io.PrintWriter
+import java.util.concurrent.CompletableFuture
+import java.util.concurrent.ExecutionException
+import java.util.concurrent.TimeUnit
+import java.util.concurrent.TimeoutException
/**
* Modern implementation of [PermissionManagerServiceInterface].
@@ -106,6 +113,17 @@ class PermissionService(
private val mountedStorageVolumes = IndexedSet<String?>()
+ private lateinit var permissionControllerManager: PermissionControllerManager
+
+ /**
+ * A permission backup might contain apps that are not installed. In this case we delay the
+ * restoration until the app is installed.
+ *
+ * This array (`userId -> noDelayedBackupLeft`) is `true` for all the users where
+ * there is **no more** delayed backup left.
+ */
+ private val isDelayedPermissionBackupFinished = IntBooleanMap()
+
fun initialize() {
metricsLogger = MetricsLogger()
packageManagerInternal = LocalServices.getService(PackageManagerInternal::class.java)
@@ -161,9 +179,7 @@ class PermissionService(
with(policy) { getPermissionGroups()[permissionGroupName] }
} ?: return null
- val isPermissionGroupVisible =
- snapshot.isPackageVisibleToUid(permissionGroup.packageName, callingUid)
- if (!isPermissionGroupVisible) {
+ if (!snapshot.isPackageVisibleToUid(permissionGroup.packageName, callingUid)) {
return null
}
}
@@ -199,9 +215,7 @@ class PermissionService(
with(policy) { getPermissions()[permissionName] }
} ?: return null
- val isPermissionVisible =
- snapshot.isPackageVisibleToUid(permission.packageName, callingUid)
- if (!isPermissionVisible) {
+ if (!snapshot.isPackageVisibleToUid(permission.packageName, callingUid)) {
return null
}
@@ -274,12 +288,30 @@ class PermissionService(
}
}
- override fun getAllPermissionsWithProtection(protection: Int): List<PermissionInfo> {
- TODO("Not yet implemented")
- }
+ override fun getAllPermissionsWithProtection(protection: Int): List<PermissionInfo> =
+ getPermissionsWithProtectionOrProtectionFlags { permission ->
+ permission.protection == protection
+ }
- override fun getAllPermissionsWithProtectionFlags(protectionFlags: Int): List<PermissionInfo> {
- TODO("Not yet implemented")
+ override fun getAllPermissionsWithProtectionFlags(protectionFlags: Int): List<PermissionInfo> =
+ getPermissionsWithProtectionOrProtectionFlags { permission ->
+ permission.protectionFlags.hasBits(protectionFlags)
+ }
+
+ private inline fun getPermissionsWithProtectionOrProtectionFlags(
+ predicate: (Permission) -> Boolean
+ ): List<PermissionInfo> {
+ service.getState {
+ with(policy) {
+ return getPermissions().mapNotNullIndexed { _, _, permission ->
+ if (predicate(permission)) {
+ permission.generatePermissionInfo(0)
+ } else {
+ null
+ }
+ }
+ }
+ }
}
override fun getPermissionGids(permissionName: String, userId: Int): IntArray {
@@ -290,11 +322,100 @@ class PermissionService(
}
override fun addPermission(permissionInfo: PermissionInfo, async: Boolean): Boolean {
- TODO("Not yet implemented")
+ val permissionName = permissionInfo.name
+ requireNotNull(permissionName) { "permissionName cannot be null" }
+ val callingUid = Binder.getCallingUid()
+ if (packageManagerLocal.withUnfilteredSnapshot().use { it.isUidInstantApp(callingUid) }) {
+ throw SecurityException("Instant apps cannot add permissions")
+ }
+ if (permissionInfo.labelRes == 0 && permissionInfo.nonLocalizedLabel == null) {
+ throw SecurityException("Label must be specified in permission")
+ }
+ val oldPermission: Permission?
+
+ service.mutateState {
+ val permissionTree = getAndEnforcePermissionTree(permissionName)
+ enforcePermissionTreeSize(permissionInfo, permissionTree)
+
+ oldPermission = with(policy) { getPermissions()[permissionName] }
+ if (oldPermission != null && !oldPermission.isDynamic) {
+ throw SecurityException(
+ "Not allowed to modify non-dynamic permission $permissionName"
+ )
+ }
+
+ permissionInfo.packageName = permissionTree.permissionInfo.packageName
+ @Suppress("DEPRECATION")
+ permissionInfo.protectionLevel =
+ PermissionInfo.fixProtectionLevel(permissionInfo.protectionLevel)
+
+ val newPermission = Permission(
+ permissionInfo, true, Permission.TYPE_DYNAMIC, permissionTree.appId
+ )
+
+ with(policy) { addPermission(newPermission, !async) }
+ }
+
+ return oldPermission == null
}
override fun removePermission(permissionName: String) {
- TODO("Not yet implemented")
+ val callingUid = Binder.getCallingUid()
+ if (packageManagerLocal.withUnfilteredSnapshot().use { it.isUidInstantApp(callingUid) }) {
+ throw SecurityException("Instant applications don't have access to this method")
+ }
+ service.mutateState {
+ getAndEnforcePermissionTree(permissionName)
+ val permission = with(policy) { getPermissions()[permissionName] } ?: return@mutateState
+
+ if (!permission.isDynamic) {
+ // TODO(b/67371907): switch to logging if it fails
+ throw SecurityException(
+ "Not allowed to modify non-dynamic permission $permissionName"
+ )
+ }
+
+ with(policy) { removePermission(permission) }
+ }
+ }
+ private fun GetStateScope.getAndEnforcePermissionTree(permissionName: String): Permission {
+ val callingUid = Binder.getCallingUid()
+ val permissionTree = with(policy) { findPermissionTree(permissionName) }
+ if (permissionTree != null && permissionTree.appId == UserHandle.getAppId(callingUid)) {
+ return permissionTree
+ }
+
+ throw SecurityException(
+ "Calling UID $callingUid is not allowed to add to or remove from the permission tree"
+ )
+ }
+
+ private fun GetStateScope.enforcePermissionTreeSize(
+ permissionInfo: PermissionInfo,
+ permissionTree: Permission
+ ) {
+ // We calculate the max size of permissions defined by this uid and throw
+ // if that plus the size of 'info' would exceed our stated maximum.
+ if (permissionTree.appId != Process.SYSTEM_UID) {
+ val permissionTreeFootprint = calculatePermissionTreeFootprint(permissionTree)
+ if (permissionTreeFootprint + permissionInfo.calculateFootprint() >
+ MAX_PERMISSION_TREE_FOOTPRINT
+ ) {
+ throw SecurityException("Permission tree size cap exceeded")
+ }
+ }
+ }
+
+ private fun GetStateScope.calculatePermissionTreeFootprint(permissionTree: Permission): Int {
+ var size = 0
+ with(policy) {
+ getPermissions().forEachValueIndexed { _, permission ->
+ if (permissionTree.appId == permission.appId) {
+ size += permission.footprint
+ }
+ }
+ }
+ return size
}
override fun checkUidPermission(uid: Int, permissionName: String): Int {
@@ -1099,30 +1220,300 @@ class PermissionService(
with(policy) { setPermissionFlags(appId, userId, permissionName, newFlags) }
}
- override fun addAllowlistedRestrictedPermission(
+ override fun getAllowlistedRestrictedPermissions(
packageName: String,
- permissionName: String,
- flags: Int,
+ allowlistedFlags: Int,
userId: Int
- ): Boolean {
- TODO("Not yet implemented")
+ ): IndexedList<String>? {
+ requireNotNull(packageName) { "packageName cannot be null" }
+ Preconditions.checkFlagsArgument(allowlistedFlags, PERMISSION_ALLOWLIST_MASK)
+ Preconditions.checkArgumentNonnegative(userId, "userId cannot be null")
+
+ enforceCallingOrSelfCrossUserPermission(
+ userId, enforceFullPermission = false, enforceShellRestriction = false,
+ "getAllowlistedRestrictedPermissions"
+ )
+
+ if (!userManagerInternal.exists(userId)) {
+ Log.w(LOG_TAG, "AllowlistedRestrictedPermission api: Unknown user $userId")
+ return null
+ }
+
+ val callingUid = Binder.getCallingUid()
+ val packageState = packageManagerLocal.withFilteredSnapshot(callingUid, userId)
+ .use { it.getPackageState(packageName) } ?: return null
+ val androidPackage = packageState.androidPackage ?: return null
+
+ val isCallerPrivileged = context.checkCallingOrSelfPermission(
+ Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS
+ ) == PackageManager.PERMISSION_GRANTED
+
+ if (allowlistedFlags.hasBits(PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM) &&
+ !isCallerPrivileged) {
+ throw SecurityException(
+ "Querying system allowlist requires " +
+ Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS
+ )
+ }
+
+ val isCallerInstallerOnRecord =
+ packageManagerInternal.isCallerInstallerOfRecord(androidPackage, callingUid)
+
+ if (allowlistedFlags.hasAnyBit(PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE or
+ PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER)) {
+ if (!isCallerPrivileged && !isCallerInstallerOnRecord) {
+ throw SecurityException(
+ "Querying upgrade or installer allowlist requires being installer on record" +
+ " or ${Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS}"
+ )
+ }
+ }
+
+ return getAllowlistedRestrictedPermissionsUnchecked(
+ packageState.appId, allowlistedFlags, userId
+ )
}
- override fun getAllowlistedRestrictedPermissions(
+ /**
+ * This method does not enforce checks on the caller, should only be called after
+ * required checks.
+ */
+ private fun getAllowlistedRestrictedPermissionsUnchecked(
+ appId: Int,
+ allowlistedFlags: Int,
+ userId: Int
+ ): IndexedList<String>? {
+ val permissionFlags = service.getState {
+ with(policy) { getUidPermissionFlags(appId, userId) }
+ } ?: return null
+
+ var queryFlags = 0
+ if (allowlistedFlags.hasBits(PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM)) {
+ queryFlags = queryFlags or PermissionFlags.SYSTEM_EXEMPT
+ }
+ if (allowlistedFlags.hasBits(PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE)) {
+ queryFlags = queryFlags or PermissionFlags.UPGRADE_EXEMPT
+ }
+ if (allowlistedFlags.hasBits(PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER)) {
+ queryFlags = queryFlags or PermissionFlags.INSTALLER_EXEMPT
+ }
+
+ return permissionFlags.mapNotNullIndexed { _, permissionName, flags ->
+ if (flags.hasAnyBit(queryFlags)) permissionName else null
+ }
+ }
+
+ override fun addAllowlistedRestrictedPermission(
packageName: String,
- flags: Int,
+ permissionName: String,
+ allowlistedFlags: Int,
userId: Int
- ): MutableList<String> {
- TODO("Not yet implemented")
+ ): Boolean {
+ requireNotNull(permissionName) { "permissionName cannot be null" }
+ if (!enforceRestrictedPermission(permissionName)) {
+ return false
+ }
+
+ val permissionNames = getAllowlistedRestrictedPermissions(
+ packageName, allowlistedFlags, userId
+ ) ?: IndexedList(1)
+
+ if (permissionName !in permissionNames) {
+ permissionNames += permissionName
+ return setAllowlistedRestrictedPermissions(
+ packageName, permissionNames, allowlistedFlags, userId, isAddingPermission = true
+ )
+ }
+ return false
}
override fun removeAllowlistedRestrictedPermission(
packageName: String,
permissionName: String,
- flags: Int,
+ allowlistedFlags: Int,
userId: Int
): Boolean {
- TODO("Not yet implemented")
+ requireNotNull(permissionName) { "permissionName cannot be null" }
+ if (!enforceRestrictedPermission(permissionName)) {
+ return false
+ }
+
+ val permissions = getAllowlistedRestrictedPermissions(
+ packageName, allowlistedFlags, userId
+ ) ?: return false
+
+ if (permissions.remove(permissionName)) {
+ return setAllowlistedRestrictedPermissions(
+ packageName, permissions, allowlistedFlags, userId, isAddingPermission = false
+ )
+ }
+
+ return false
+ }
+
+ private fun enforceRestrictedPermission(permissionName: String): Boolean {
+ val permission = service.getState { with(policy) { getPermissions()[permissionName] } }
+ if (permission == null) {
+ Log.w(LOG_TAG, "permission definition for $permissionName does not exist")
+ return false
+ }
+
+ if (packageManagerLocal.withFilteredSnapshot()
+ .use { it.getPackageState(permission.packageName) } == null) {
+ return false
+ }
+
+ val isImmutablyRestrictedPermission =
+ permission.isHardOrSoftRestricted && permission.isImmutablyRestricted
+ if (isImmutablyRestrictedPermission && context.checkCallingOrSelfPermission(
+ Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS
+ ) != PackageManager.PERMISSION_GRANTED) {
+ throw SecurityException(
+ "Cannot modify allowlist of an immutably restricted permission: ${permission.name}"
+ )
+ }
+
+ return true
+ }
+
+ private fun setAllowlistedRestrictedPermissions(
+ packageName: String,
+ allowlistedPermissions: List<String>,
+ allowlistedFlags: Int,
+ userId: Int,
+ isAddingPermission: Boolean
+ ): Boolean {
+ Preconditions.checkArgument(allowlistedFlags.countOneBits() == 1)
+
+ val isCallerPrivileged = context.checkCallingOrSelfPermission(
+ Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS
+ ) == PackageManager.PERMISSION_GRANTED
+
+ val callingUid = Binder.getCallingUid()
+ val packageState = packageManagerLocal.withFilteredSnapshot(callingUid, userId)
+ .use { snapshot -> snapshot.packageStates[packageName] ?: return false }
+ val androidPackage = packageState.androidPackage ?: return false
+
+ val isCallerInstallerOnRecord =
+ packageManagerInternal.isCallerInstallerOfRecord(androidPackage, callingUid)
+
+ if (allowlistedFlags.hasBits(PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE)) {
+ if (!isCallerPrivileged && !isCallerInstallerOnRecord) {
+ throw SecurityException(
+ "Modifying upgrade allowlist requires being installer on record or " +
+ Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS
+ )
+ }
+ if (isAddingPermission && !isCallerPrivileged) {
+ throw SecurityException(
+ "Adding to upgrade allowlist requires" +
+ Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS
+ )
+ }
+ }
+
+ setAllowlistedRestrictedPermissionsUnchecked(
+ androidPackage, packageState.appId, allowlistedPermissions, allowlistedFlags, userId
+ )
+
+ return true
+ }
+
+ /**
+ * This method does not enforce checks on the caller, should only be called after
+ * required checks.
+ */
+ private fun setAllowlistedRestrictedPermissionsUnchecked(
+ androidPackage: AndroidPackage,
+ appId: Int,
+ allowlistedPermissions: List<String>,
+ allowlistedFlags: Int,
+ userId: Int
+ ) {
+ service.mutateState {
+ with(policy) {
+ val permissionsFlags =
+ getUidPermissionFlags(appId, userId) ?: return@mutateState
+
+ val permissions = getPermissions()
+ androidPackage.requestedPermissions.forEachIndexed { _, requestedPermission ->
+ val permission = permissions[requestedPermission]
+ if (permission == null || !permission.isHardOrSoftRestricted) {
+ return@forEachIndexed
+ }
+
+ val oldFlags = permissionsFlags[requestedPermission] ?: 0
+ val wasGranted = PermissionFlags.isPermissionGranted(oldFlags)
+
+ var newFlags = oldFlags
+ var mask = 0
+ var allowlistFlagsCopy = allowlistedFlags
+ while (allowlistFlagsCopy != 0) {
+ val flag = 1 shl allowlistFlagsCopy.countTrailingZeroBits()
+ allowlistFlagsCopy = allowlistFlagsCopy and flag.inv()
+ when (flag) {
+ PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM -> {
+ mask = mask or PermissionFlags.SYSTEM_EXEMPT
+ newFlags =
+ if (allowlistedPermissions.contains(requestedPermission)) {
+ newFlags or PermissionFlags.SYSTEM_EXEMPT
+ } else {
+ newFlags andInv PermissionFlags.SYSTEM_EXEMPT
+ }
+ }
+ PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE -> {
+ mask = mask or PermissionFlags.UPGRADE_EXEMPT
+ newFlags =
+ if (allowlistedPermissions.contains(requestedPermission)) {
+ newFlags or PermissionFlags.UPGRADE_EXEMPT
+ } else {
+ newFlags andInv PermissionFlags.UPGRADE_EXEMPT
+ }
+ }
+ PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER -> {
+ mask = mask or PermissionFlags.INSTALLER_EXEMPT
+ newFlags =
+ if (allowlistedPermissions.contains(requestedPermission)) {
+ newFlags or PermissionFlags.INSTALLER_EXEMPT
+ } else {
+ newFlags andInv PermissionFlags.INSTALLER_EXEMPT
+ }
+ }
+ }
+ }
+
+ if (oldFlags == newFlags) {
+ return@forEachIndexed
+ }
+
+ val wasAllowlisted = oldFlags.hasAnyBit(PermissionFlags.MASK_EXEMPT)
+ val isAllowlisted = newFlags.hasAnyBit(PermissionFlags.MASK_EXEMPT)
+
+ // If the permission is policy fixed as granted but it is no longer
+ // on any of the allowlists we need to clear the policy fixed flag
+ // as allowlisting trumps policy i.e. policy cannot grant a non
+ // grantable permission.
+ if (oldFlags.hasBits(PermissionFlags.POLICY_FIXED)) {
+ if (!isAllowlisted && wasGranted) {
+ mask = mask or PermissionFlags.POLICY_FIXED
+ newFlags = newFlags andInv PermissionFlags.POLICY_FIXED
+ }
+ }
+
+ // If we are allowlisting an app that does not support runtime permissions
+ // we need to make sure it goes through the permission review UI at launch.
+ if (androidPackage.targetSdkVersion < Build.VERSION_CODES.M &&
+ !wasAllowlisted && isAllowlisted) {
+ mask = mask or PermissionFlags.IMPLICIT
+ newFlags = newFlags or PermissionFlags.IMPLICIT
+ }
+
+ updatePermissionFlags(
+ appId, userId, requestedPermission, mask, newFlags
+ )
+ }
+ }
+ }
}
override fun resetRuntimePermissions(androidPackage: AndroidPackage, userId: Int) {
@@ -1159,8 +1550,29 @@ class PermissionService(
)
}
+
+
override fun getAppOpPermissionPackages(permissionName: String): Array<String> {
- TODO("Not yet implemented")
+ requireNotNull(permissionName) { "permissionName cannot be null" }
+ val packageNames = IndexedSet<String>()
+
+ val permission = service.getState {
+ with(policy) { getPermissions()[permissionName] }
+ }
+ if (permission == null || !permission.isAppOp) {
+ packageNames.toTypedArray()
+ }
+
+ packageManagerLocal.withUnfilteredSnapshot().use { snapshot ->
+ snapshot.packageStates.forEach packageStates@{ (_, packageState) ->
+ val androidPackage = packageState.androidPackage ?: return@packageStates
+ if (permissionName in androidPackage.requestedPermissions) {
+ packageNames += androidPackage.packageName
+ }
+ }
+ }
+
+ return packageNames.toTypedArray()
}
override fun getAllAppOpPermissionPackages(): Map<String, Set<String>> {
@@ -1183,19 +1595,63 @@ class PermissionService(
}
override fun backupRuntimePermissions(userId: Int): ByteArray? {
- TODO("Not yet implemented")
+ Preconditions.checkArgumentNonnegative(userId, "userId cannot be null")
+ val backup = CompletableFuture<ByteArray>()
+ permissionControllerManager.getRuntimePermissionBackup(
+ UserHandle.of(userId), PermissionThread.getExecutor(), backup::complete
+ )
+
+ return try {
+ backup.get(BACKUP_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
+ } catch (e: Exception) {
+ when (e) {
+ is TimeoutException, is InterruptedException, is ExecutionException -> {
+ Log.e(LOG_TAG, "Cannot create permission backup for user $userId", e)
+ null
+ }
+ else -> throw e
+ }
+ }
}
override fun restoreRuntimePermissions(backup: ByteArray, userId: Int) {
- TODO("Not yet implemented")
+ requireNotNull(backup) { "backup" }
+ Preconditions.checkArgumentNonnegative(userId, "userId")
+
+ synchronized(isDelayedPermissionBackupFinished) {
+ isDelayedPermissionBackupFinished -= userId
+ }
+ permissionControllerManager.stageAndApplyRuntimePermissionsBackup(
+ backup, UserHandle.of(userId)
+ )
}
override fun restoreDelayedRuntimePermissions(packageName: String, userId: Int) {
- TODO("Not yet implemented")
+ requireNotNull(packageName) { "packageName" }
+ Preconditions.checkArgumentNonnegative(userId, "userId")
+
+ synchronized(isDelayedPermissionBackupFinished) {
+ if (isDelayedPermissionBackupFinished.get(userId, false)) {
+ return
+ }
+ }
+ permissionControllerManager.applyStagedRuntimePermissionBackup(
+ packageName, UserHandle.of(userId), PermissionThread.getExecutor()
+ ) { hasMoreBackup ->
+ if (hasMoreBackup) {
+ return@applyStagedRuntimePermissionBackup
+ }
+ synchronized(isDelayedPermissionBackupFinished) {
+ isDelayedPermissionBackupFinished.put(userId, true)
+ }
+ }
}
override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>?) {
- TODO("Not yet implemented")
+ if (!DumpUtils.checkDumpPermission(context, LOG_TAG, pw)) {
+ return
+ }
+ context.getSystemService(PermissionControllerManager::class.java)!!.dump(fd, args)
}
override fun getPermissionTEMP(
@@ -1231,7 +1687,10 @@ class PermissionService(
}
override fun onSystemReady() {
- TODO("Not yet implemented")
+ // TODO STOPSHIP privappPermissionsViolationsfix check
+ permissionControllerManager = PermissionControllerManager(
+ context, PermissionThread.getHandler()
+ )
}
override fun onUserCreated(userId: Int) {
@@ -1349,13 +1808,12 @@ class PermissionService(
if (activityManager != null) {
val appId = UserHandle.getAppId(uid)
val userId = UserHandle.getUserId(uid)
- val identity = Binder.clearCallingIdentity()
- try {
- activityManager.killUidForPermissionChange(appId, userId, reason)
- } catch (e: RemoteException) {
- /* ignore - same process */
- } finally {
- Binder.restoreCallingIdentity(identity)
+ Binder::class.withClearedCallingIdentity {
+ try {
+ activityManager.killUidForPermissionChange(appId, userId, reason)
+ } catch (e: RemoteException) {
+ /* ignore - same process */
+ }
}
}
}
@@ -1678,5 +2136,15 @@ class PermissionService(
private const val UNREQUESTABLE_MASK = PermissionFlags.RESTRICTION_REVOKED or
PermissionFlags.SYSTEM_FIXED or PermissionFlags.POLICY_FIXED or
PermissionFlags.USER_FIXED
+
+ private val BACKUP_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(60)
+
+ /** Cap the size of permission trees that 3rd party apps can define; in characters of text */
+ private const val MAX_PERMISSION_TREE_FOOTPRINT = 32768
+
+ private const val PERMISSION_ALLOWLIST_MASK =
+ PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE or
+ PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM or
+ PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER
}
}
diff --git a/services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt
index 531ab7126155..4c3ffde6b3be 100644
--- a/services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt
@@ -361,7 +361,7 @@ class UidPermissionPolicy : SchemePolicy() {
// Different from the old implementation, which may add an (incomplete) signature
// permission inside another package's permission tree, we now consistently ignore such
// permissions.
- val permissionTree = getPermissionTree(permissionName)
+ val permissionTree = findPermissionTree(permissionName)
val newPackageName = newPermissionInfo.packageName
if (permissionTree != null && newPackageName != permissionTree.packageName) {
Log.w(
@@ -482,7 +482,7 @@ class UidPermissionPolicy : SchemePolicy() {
if (!permission.isDynamic) {
return permission
}
- val permissionTree = getPermissionTree(permission.name) ?: return permission
+ val permissionTree = findPermissionTree(permission.name) ?: return permission
@Suppress("DEPRECATION")
return permission.copy(
permissionInfo = PermissionInfo(permission.permissionInfo).apply {
@@ -491,18 +491,6 @@ class UidPermissionPolicy : SchemePolicy() {
)
}
- private fun MutateStateScope.getPermissionTree(permissionName: String): Permission? =
- newState.systemState.permissionTrees.firstNotNullOfOrNullIndexed {
- _, permissionTreeName, permissionTree ->
- if (permissionName.startsWith(permissionTreeName) &&
- permissionName.length > permissionTreeName.length &&
- permissionName[permissionTreeName.length] == '.') {
- permissionTree
- } else {
- null
- }
- }
-
private fun MutateStateScope.trimPermissionStates(appId: Int) {
val requestedPermissions = IndexedSet<String>()
forEachPackageInAppId(appId) {
@@ -1103,6 +1091,26 @@ class UidPermissionPolicy : SchemePolicy() {
with(persistence) { this@serializeUserState.serializeUserState(state, userId) }
}
+ fun GetStateScope.getPermissionTrees(): IndexedMap<String, Permission> =
+ state.systemState.permissionTrees
+
+ fun GetStateScope.findPermissionTree(permissionName: String): Permission? =
+ state.systemState.permissionTrees.firstNotNullOfOrNullIndexed {
+ _, permissionTreeName, permissionTree ->
+ if (permissionName.startsWith(permissionTreeName) &&
+ permissionName.length > permissionTreeName.length &&
+ permissionName[permissionTreeName.length] == '.') {
+ permissionTree
+ } else {
+ null
+ }
+ }
+
+ fun MutateStateScope.addPermissionTree(permission: Permission) {
+ newState.systemState.permissionTrees[permission.name] = permission
+ newState.systemState.requestWrite()
+ }
+
/**
* returns all permission group definitions available in the system
*/
@@ -1115,6 +1123,16 @@ class UidPermissionPolicy : SchemePolicy() {
fun GetStateScope.getPermissions(): IndexedMap<String, Permission> =
state.systemState.permissions
+ fun MutateStateScope.addPermission(permission: Permission, sync: Boolean = false) {
+ newState.systemState.permissions[permission.name] = permission
+ newState.systemState.requestWrite(sync)
+ }
+
+ fun MutateStateScope.removePermission(permission: Permission) {
+ newState.systemState.permissions -= permission.name
+ newState.systemState.requestWrite()
+ }
+
fun GetStateScope.getUidPermissionFlags(appId: Int, userId: Int): IndexedMap<String, Int>? =
state.userStates[userId]?.uidPermissionFlags?.get(appId)