summaryrefslogtreecommitdiff
path: root/services/permission/java
diff options
context:
space:
mode:
author Hai Zhang <zhanghai@google.com> 2022-12-19 02:25:44 -0800
committer Hai Zhang <zhanghai@google.com> 2022-12-19 02:43:30 -0800
commit20b04c7faaf6a1075b79b1069d84053fb7ab205e (patch)
tree74e2da083b88df17dd2df356ccd0753c38853ffa /services/permission/java
parent4b8c2445896947b1451a754759efb76bf2e1ed45 (diff)
Add permission flag related methods.
Implemented methods for checking/updating permission state and checking/updating permission flags. Different from the old implementation, the new code has been re-written to cleanly check or not check caller's privileges. external facing methods will enforce necessary permissions, and then will only call internal methods that no longer enforces permissions. The new code also tries to gets the package state, and gets or mutates the permission state, for only once. This ensures we are at least consistent within package or permission. The methods have also be restructured to do: 1. Enforce cross-user permissions. 2. Enforce method-related permissions. 3. Skip if user is not found (may happen upon user deletion). 4. Gather package state, get or mutate permission state, or call into internal methods that doesn't enforce caller permissions. Log statements have also been prefixed with method names to make debugging either - whereas exceptions already have its stacktrace. PermissionFlags now also takes in the Permission object and correctly handles restricted permissions, so that the restricted permission allowlist implementation can now call it directly. Bug: 252884423 Test: presubmit Change-Id: Ide19bb2e286123e3c81f641f114461fcd2c04a04
Diffstat (limited to 'services/permission/java')
-rw-r--r--services/permission/java/com/android/server/permission/access/collection/IndexedMap.kt17
-rw-r--r--services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt156
-rw-r--r--services/permission/java/com/android/server/permission/access/permission/PermissionService.kt1095
-rw-r--r--services/permission/java/com/android/server/permission/access/util/BinderExtensions.kt29
4 files changed, 1056 insertions, 241 deletions
diff --git a/services/permission/java/com/android/server/permission/access/collection/IndexedMap.kt b/services/permission/java/com/android/server/permission/access/collection/IndexedMap.kt
index 1e73be7923ae..f4e362ceb2c7 100644
--- a/services/permission/java/com/android/server/permission/access/collection/IndexedMap.kt
+++ b/services/permission/java/com/android/server/permission/access/collection/IndexedMap.kt
@@ -148,10 +148,21 @@ inline fun <K, V> IndexedMap<K, V>.retainAllIndexed(predicate: (Int, K, V) -> Bo
return isChanged
}
-inline fun <K, V, R> IndexedMap<K, V>.mapNotNullIndexed(transform: (K, V) -> R?): IndexedList<R> =
+inline fun <K, V, R> IndexedMap<K, V>.mapNotNullIndexed(
+ transform: (Int, K, V) -> R?
+): IndexedList<R> =
IndexedList<R>().also { destination ->
- forEachIndexed { _, key, value ->
- transform(key, value)?.let { destination += it }
+ forEachIndexed { index, key, value ->
+ transform(index, key, value)?.let { destination += it }
+ }
+ }
+
+inline fun <K, V, R> IndexedMap<K, V>.mapNotNullIndexedToSet(
+ transform: (Int, K, V) -> R?
+): IndexedSet<R> =
+ IndexedSet<R>().also { destination ->
+ forEachIndexed { index, key, value ->
+ transform(index, key, value)?.let { destination += it }
}
}
diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt
index a4708c85345f..48658ff1c223 100644
--- a/services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt
@@ -317,172 +317,164 @@ object PermissionFlags {
*/
const val MASK_EXEMPT = INSTALLER_EXEMPT or SYSTEM_EXEMPT or UPGRADE_EXEMPT
- /**
- * Mask for all API permission flags about permission restriction.
- */
- private const val API_MASK_RESTRICTION =
- PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT or
- PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT or
- PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT or
- PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION
-
- /**
- * Mask for all permission flags about permission restriction.
- */
- private const val MASK_RESTRICTION = INSTALLER_EXEMPT or SYSTEM_EXEMPT or
- UPGRADE_EXEMPT or RESTRICTION_REVOKED or SOFT_RESTRICTED
-
- fun isPermissionGranted(policyFlags: Int): Boolean {
- if (policyFlags.hasBits(INSTALL_GRANTED)) {
+ fun isPermissionGranted(flags: Int): Boolean {
+ if (flags.hasBits(INSTALL_GRANTED)) {
return true
}
- if (policyFlags.hasBits(INSTALL_REVOKED)) {
+ if (flags.hasBits(INSTALL_REVOKED)) {
return false
}
- if (policyFlags.hasBits(PROTECTION_GRANTED)) {
+ if (flags.hasBits(PROTECTION_GRANTED)) {
return true
}
- if (policyFlags.hasBits(LEGACY_GRANTED) || policyFlags.hasBits(IMPLICIT_GRANTED)) {
+ if (flags.hasBits(LEGACY_GRANTED) || flags.hasBits(IMPLICIT_GRANTED)) {
return true
}
- if (policyFlags.hasBits(RESTRICTION_REVOKED)) {
+ if (flags.hasBits(RESTRICTION_REVOKED)) {
return false
}
- return policyFlags.hasBits(RUNTIME_GRANTED)
+ return flags.hasBits(RUNTIME_GRANTED)
}
- fun isAppOpGranted(policyFlags: Int): Boolean =
- isPermissionGranted(policyFlags) && !policyFlags.hasBits(APP_OP_REVOKED)
-
- fun isReviewRequired(policyFlags: Int): Boolean =
- policyFlags.hasBits(LEGACY_GRANTED) && policyFlags.hasBits(IMPLICIT)
+ fun isAppOpGranted(flags: Int): Boolean =
+ isPermissionGranted(flags) && !flags.hasBits(APP_OP_REVOKED)
- fun toApiFlags(policyFlags: Int): Int {
+ fun toApiFlags(flags: Int): Int {
var apiFlags = 0
- if (policyFlags.hasBits(USER_SET)) {
+ if (flags.hasBits(USER_SET)) {
apiFlags = apiFlags or PackageManager.FLAG_PERMISSION_USER_SET
}
- if (policyFlags.hasBits(USER_FIXED)) {
+ if (flags.hasBits(USER_FIXED)) {
apiFlags = apiFlags or PackageManager.FLAG_PERMISSION_USER_FIXED
}
- if (policyFlags.hasBits(POLICY_FIXED)) {
+ if (flags.hasBits(POLICY_FIXED)) {
apiFlags = apiFlags or PackageManager.FLAG_PERMISSION_POLICY_FIXED
}
- if (policyFlags.hasBits(SYSTEM_FIXED)) {
+ if (flags.hasBits(SYSTEM_FIXED)) {
apiFlags = apiFlags or PackageManager.FLAG_PERMISSION_SYSTEM_FIXED
}
- if (policyFlags.hasBits(PREGRANT)) {
+ if (flags.hasBits(PREGRANT)) {
apiFlags = apiFlags or PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT
}
- if (policyFlags.hasBits(IMPLICIT)) {
- apiFlags = apiFlags or if (policyFlags.hasBits(LEGACY_GRANTED)) {
+ if (flags.hasBits(IMPLICIT)) {
+ apiFlags = apiFlags or if (flags.hasBits(LEGACY_GRANTED)) {
PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED
} else {
PackageManager.FLAG_PERMISSION_REVOKE_WHEN_REQUESTED
}
}
- if (policyFlags.hasBits(USER_SENSITIVE_WHEN_GRANTED)) {
+ if (flags.hasBits(USER_SENSITIVE_WHEN_GRANTED)) {
apiFlags = apiFlags or PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED
}
- if (policyFlags.hasBits(USER_SENSITIVE_WHEN_REVOKED)) {
+ if (flags.hasBits(USER_SENSITIVE_WHEN_REVOKED)) {
apiFlags = apiFlags or PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED
}
- if (policyFlags.hasBits(INSTALLER_EXEMPT)) {
+ if (flags.hasBits(INSTALLER_EXEMPT)) {
apiFlags = apiFlags or PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT
}
- if (policyFlags.hasBits(SYSTEM_EXEMPT)) {
+ if (flags.hasBits(SYSTEM_EXEMPT)) {
apiFlags = apiFlags or PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT
}
- if (policyFlags.hasBits(UPGRADE_EXEMPT)) {
+ if (flags.hasBits(UPGRADE_EXEMPT)) {
apiFlags = apiFlags or PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT
}
- if (policyFlags.hasBits(RESTRICTION_REVOKED) || policyFlags.hasBits(SOFT_RESTRICTED)) {
+ if (flags.hasBits(RESTRICTION_REVOKED) || flags.hasBits(SOFT_RESTRICTED)) {
apiFlags = apiFlags or PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION
}
- if (policyFlags.hasBits(ROLE)) {
+ if (flags.hasBits(ROLE)) {
apiFlags = apiFlags or PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE
}
- if (policyFlags.hasBits(APP_OP_REVOKED)) {
+ if (flags.hasBits(APP_OP_REVOKED)) {
apiFlags = apiFlags or PackageManager.FLAG_PERMISSION_REVOKED_COMPAT
}
- if (policyFlags.hasBits(ONE_TIME)) {
+ if (flags.hasBits(ONE_TIME)) {
apiFlags = apiFlags or PackageManager.FLAG_PERMISSION_ONE_TIME
}
- if (policyFlags.hasBits(HIBERNATION)) {
+ if (flags.hasBits(HIBERNATION)) {
apiFlags = apiFlags or PackageManager.FLAG_PERMISSION_AUTO_REVOKED
}
- if (policyFlags.hasBits(USER_SELECTED)) {
+ if (flags.hasBits(USER_SELECTED)) {
apiFlags = apiFlags or PackageManager.FLAG_PERMISSION_SELECTED_LOCATION_ACCURACY
}
return apiFlags
}
- fun setRuntimePermissionGranted(policyFlags: Int, isGranted: Boolean): Int =
- if (isGranted) policyFlags or RUNTIME_GRANTED else policyFlags andInv RUNTIME_GRANTED
+ fun updateRuntimePermissionGranted(flags: Int, isGranted: Boolean): Int =
+ if (isGranted) flags or RUNTIME_GRANTED else flags andInv RUNTIME_GRANTED
- fun updatePolicyFlags(policyFlags: Int, apiFlagMask: Int, apiFlagValues: Int): Int {
- check(!apiFlagMask.hasAnyBit(API_MASK_RESTRICTION)) {
- "Permission flags about permission restriction can only be directly mutated by the" +
- " policy"
- }
- val oldApiFlags = toApiFlags(policyFlags)
+ fun updateFlags(permission: Permission, flags: Int, apiFlagMask: Int, apiFlagValues: Int): Int {
+ val oldApiFlags = toApiFlags(flags)
val newApiFlags = (oldApiFlags andInv apiFlagMask) or (apiFlagValues and apiFlagMask)
- return toPolicyFlags(policyFlags, newApiFlags)
+ return fromApiFlags(newApiFlags, permission, flags)
}
- private fun toPolicyFlags(oldPolicyFlags: Int, apiFlags: Int): Int {
- var policyFlags = 0
- policyFlags = policyFlags or (oldPolicyFlags and INSTALL_GRANTED)
- policyFlags = policyFlags or (oldPolicyFlags and INSTALL_REVOKED)
- policyFlags = policyFlags or (oldPolicyFlags and PROTECTION_GRANTED)
+ private fun fromApiFlags(apiFlags: Int, permission: Permission, oldFlags: Int): Int {
+ var flags = 0
+ flags = flags or (oldFlags and INSTALL_GRANTED)
+ flags = flags or (oldFlags and INSTALL_REVOKED)
+ flags = flags or (oldFlags and PROTECTION_GRANTED)
if (apiFlags.hasBits(PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE)) {
- policyFlags = policyFlags or ROLE
+ flags = flags or ROLE
}
- policyFlags = policyFlags or (oldPolicyFlags and RUNTIME_GRANTED)
+ flags = flags or (oldFlags and RUNTIME_GRANTED)
if (apiFlags.hasBits(PackageManager.FLAG_PERMISSION_USER_SET)) {
- policyFlags = policyFlags or USER_SET
+ flags = flags or USER_SET
}
if (apiFlags.hasBits(PackageManager.FLAG_PERMISSION_USER_FIXED)) {
- policyFlags = policyFlags or USER_FIXED
+ flags = flags or USER_FIXED
}
if (apiFlags.hasBits(PackageManager.FLAG_PERMISSION_POLICY_FIXED)) {
- policyFlags = policyFlags or POLICY_FIXED
+ flags = flags or POLICY_FIXED
}
if (apiFlags.hasBits(PackageManager.FLAG_PERMISSION_SYSTEM_FIXED)) {
- policyFlags = policyFlags or SYSTEM_FIXED
+ flags = flags or SYSTEM_FIXED
}
if (apiFlags.hasBits(PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT)) {
- policyFlags = policyFlags or PREGRANT
+ flags = flags or PREGRANT
}
- policyFlags = policyFlags or (oldPolicyFlags and LEGACY_GRANTED)
- policyFlags = policyFlags or (oldPolicyFlags and IMPLICIT_GRANTED)
+ flags = flags or (oldFlags and LEGACY_GRANTED)
+ flags = flags or (oldFlags and IMPLICIT_GRANTED)
if (apiFlags.hasBits(PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) ||
apiFlags.hasBits(PackageManager.FLAG_PERMISSION_REVOKE_WHEN_REQUESTED)) {
- policyFlags = policyFlags or IMPLICIT
+ flags = flags or IMPLICIT
}
if (apiFlags.hasBits(PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED)) {
- policyFlags = policyFlags or USER_SENSITIVE_WHEN_GRANTED
+ flags = flags or USER_SENSITIVE_WHEN_GRANTED
}
if (apiFlags.hasBits(PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED)) {
- policyFlags = policyFlags or USER_SENSITIVE_WHEN_REVOKED
+ flags = flags or USER_SENSITIVE_WHEN_REVOKED
+ }
+ if (apiFlags.hasBits(PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT)) {
+ flags = flags or INSTALLER_EXEMPT
+ }
+ if (apiFlags.hasBits(PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT)) {
+ flags = flags or SYSTEM_EXEMPT
+ }
+ if (apiFlags.hasBits(PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT)) {
+ flags = flags or UPGRADE_EXEMPT
+ }
+ // We ignore whether FLAG_PERMISSION_APPLY_RESTRICTION is set here because previously
+ // platform may be relying on the old restorePermissionState() to get it correct later.
+ if (!flags.hasAnyBit(MASK_EXEMPT)) {
+ if (permission.isHardRestricted) {
+ flags = flags or RESTRICTION_REVOKED
+ }
+ if (permission.isSoftRestricted) {
+ flags = flags or SOFT_RESTRICTED
+ }
}
- // FLAG_PERMISSION_APPLY_RESTRICTION can be either REVOKED_BY_RESTRICTION when the
- // permission is hard restricted, or SOFT_RESTRICTED when the permission is soft restricted.
- // However since we should never allow indirect mutation of restriction state, we can just
- // get the flags about restriction from the old policy flags.
- policyFlags = policyFlags or (oldPolicyFlags and MASK_RESTRICTION)
if (apiFlags.hasBits(PackageManager.FLAG_PERMISSION_REVOKED_COMPAT)) {
- policyFlags = policyFlags or APP_OP_REVOKED
+ flags = flags or APP_OP_REVOKED
}
if (apiFlags.hasBits(PackageManager.FLAG_PERMISSION_ONE_TIME)) {
- policyFlags = policyFlags or ONE_TIME
+ flags = flags or ONE_TIME
}
if (apiFlags.hasBits(PackageManager.FLAG_PERMISSION_AUTO_REVOKED)) {
- policyFlags = policyFlags or HIBERNATION
+ flags = flags or HIBERNATION
}
if (apiFlags.hasBits(PackageManager.FLAG_PERMISSION_SELECTED_LOCATION_ACCURACY)) {
- policyFlags = policyFlags or USER_SELECTED
+ flags = flags or USER_SELECTED
}
- return policyFlags
+ return flags
}
}
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 f8ab6e9f755c..71175016250d 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
@@ -26,6 +26,7 @@ import android.content.pm.PackageManagerInternal
import android.content.pm.PermissionGroupInfo
import android.content.pm.PermissionInfo
import android.content.pm.permission.SplitPermissionInfoParcelable
+import android.metrics.LogMaker
import android.os.Binder
import android.os.Build
import android.os.Handler
@@ -37,31 +38,45 @@ import android.os.RemoteCallbackList
import android.os.RemoteException
import android.os.ServiceManager
import android.os.UserHandle
+import android.os.UserManager
import android.permission.IOnPermissionsChangeListener
import android.permission.PermissionManager
import android.provider.Settings
+import android.util.DebugUtils
import android.util.IntArray as GrowingIntArray
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.Preconditions
import com.android.server.FgThread
import com.android.server.LocalManagerRegistry
import com.android.server.LocalServices
import com.android.server.ServiceThread
import com.android.server.SystemConfig
import com.android.server.permission.access.AccessCheckingService
+import com.android.server.permission.access.GetStateScope
+import com.android.server.permission.access.MutateStateScope
import com.android.server.permission.access.PermissionUri
import com.android.server.permission.access.UidUri
import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports
+import com.android.server.permission.access.util.andInv
import com.android.server.permission.access.util.hasAnyBit
import com.android.server.permission.access.util.hasBits
+import com.android.server.permission.access.util.withClearedCallingIdentity
+import com.android.server.pm.KnownPackages
import com.android.server.pm.PackageManagerLocal
+import com.android.server.pm.UserManagerInternal
import com.android.server.pm.UserManagerService
+import com.android.server.pm.parsing.pkg.AndroidPackageUtils
import com.android.server.pm.permission.LegacyPermission
import com.android.server.pm.permission.LegacyPermissionSettings
import com.android.server.pm.permission.LegacyPermissionState
import com.android.server.pm.permission.PermissionManagerServiceInterface
import com.android.server.pm.permission.PermissionManagerServiceInternal
import com.android.server.pm.pkg.AndroidPackage
+import com.android.server.pm.pkg.PackageState
+import com.android.server.policy.SoftRestrictedPermissionPolicy
import libcore.util.EmptyArray
import java.io.FileDescriptor
import java.io.PrintWriter
@@ -76,21 +91,23 @@ class PermissionService(
service.getSchemePolicy(UidUri.SCHEME, PermissionUri.SCHEME) as UidPermissionPolicy
private val context = service.context
+ private lateinit var metricsLogger: MetricsLogger
private lateinit var packageManagerInternal: PackageManagerInternal
private lateinit var packageManagerLocal: PackageManagerLocal
private lateinit var platformCompat: IPlatformCompat
private lateinit var systemConfig: SystemConfig
+ private lateinit var userManagerInternal: UserManagerInternal
private lateinit var userManagerService: UserManagerService
- private val mountedStorageVolumes = IndexedSet<String?>()
-
private lateinit var handlerThread: HandlerThread
private lateinit var handler: Handler
-
private lateinit var onPermissionsChangeListeners: OnPermissionsChangeListeners
private lateinit var onPermissionFlagsChangedListener: OnPermissionFlagsChangedListener
+ private val mountedStorageVolumes = IndexedSet<String?>()
+
fun initialize() {
+ metricsLogger = MetricsLogger()
packageManagerInternal = LocalServices.getService(PackageManagerInternal::class.java)
packageManagerLocal =
LocalManagerRegistry.getManagerOrThrow(PackageManagerLocal::class.java)
@@ -98,19 +115,19 @@ class PermissionService(
ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)
)
systemConfig = SystemConfig.getInstance()
+ userManagerInternal = LocalServices.getService(UserManagerInternal::class.java)
userManagerService = UserManagerService.getInstance()
handlerThread = ServiceThread(LOG_TAG, Process.THREAD_PRIORITY_BACKGROUND, true)
handler = Handler(handlerThread.looper)
-
onPermissionsChangeListeners = OnPermissionsChangeListeners(FgThread.get().looper)
onPermissionFlagsChangedListener = OnPermissionFlagsChangedListener()
policy.addOnPermissionFlagsChangedListener(onPermissionFlagsChangedListener)
}
override fun getAllPermissionGroups(flags: Int): List<PermissionGroupInfo> {
- val callingUid = Binder.getCallingUid()
packageManagerLocal.withUnfilteredSnapshot().use { snapshot ->
+ val callingUid = Binder.getCallingUid()
if (snapshot.isUidInstantApp(callingUid)) {
return emptyList()
}
@@ -119,7 +136,7 @@ class PermissionService(
with(policy) { getPermissionGroups() }
}
- return permissionGroups.mapNotNullIndexed { _, permissionGroup ->
+ return permissionGroups.mapNotNullIndexed { _, _, permissionGroup ->
if (snapshot.isPackageVisibleToUid(permissionGroup.packageName, callingUid)) {
permissionGroup.generatePermissionGroupInfo(flags)
} else {
@@ -188,12 +205,10 @@ class PermissionService(
return null
}
- val callingAppId = UserHandle.getAppId(callingUid)
- val opPackage = snapshot.packageStates[opPackageName]?.androidPackage
+ val opPackage = snapshot.getPackageState(opPackageName)?.androidPackage
targetSdkVersion = when {
// System sees all flags.
- callingAppId == Process.ROOT_UID || callingAppId == Process.SYSTEM_UID ||
- callingAppId == Process.SHELL_UID -> Build.VERSION_CODES.CUR_DEVELOPMENT
+ isRootOrSystemOrShell(callingUid) -> Build.VERSION_CODES.CUR_DEVELOPMENT
opPackage != null -> opPackage.targetSdkVersion
else -> Build.VERSION_CODES.CUR_DEVELOPMENT
}
@@ -227,29 +242,30 @@ class PermissionService(
permissionGroupName: String?,
flags: Int
): List<PermissionInfo>? {
- val callingUid = Binder.getCallingUid()
packageManagerLocal.withUnfilteredSnapshot().use { snapshot ->
+ val callingUid = Binder.getCallingUid()
if (snapshot.isUidInstantApp(callingUid)) {
return null
}
- if (permissionGroupName != null) {
- val permissionGroup = service.getState {
- with(policy) { getPermissionGroups()[permissionGroupName] }
- } ?: return null
+ val permissions: IndexedMap<String, Permission>
+ service.getState {
+ if (permissionGroupName != null) {
+ val permissionGroup =
+ with(policy) { getPermissionGroups()[permissionGroupName] } ?: return null
- if (!snapshot.isPackageVisibleToUid(permissionGroup.packageName, callingUid)) {
- return null
+ if (!snapshot.isPackageVisibleToUid(permissionGroup.packageName, callingUid)) {
+ return null
+ }
}
- }
- val permissions = service.getState {
- with(policy) { getPermissions() }
+ permissions = with(policy) { getPermissions() }
}
- return permissions.mapNotNullIndexed { _, permission ->
+ return permissions.mapNotNullIndexed { _, _, permission ->
if (permission.groupName == permissionGroupName &&
- snapshot.isPackageVisibleToUid(permission.packageName, callingUid)) {
+ snapshot.isPackageVisibleToUid(permission.packageName, callingUid)
+ ) {
permission.generatePermissionInfo(flags)
} else {
null
@@ -281,43 +297,188 @@ class PermissionService(
TODO("Not yet implemented")
}
+ override fun checkUidPermission(uid: Int, permissionName: String): Int {
+ val userId = UserHandle.getUserId(uid)
+ if (!userManagerInternal.exists(userId)) {
+ return PackageManager.PERMISSION_DENIED
+ }
+
+ // PackageManagerInternal.getPackage(int) already checks package visibility and enforces
+ // that instant apps can't see shared UIDs. Note that on the contrary,
+ // Note that PackageManagerInternal.getPackage(String) doesn't perform any checks.
+ val androidPackage = packageManagerInternal.getPackage(uid)
+ if (androidPackage != null) {
+ // Note that PackageManagerInternal.getPackageStateInternal() is not filtered.
+ val packageState =
+ packageManagerInternal.getPackageStateInternal(androidPackage.packageName)
+ if (packageState == null) {
+ Log.e(
+ LOG_TAG, "checkUidPermission: PackageState not found for AndroidPackage" +
+ " $androidPackage"
+ )
+ return PackageManager.PERMISSION_DENIED
+ }
+ val isPermissionGranted = service.getState {
+ isPermissionGranted(packageState, userId, permissionName)
+ }
+ return if (isPermissionGranted) {
+ PackageManager.PERMISSION_GRANTED
+ } else {
+ PackageManager.PERMISSION_DENIED
+ }
+ }
+
+ return if (isSystemUidPermissionGranted(uid, permissionName)) {
+ PackageManager.PERMISSION_GRANTED
+ } else {
+ PackageManager.PERMISSION_DENIED
+ }
+ }
+
+ /**
+ * Internal implementation that should only be called by [checkUidPermission].
+ */
+ private fun isSystemUidPermissionGranted(uid: Int, permissionName: String): Boolean {
+ val uidPermissions = systemConfig.systemPermissions[uid] ?: return false
+ if (permissionName in uidPermissions) {
+ return true
+ }
+
+ val fullerPermissionName = FULLER_PERMISSIONS[permissionName]
+ if (fullerPermissionName != null && fullerPermissionName in uidPermissions) {
+ return true
+ }
+
+ return false
+ }
+
override fun checkPermission(packageName: String, permissionName: String, userId: Int): Int {
- TODO("Not yet implemented")
+ if (!userManagerInternal.exists(userId)) {
+ return PackageManager.PERMISSION_DENIED
+ }
+
+ val packageState = packageManagerLocal.withFilteredSnapshot(Binder.getCallingUid(), userId)
+ .use { it.getPackageState(packageName) } ?: return PackageManager.PERMISSION_DENIED
+
+ val isPermissionGranted = service.getState {
+ isPermissionGranted(packageState, userId, permissionName)
+ }
+ return if (isPermissionGranted) {
+ PackageManager.PERMISSION_GRANTED
+ } else {
+ PackageManager.PERMISSION_DENIED
+ }
}
- override fun checkUidPermission(uid: Int, permissionName: String): Int {
- TODO("Not yet implemented")
+ /**
+ * Check whether a permission is granted, without any validation on caller.
+ *
+ * This method should always be called for checking whether a permission is granted, instead of
+ * reading permission flags directly from the policy.
+ */
+ private fun GetStateScope.isPermissionGranted(
+ packageState: PackageState,
+ userId: Int,
+ permissionName: String
+ ): Boolean {
+ val appId = packageState.appId
+ // Note that instant apps can't have shared UIDs, so we only need to check the current
+ // package state.
+ val isInstantApp = packageState.getUserStateOrDefault(userId).isInstantApp
+ if (isSinglePermissionGranted(appId, userId, isInstantApp, permissionName)) {
+ return true
+ }
+
+ val fullerPermissionName = FULLER_PERMISSIONS[permissionName]
+ if (fullerPermissionName != null &&
+ isSinglePermissionGranted(appId, userId, isInstantApp, fullerPermissionName)) {
+ return true
+ }
+
+ return false
+ }
+
+ /**
+ * Internal implementation that should only be called by [isPermissionGranted].
+ */
+ private fun GetStateScope.isSinglePermissionGranted(
+ appId: Int,
+ userId: Int,
+ isInstantApp: Boolean,
+ permissionName: String
+ ): Boolean {
+ val flags = with(policy) { getPermissionFlags(appId, userId, permissionName) }
+ if (!PermissionFlags.isPermissionGranted(flags)) {
+ return false
+ }
+
+ if (isInstantApp) {
+ val permission = with(policy) { getPermissions()[permissionName] } ?: return false
+ if (!permission.isInstant) {
+ return false
+ }
+ }
+
+ return true
}
override fun getGrantedPermissions(packageName: String, userId: Int): Set<String> {
- TODO("Not yet implemented")
+ requireNotNull(packageName) { "packageName cannot be null" }
+ Preconditions.checkArgumentNonnegative(userId, "userId")
+
+ val packageState = packageManagerLocal.withUnfilteredSnapshot()
+ .use { it.getPackageState(packageName) }
+ if (packageState == null) {
+ Log.w(LOG_TAG, "getGrantedPermissions: Unknown package $packageName")
+ return emptySet()
+ }
+
+ service.getState {
+ val permissionFlags = with(policy) { getUidPermissionFlags(packageState.appId, userId) }
+ ?: return emptySet()
+
+ return permissionFlags.mapNotNullIndexedToSet { _, permissionName, _ ->
+ if (isPermissionGranted(packageState, userId, permissionName)) {
+ permissionName
+ } else {
+ null
+ }
+ }
+ }
}
override fun getGidsForUid(uid: Int): IntArray {
val appId = UserHandle.getAppId(uid)
val userId = UserHandle.getUserId(uid)
- val permissionFlags = service.getState {
- with(policy) { getUidPermissionFlags(appId, userId) }
- } ?: return EmptyArray.INT
- val gids = GrowingIntArray.wrap(systemConfig.globalGids)
- permissionFlags.forEachIndexed { _, permissionName, flags ->
- if (!PermissionFlags.isPermissionGranted(flags)) {
- return@forEachIndexed
- }
- val permission = service.getState {
- with(policy) { getPermissions()[permissionName] }
- } ?: return@forEachIndexed
- val permissionGids = permission.getGidsForUser(userId)
- if (permissionGids.isEmpty()) {
- return@forEachIndexed
+ val globalGids = systemConfig.globalGids
+ service.getState {
+ // Different from the old implementation, which returns an empty array when the
+ // permission state is not found, now we always return at least global GIDs. This is
+ // more consistent with the pre-S-refactor behavior. This is also because we are now
+ // actively trimming the per-UID objects when empty.
+ val permissionFlags = with(policy) { getUidPermissionFlags(appId, userId) }
+ ?: return globalGids.clone()
+
+ val gids = GrowingIntArray.wrap(globalGids)
+ permissionFlags.forEachIndexed { _, permissionName, flags ->
+ if (!PermissionFlags.isPermissionGranted(flags)) {
+ return@forEachIndexed
+ }
+
+ val permission = with(policy) { getPermissions()[permissionName] }
+ ?: return@forEachIndexed
+ val permissionGids = permission.getGidsForUser(userId)
+ if (permissionGids.isEmpty()) {
+ return@forEachIndexed
+ }
+ gids.addAll(permissionGids)
}
- gids.addAll(permissionGids)
+ return gids.toArray()
}
- return gids.toArray()
}
override fun grantRuntimePermission(packageName: String, permissionName: String, userId: Int) {
- TODO("Not yet implemented")
+ setRuntimePermissionGranted(packageName, userId, permissionName, isGranted = true)
}
override fun revokeRuntimePermission(
@@ -326,73 +487,345 @@ class PermissionService(
userId: Int,
reason: String?
) {
- TODO("Not yet implemented")
+ setRuntimePermissionGranted(
+ packageName, userId, permissionName, isGranted = false, revokeReason = reason
+ )
}
override fun revokePostNotificationPermissionWithoutKillForTest(
packageName: String,
userId: Int
) {
- TODO("Not yet implemented")
+ setRuntimePermissionGranted(
+ packageName, userId, Manifest.permission.POST_NOTIFICATIONS, isGranted = false,
+ skipKillUid = true
+ )
}
- override fun getPermissionFlags(packageName: String, permissionName: String, userId: Int): Int {
- // TODO: Implement permission checks.
- val appId = 0
- val flags = service.getState {
- with(policy) { getPermissionFlags(appId, userId, permissionName) }
+ /**
+ * Shared internal implementation that should only be called by [grantRuntimePermission],
+ * [revokeRuntimePermission] and [revokePostNotificationPermissionWithoutKillForTest].
+ */
+ private fun setRuntimePermissionGranted(
+ packageName: String,
+ userId: Int,
+ permissionName: String,
+ isGranted: Boolean,
+ skipKillUid: Boolean = false,
+ revokeReason: String? = null
+ ) {
+ val methodName = if (isGranted) "grantRuntimePermission" else "revokeRuntimePermission"
+ val callingUid = Binder.getCallingUid()
+ val isDebugEnabled = if (isGranted) {
+ PermissionManager.DEBUG_TRACE_GRANTS
+ } else {
+ PermissionManager.DEBUG_TRACE_PERMISSION_UPDATES
+ }
+ if (isDebugEnabled &&
+ PermissionManager.shouldTraceGrant(packageName, permissionName, userId)) {
+ val callingUidName = packageManagerInternal.getNameForUid(callingUid)
+ Log.i(
+ LOG_TAG, "$methodName(packageName = $packageName," +
+ " permissionName = $permissionName" +
+ (if (isGranted) "" else "skipKillUid = $skipKillUid, reason = $revokeReason") +
+ ", userId = $userId," + " callingUid = $callingUidName ($callingUid))",
+ RuntimeException()
+ )
+ }
+
+ enforceCallingOrSelfCrossUserPermission(
+ userId, enforceFullPermission = true, enforceShellRestriction = true, methodName
+ )
+ val enforcedPermissionName = if (isGranted) {
+ Manifest.permission.GRANT_RUNTIME_PERMISSIONS
+ } else {
+ Manifest.permission.REVOKE_RUNTIME_PERMISSIONS
+ }
+ context.enforceCallingOrSelfPermission(enforcedPermissionName, methodName)
+
+ if (!userManagerInternal.exists(userId)) {
+ Log.w(LOG_TAG, "$methodName: Unknown user $userId")
+ return
+ }
+
+ val packageState: PackageState?
+ val permissionControllerPackageName = packageManagerInternal.getKnownPackageNames(
+ KnownPackages.PACKAGE_PERMISSION_CONTROLLER, UserHandle.USER_SYSTEM
+ ).first()
+ val permissionControllerPackageState: PackageState?
+ packageManagerLocal.withUnfilteredSnapshot().use { snapshot ->
+ packageState = snapshot.filtered(callingUid, userId)
+ .use { it.getPackageState(packageName) }
+ permissionControllerPackageState =
+ snapshot.getPackageState(permissionControllerPackageName)
+ }
+ val androidPackage = packageState?.androidPackage
+ // Different from the old implementation, which returns when package doesn't exist but
+ // throws when package exists but isn't visible, we now return in both cases to avoid
+ // leaking the package existence.
+ if (androidPackage == null) {
+ Log.w(LOG_TAG, "$methodName: Unknown package $packageName")
+ return
+ }
+
+ val canManageRolePermission = isRootOrSystem(callingUid) ||
+ UserHandle.getAppId(callingUid) == permissionControllerPackageState!!.appId
+ val overridePolicyFixed = context.checkCallingOrSelfPermission(
+ Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY
+ ) == PackageManager.PERMISSION_GRANTED
+
+ service.mutateState {
+ with(onPermissionFlagsChangedListener) {
+ if (skipKillUid) {
+ skipKillRuntimePermissionRevokedUids()
+ }
+ if (revokeReason != null) {
+ addKillRuntimePermissionRevokedUidsReason(revokeReason)
+ }
+ }
+
+ setRuntimePermissionGranted(
+ packageState, userId, permissionName, isGranted, canManageRolePermission,
+ overridePolicyFixed, reportError = true, methodName
+ )
}
- return PermissionFlags.toApiFlags(flags)
}
- override fun isPermissionRevokedByPolicy(
- packageName: String,
+ private fun grantRequestedRuntimePermissions(
+ packageState: PackageState,
+ userId: Int,
+ permissionNames: List<String>
+ ) {
+ service.mutateState {
+ permissionNames.forEachIndexed { _, permissionName ->
+ setRuntimePermissionGranted(
+ packageState, userId, permissionName, isGranted = true,
+ canManageRolePermission = false, overridePolicyFixed = false,
+ reportError = false, "grantRequestedRuntimePermissions"
+ )
+ }
+ }
+ }
+
+ /**
+ * Set whether a runtime permission is granted, without any validation on caller.
+ */
+ private fun MutateStateScope.setRuntimePermissionGranted(
+ packageState: PackageState,
+ userId: Int,
permissionName: String,
- userId: Int
- ): Boolean {
- if (UserHandle.getCallingUserId() != userId) {
- context.enforceCallingPermission(
- Manifest.permission.INTERACT_ACROSS_USERS_FULL,
- "isPermissionRevokedByPolicy for user $userId"
+ isGranted: Boolean,
+ canManageRolePermission: Boolean,
+ overridePolicyFixed: Boolean,
+ reportError: Boolean,
+ methodName: String
+ ) {
+ val permission = with(policy) { getPermissions()[permissionName] }
+ if (permission == null) {
+ if (reportError) {
+ throw IllegalArgumentException("Unknown permission $permissionName")
+ }
+ return
+ }
+
+ val androidPackage = packageState.androidPackage!!
+ val packageName = packageState.packageName
+ when {
+ permission.isDevelopment -> {}
+ permission.isRole -> {
+ if (!canManageRolePermission) {
+ if (reportError) {
+ throw SecurityException("Permission $permissionName is managed by role")
+ }
+ return
+ }
+ }
+ permission.isRuntime -> {
+ if (androidPackage.targetSdkVersion < Build.VERSION_CODES.M) {
+ // If a permission review is required for legacy apps we represent
+ // their permissions as always granted
+ return
+ }
+ if (isGranted && packageState.getUserStateOrDefault(userId).isInstantApp &&
+ !permission.isInstant) {
+ if (reportError) {
+ throw SecurityException(
+ "Cannot grant non-instant permission $permissionName to package" +
+ " $packageName"
+ )
+ }
+ return
+ }
+ }
+ else -> {
+ if (reportError) {
+ throw SecurityException(
+ "Permission $permissionName requested by package $packageName is not a" +
+ " changeable permission type"
+ )
+ }
+ return
+ }
+ }
+
+ val appId = packageState.appId
+ val oldFlags = with(policy) { getPermissionFlags(appId, userId, permissionName) }
+
+ if (permissionName !in androidPackage.requestedPermissions && oldFlags == 0) {
+ if (reportError) {
+ throw SecurityException(
+ "Permission $permissionName isn't requested by package $packageName"
+ )
+ }
+ return
+ }
+
+ if (oldFlags.hasBits(PermissionFlags.SYSTEM_FIXED)) {
+ if (reportError) {
+ Log.e(
+ LOG_TAG, "$methodName: Cannot change system fixed permission $permissionName" +
+ " for package $packageName"
+ )
+ }
+ return
+ }
+
+ if (oldFlags.hasBits(PermissionFlags.POLICY_FIXED) && !overridePolicyFixed) {
+ if (reportError) {
+ Log.e(
+ LOG_TAG, "$methodName: Cannot change policy fixed permission $permissionName" +
+ " for package $packageName"
+ )
+ }
+ return
+ }
+
+ if (isGranted && oldFlags.hasBits(PermissionFlags.RESTRICTION_REVOKED)) {
+ if (reportError) {
+ Log.e(
+ LOG_TAG, "$methodName: Cannot grant hard-restricted non-exempt permission" +
+ " $permissionName to package $packageName"
+ )
+ }
+ return
+ }
+
+ if (isGranted && oldFlags.hasBits(PermissionFlags.SOFT_RESTRICTED)) {
+ // TODO: Refactor SoftRestrictedPermissionPolicy.
+ val softRestrictedPermissionPolicy = SoftRestrictedPermissionPolicy.forPermission(
+ context, AndroidPackageUtils.generateAppInfoWithoutState(androidPackage),
+ androidPackage, UserHandle.of(userId), permissionName
)
+ if (!softRestrictedPermissionPolicy.mayGrantPermission()) {
+ if (reportError) {
+ Log.e(
+ LOG_TAG, "$methodName: Cannot grant soft-restricted non-exempt permission" +
+ " $permissionName to package $packageName"
+ )
+ }
+ return
+ }
}
- if (checkPermission(packageName, permissionName, userId) ==
- PackageManager.PERMISSION_GRANTED) {
- return false
+ val newFlags = PermissionFlags.updateRuntimePermissionGranted(oldFlags, isGranted)
+ if (oldFlags == newFlags) {
+ return
}
- val callingUid = Binder.getCallingUid()
- if (packageManagerLocal.withUnfilteredSnapshot()
- .use { !it.isPackageVisibleToUid(packageName, userId, callingUid) }) {
- return false
+ with(policy) { setPermissionFlags(appId, userId, permissionName, newFlags) }
+
+ if (permission.isRuntime) {
+ val action = if (isGranted) {
+ MetricsProto.MetricsEvent.ACTION_PERMISSION_GRANTED
+ } else {
+ MetricsProto.MetricsEvent.ACTION_PERMISSION_REVOKED
+ }
+ val log = LogMaker(action).apply {
+ setPackageName(packageName)
+ addTaggedData(MetricsProto.MetricsEvent.FIELD_PERMISSION, permissionName)
+ }
+ metricsLogger.write(log)
}
+ }
- val permissionFlags = getPermissionFlagsUnchecked(packageName,
- permissionName, callingUid, userId)
- return permissionFlags.hasBits(PackageManager.FLAG_PERMISSION_POLICY_FIXED)
+ override fun getPermissionFlags(packageName: String, permissionName: String, userId: Int): Int {
+ enforceCallingOrSelfCrossUserPermission(
+ userId, enforceFullPermission = true, enforceShellRestriction = false,
+ "getPermissionFlags"
+ )
+ enforceCallingOrSelfAnyPermission(
+ "getPermissionFlags", Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
+ Manifest.permission.REVOKE_RUNTIME_PERMISSIONS,
+ Manifest.permission.GET_RUNTIME_PERMISSIONS
+ )
+
+ if (!userManagerInternal.exists(userId)) {
+ Log.w(LOG_TAG, "getPermissionFlags: Unknown user $userId")
+ return 0
+ }
+
+ val packageState = packageManagerLocal.withFilteredSnapshot()
+ .use { it.getPackageState(packageName) }
+ if (packageState == null) {
+ Log.w(LOG_TAG, "getPermissionFlags: Unknown package $packageName")
+ return 0
+ }
+
+ service.getState {
+ val permission = with(policy) { getPermissions()[permissionName] }
+ if (permission == null) {
+ Log.w(LOG_TAG, "getPermissionFlags: Unknown permission $permissionName")
+ return 0
+ }
+
+ val flags =
+ with(policy) { getPermissionFlags(packageState.appId, userId, permissionName) }
+ return PermissionFlags.toApiFlags(flags)
+ }
}
- private fun getPermissionFlagsUnchecked(
+ override fun isPermissionRevokedByPolicy(
packageName: String,
- permName: String,
- callingUid: Int,
+ permissionName: String,
userId: Int
- ): Int {
- throw NotImplementedError()
+ ): Boolean {
+ enforceCallingOrSelfCrossUserPermission(
+ userId, enforceFullPermission = true, enforceShellRestriction = false,
+ "isPermissionRevokedByPolicy"
+ )
+
+ if (!userManagerInternal.exists(userId)) {
+ Log.w(LOG_TAG, "isPermissionRevokedByPolicy: Unknown user $userId")
+ return false
+ }
+
+ val packageState = packageManagerLocal.withFilteredSnapshot(Binder.getCallingUid(), userId)
+ .use { it.getPackageState(packageName) } ?: return false
+
+ service.getState {
+ if (isPermissionGranted(packageState, userId, permissionName)) {
+ return false
+ }
+
+ val flags = with(policy) {
+ getPermissionFlags(packageState.appId, userId, permissionName)
+ }
+ return flags.hasBits(PermissionFlags.POLICY_FIXED)
+ }
}
override fun isPermissionsReviewRequired(packageName: String, userId: Int): Boolean {
- requireNotNull(packageName) { "packageName" }
+ requireNotNull(packageName) { "packageName cannot be null" }
// TODO(b/173235285): Some caller may pass USER_ALL as userId.
- // Preconditions.checkArgumentNonnegative(userId, "userId");
+ // Preconditions.checkArgumentNonnegative(userId, "userId")
+
val packageState = packageManagerLocal.withUnfilteredSnapshot()
- .use { it.packageStates[packageName] } ?: return false
+ .use { it.getPackageState(packageName) } ?: return false
+
val permissionFlags = service.getState {
with(policy) { getUidPermissionFlags(packageState.appId, userId) }
} ?: return false
- return permissionFlags.anyIndexed { _, _, flags -> PermissionFlags.isReviewRequired(flags)
- }
+ return permissionFlags.anyIndexed { _, _, it -> it.hasBits(REVIEW_REQUIRED_FLAGS) }
}
override fun shouldShowRequestPermissionRationale(
@@ -400,70 +833,54 @@ class PermissionService(
permissionName: String,
userId: Int
): Boolean {
- val callingUid = Binder.getCallingUid()
- if (UserHandle.getCallingUserId() != userId) {
- context.enforceCallingPermission(
- Manifest.permission.INTERACT_ACROSS_USERS_FULL,
- "canShowRequestPermissionRationale for user $userId"
- )
- }
+ enforceCallingOrSelfCrossUserPermission(
+ userId, enforceFullPermission = true, enforceShellRestriction = false,
+ "shouldShowRequestPermissionRationale"
+ )
- val appId = packageManagerLocal.withUnfilteredSnapshot().use { snapshot ->
- snapshot.packageStates[packageName]?.appId ?: return false
- }
- if (UserHandle.getAppId(callingUid) != appId) {
+ if (!userManagerInternal.exists(userId)) {
+ Log.w(LOG_TAG, "shouldShowRequestPermissionRationale: Unknown user $userId")
return false
}
- if (checkPermission(packageName, permissionName, userId) ==
- PackageManager.PERMISSION_GRANTED) {
+ val callingUid = Binder.getCallingUid()
+ val packageState = packageManagerLocal.withFilteredSnapshot(callingUid, userId)
+ .use { it.getPackageState(packageName) } ?: return false
+ val appId = packageState.appId
+ if (UserHandle.getAppId(callingUid) != appId) {
return false
}
- val identity = Binder.clearCallingIdentity()
- val permissionFlags = try {
- getPermissionFlagsInternal(packageName, permissionName, callingUid, userId)
- } finally {
- Binder.restoreCallingIdentity(identity)
- }
-
- val fixedFlags = (PermissionFlags.SYSTEM_FIXED or PermissionFlags.POLICY_FIXED
- or PermissionFlags.USER_FIXED)
+ val flags: Int
+ service.getState {
+ if (isPermissionGranted(packageState, userId, permissionName)) {
+ return false
+ }
- if (permissionFlags.hasAnyBit(fixedFlags) ||
- permissionFlags.hasBits(PermissionFlags.RESTRICTION_REVOKED)) {
+ flags = with(policy) { getPermissionFlags(appId, userId, permissionName) }
+ }
+ if (flags.hasAnyBit(UNREQUESTABLE_MASK)) {
return false
}
- val token = Binder.clearCallingIdentity()
- try {
- if (permissionName == Manifest.permission.ACCESS_BACKGROUND_LOCATION &&
- platformCompat.isChangeEnabledByPackageName(
- BACKGROUND_RATIONALE_CHANGE_ID, packageName, userId)
- ) {
+ if (permissionName == Manifest.permission.ACCESS_BACKGROUND_LOCATION) {
+ val isBackgroundRationaleChangeEnabled = Binder::class.withClearedCallingIdentity {
+ try {
+ platformCompat.isChangeEnabledByPackageName(
+ BACKGROUND_RATIONALE_CHANGE_ID, packageName, userId
+ )
+ } catch (e: RemoteException) {
+ Log.e(LOG_TAG, "shouldShowRequestPermissionRationale: Unable to check if" +
+ " compatibility change is enabled", e)
+ false
+ }
+ }
+ if (isBackgroundRationaleChangeEnabled) {
return true
}
- } catch (e: RemoteException) {
- Log.e(LOG_TAG, "Unable to check if compatibility change is enabled.", e)
- } finally {
- Binder.restoreCallingIdentity(token)
}
- return permissionFlags and PackageManager.FLAG_PERMISSION_USER_SET != 0
- }
-
- /**
- * read internal permission flags
- * @return internal permission Flags
- * @see PermissionFlags
- */
- private fun getPermissionFlagsInternal(
- packageName: String,
- permName: String,
- callingUid: Int,
- userId: Int
- ): Int {
- throw NotImplementedError()
+ return flags.hasBits(PermissionFlags.USER_SET)
}
override fun updatePermissionFlags(
@@ -471,14 +888,215 @@ class PermissionService(
permissionName: String,
flagMask: Int,
flagValues: Int,
- checkAdjustPolicyFlagPermission: Boolean,
+ enforceAdjustPolicyPermission: Boolean,
userId: Int
) {
- TODO("Not yet implemented")
+ val callingUid = Binder.getCallingUid()
+ if (PermissionManager.DEBUG_TRACE_PERMISSION_UPDATES &&
+ PermissionManager.shouldTraceGrant(packageName, permissionName, userId)) {
+ val flagMaskString = DebugUtils.flagsToString(
+ PackageManager::class.java, "FLAG_PERMISSION_", flagMask.toLong()
+ )
+ val flagValuesString = DebugUtils.flagsToString(
+ PackageManager::class.java, "FLAG_PERMISSION_", flagValues.toLong()
+ )
+ val callingUidName = packageManagerInternal.getNameForUid(callingUid)
+ Log.i(
+ LOG_TAG, "updatePermissionFlags(packageName = $packageName," +
+ " permissionName = $permissionName, flagMask = $flagMaskString," +
+ " flagValues = $flagValuesString, userId = $userId," +
+ " callingUid = $callingUidName ($callingUid))", RuntimeException()
+ )
+ }
+
+ enforceCallingOrSelfCrossUserPermission(
+ userId, enforceFullPermission = true, enforceShellRestriction = true,
+ "updatePermissionFlags"
+ )
+ enforceCallingOrSelfAnyPermission(
+ "updatePermissionFlags", Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
+ Manifest.permission.REVOKE_RUNTIME_PERMISSIONS
+ )
+
+ // Different from the old implementation, which implicitly didn't allow modifying the
+ // POLICY_FIXED flag if the caller is system or root UID, now we do allow that since system
+ // and root UIDs are supposed to have all permissions including
+ // ADJUST_RUNTIME_PERMISSIONS_POLICY.
+ if (!isRootOrSystem(callingUid)) {
+ if (flagMask.hasBits(PackageManager.FLAG_PERMISSION_POLICY_FIXED)) {
+ if (enforceAdjustPolicyPermission) {
+ context.enforceCallingOrSelfPermission(
+ Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY,
+ "Need ${Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY} to change" +
+ " policy flags"
+ )
+ } else {
+ val targetSdkVersion = packageManagerInternal.getUidTargetSdkVersion(callingUid)
+ require(targetSdkVersion < Build.VERSION_CODES.Q) {
+ "${Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY} needs to be" +
+ " checked for packages targeting ${Build.VERSION_CODES.Q} or later" +
+ " when changing policy flags"
+ }
+ }
+ }
+ }
+
+ if (!userManagerInternal.exists(userId)) {
+ Log.w(LOG_TAG, "updatePermissionFlags: Unknown user $userId")
+ return
+ }
+
+ // Using PackageManagerInternal instead of PackageManagerLocal for now due to need to access
+ // shared user packages.
+ // TODO: We probably shouldn't check the share user packages, since the package name is
+ // explicitly provided and grantRuntimePermission() isn't checking shared user packages
+ // anyway.
+ val packageState = packageManagerInternal.getPackageStateInternal(packageName)
+ val androidPackage = packageState?.androidPackage
+ // Different from the old implementation, which returns when package doesn't exist but
+ // throws when package exists but isn't visible, we now return in both cases to avoid
+ // leaking the package existence.
+ if (androidPackage == null ||
+ packageManagerInternal.filterAppAccess(packageName, callingUid, userId, false)) {
+ Log.w(LOG_TAG, "updatePermissionFlags: Unknown package $packageName")
+ return
+ }
+
+ val isPermissionRequested = if (permissionName in androidPackage.requestedPermissions) {
+ // Fast path, the current package has requested the permission.
+ true
+ } else {
+ // Slow path, go through all shared user packages.
+ val sharedUserPackageNames =
+ packageManagerInternal.getSharedUserPackagesForPackage(packageName, userId)
+ sharedUserPackageNames.any { sharedUserPackageName ->
+ val sharedUserPackage = packageManagerInternal.getPackage(sharedUserPackageName)
+ sharedUserPackage != null &&
+ permissionName in sharedUserPackage.requestedPermissions
+ }
+ }
+
+ val appId = packageState.appId
+ service.mutateState {
+ updatePermissionFlags(
+ appId, userId, permissionName, flagMask, flagValues,
+ reportErrorForUnknownPermission = true, isPermissionRequested,
+ "updatePermissionFlags", packageName
+ )
+ }
}
override fun updatePermissionFlagsForAllApps(flagMask: Int, flagValues: Int, userId: Int) {
- TODO("Not yet implemented")
+ val callingUid = Binder.getCallingUid()
+ if (PermissionManager.DEBUG_TRACE_PERMISSION_UPDATES) {
+ val flagMaskString = DebugUtils.flagsToString(
+ PackageManager::class.java, "FLAG_PERMISSION_", flagMask.toLong()
+ )
+ val flagValuesString = DebugUtils.flagsToString(
+ PackageManager::class.java, "FLAG_PERMISSION_", flagValues.toLong()
+ )
+ val callingUidName = packageManagerInternal.getNameForUid(callingUid)
+ Log.i(
+ LOG_TAG, "updatePermissionFlagsForAllApps(flagMask = $flagMaskString," +
+ " flagValues = $flagValuesString, userId = $userId," +
+ " callingUid = $callingUidName ($callingUid))", RuntimeException()
+ )
+ }
+
+ enforceCallingOrSelfCrossUserPermission(
+ userId, enforceFullPermission = true, enforceShellRestriction = true,
+ "updatePermissionFlagsForAllApps"
+ )
+ enforceCallingOrSelfAnyPermission(
+ "updatePermissionFlagsForAllApps", Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
+ Manifest.permission.REVOKE_RUNTIME_PERMISSIONS
+ )
+
+ if (!userManagerInternal.exists(userId)) {
+ Log.w(LOG_TAG, "updatePermissionFlagsForAllApps: Unknown user $userId")
+ return
+ }
+
+ val packageStates = packageManagerLocal.withUnfilteredSnapshot()
+ .use { it.packageStates }
+ service.mutateState {
+ packageStates.forEach { (packageName, packageState) ->
+ val androidPackage = packageState.androidPackage ?: return@forEach
+ androidPackage.requestedPermissions.forEach { permissionName ->
+ // Different from the old implementation, which only sanitized the SYSTEM_FIXED
+ // flag, we now properly sanitize all flags as in updatePermissionFlags().
+ updatePermissionFlags(
+ packageState.appId, userId, permissionName, flagMask, flagValues,
+ reportErrorForUnknownPermission = false, isPermissionRequested = true,
+ "updatePermissionFlagsForAllApps", packageName
+ )
+ }
+ }
+ }
+ }
+
+ /**
+ * Shared internal implementation that should only be called by [updatePermissionFlags] and
+ * [updatePermissionFlagsForAllApps].
+ */
+ private fun MutateStateScope.updatePermissionFlags(
+ appId: Int,
+ userId: Int,
+ permissionName: String,
+ flagMask: Int,
+ flagValues: Int,
+ reportErrorForUnknownPermission: Boolean,
+ isPermissionRequested: Boolean,
+ methodName: String,
+ packageName: String
+ ) {
+ // Different from the old implementation, which only allowed the system UID to modify the
+ // following flags, we now allow the root UID as well since both should have all
+ // permissions.
+ // Only the system can change these flags and nothing else.
+ val callingUid = Binder.getCallingUid()
+ @Suppress("NAME_SHADOWING")
+ var flagMask = flagMask
+ @Suppress("NAME_SHADOWING")
+ var flagValues = flagValues
+ if (!isRootOrSystem(callingUid)) {
+ // Different from the old implementation, which allowed non-system UIDs to remove (but
+ // not add) permission restriction flags, we now consistently ignore them altogether.
+ val ignoredMask = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED or
+ PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT or
+ // REVIEW_REQUIRED can be set on any permission by the shell, or by any app for the
+ // NOTIFICATIONS permissions specifically.
+ if (isShell(callingUid) || permissionName in NOTIFICATIONS_PERMISSIONS) {
+ 0
+ } else {
+ PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED
+ } or PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT or
+ PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT or
+ PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT or
+ PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION
+ flagMask = flagMask andInv ignoredMask
+ flagValues = flagValues andInv ignoredMask
+ }
+
+ val permission = with(policy) { getPermissions()[permissionName] }
+ if (permission == null) {
+ if (reportErrorForUnknownPermission) {
+ throw IllegalArgumentException("Unknown permission $permissionName")
+ }
+ return
+ }
+
+ val oldFlags = with(policy) { getPermissionFlags(appId, userId, permissionName) }
+ if (!isPermissionRequested && oldFlags == 0) {
+ Log.w(
+ LOG_TAG, "$methodName: Permission $permissionName isn't requested by package" +
+ " $packageName"
+ )
+ return
+ }
+
+ val newFlags = PermissionFlags.updateFlags(permission, oldFlags, flagMask, flagValues)
+ with(policy) { setPermissionFlags(appId, userId, permissionName, newFlags) }
}
override fun addAllowlistedRestrictedPermission(
@@ -659,6 +1277,8 @@ class PermissionService(
synchronized(mountedStorageVolumes) {
if (androidPackage.volumeUuid !in mountedStorageVolumes) {
// Wait for the storage volume to be mounted and batch the state mutation there.
+ // PackageInstalledParams won't exist when packages are being scanned instead of
+ // being installed by an installer.
return
}
}
@@ -667,8 +1287,15 @@ class PermissionService(
} else {
intArrayOf(userId)
}
- userIds.forEach { service.onPackageInstalled(androidPackage.packageName, it) }
- // TODO: Handle params.
+ @Suppress("NAME_SHADOWING")
+ userIds.forEach { userId ->
+ service.onPackageInstalled(androidPackage.packageName, userId)
+ // TODO: Remove when this callback receives packageState directly.
+ val packageState =
+ packageManagerInternal.getPackageStateInternal(androidPackage.packageName)!!
+ // TODO: Add allowlisting
+ grantRequestedRuntimePermissions(packageState, userId, params.grantedPermissions)
+ }
}
override fun onPackageUninstalled(
@@ -690,6 +1317,24 @@ class PermissionService(
}
}
+ /**
+ * Check whether a UID is root or system.
+ */
+ private fun isRootOrSystem(uid: Int) =
+ when (UserHandle.getAppId(uid)) {
+ Process.ROOT_UID, Process.SYSTEM_UID -> true
+ else -> false
+ }
+
+ /**
+ * Check whether a UID is shell.
+ */
+ private fun isShell(uid: Int) = UserHandle.getAppId(uid) == Process.SHELL_UID
+
+ /**
+ * Check whether a UID is root, system or shell.
+ */
+ private fun isRootOrSystemOrShell(uid: Int) = isRootOrSystem(uid) || isShell(uid)
/**
* This method should typically only be used when granting or revoking permissions, since the
@@ -715,21 +1360,33 @@ class PermissionService(
}
/**
+ * @see PackageManagerLocal.withFilteredSnapshot
+ */
+ private fun PackageManagerLocal.withFilteredSnapshot(
+ callingUid: Int,
+ userId: Int
+ ): PackageManagerLocal.FilteredSnapshot =
+ withFilteredSnapshot(callingUid, UserHandle.of(userId))
+
+ /**
+ * Get the [PackageState] for a package name.
+ *
+ * This is for parity with [PackageManagerLocal.FilteredSnapshot.getPackageState] which is more
+ * efficient than [PackageManagerLocal.FilteredSnapshot.getPackageStates], so that we can always
+ * prefer using `getPackageState()` without worrying about whether the snapshot is filtered.
+ */
+ private fun PackageManagerLocal.UnfilteredSnapshot.getPackageState(
+ packageName: String
+ ): PackageState? = packageStates[packageName]
+
+ /**
* Check whether a UID belongs to an instant app.
*/
- private fun PackageManagerLocal.UnfilteredSnapshot.isUidInstantApp(uid: Int): Boolean {
- if (Process.isIsolatedUid(uid)) {
- // Unfortunately we don't have the API for getting the owner UID of an isolated UID yet,
- // so for now we just keep calling the old API.
- return packageManagerInternal.getInstantAppPackageName(uid) != null
- }
- val appId = UserHandle.getAppId(uid)
- // Instant apps can't have shared UIDs, so we can just take the first package.
- val firstPackageState = packageStates.values.firstOrNull { it.appId == appId }
- ?: return false
- val userId = UserHandle.getUserId(uid)
- return firstPackageState.getUserStateOrDefault(userId).isInstantApp
- }
+ private fun PackageManagerLocal.UnfilteredSnapshot.isUidInstantApp(uid: Int): Boolean =
+ // Unfortunately we don't have the API for getting the owner UID of an isolated UID or the
+ // API for getting the SharedUserApi object for an app ID yet, so for now we just keep
+ // calling the old API.
+ packageManagerInternal.getInstantAppPackageName(uid) != null
/**
* Check whether a package is visible to a UID within the same user as the UID.
@@ -746,9 +1403,100 @@ class PermissionService(
packageName: String,
userId: Int,
uid: Int
- ): Boolean {
- val user = UserHandle.of(userId)
- return filtered(uid, user).use { it.getPackageState(packageName) != null }
+ ): Boolean = filtered(uid, userId).use { it.getPackageState(packageName) != null }
+
+ /**
+ * @see PackageManagerLocal.UnfilteredSnapshot.filtered
+ */
+ private fun PackageManagerLocal.UnfilteredSnapshot.filtered(
+ callingUid: Int,
+ userId: Int
+ ): PackageManagerLocal.FilteredSnapshot = filtered(callingUid, UserHandle.of(userId))
+
+ /**
+ * If neither you nor the calling process of an IPC you are handling has been granted the
+ * permission for accessing a particular [userId], throw a [SecurityException].
+ *
+ * @see Context.enforceCallingOrSelfPermission
+ * @see UserManager.DISALLOW_DEBUGGING_FEATURES
+ */
+ private fun enforceCallingOrSelfCrossUserPermission(
+ userId: Int,
+ enforceFullPermission: Boolean,
+ enforceShellRestriction: Boolean,
+ message: String?
+ ) {
+ require(userId >= 0) { "userId $userId is invalid" }
+ val callingUid = Binder.getCallingUid()
+ val callingUserId = UserHandle.getUserId(callingUid)
+ if (userId != callingUserId) {
+ val permissionName = if (enforceFullPermission) {
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL
+ } else {
+ Manifest.permission.INTERACT_ACROSS_USERS
+ }
+ if (context.checkCallingOrSelfPermission(permissionName) !=
+ PackageManager.PERMISSION_GRANTED) {
+ val exceptionMessage = buildString {
+ if (message != null) {
+ append(message)
+ append(": ")
+ }
+ append("Neither user ")
+ append(Binder.getCallingUid())
+ append(" nor current process has ")
+ append(permissionName)
+ append(" to access user ")
+ append(userId)
+ }
+ throw SecurityException(exceptionMessage)
+ }
+ }
+ if (enforceShellRestriction && isShell(callingUid)) {
+ val isShellRestricted = userManagerInternal.hasUserRestriction(
+ UserManager.DISALLOW_DEBUGGING_FEATURES, userId
+ )
+ if (isShellRestricted) {
+ val exceptionMessage = buildString {
+ if (message != null) {
+ append(message)
+ append(": ")
+ }
+ append("Shell is disallowed to access user ")
+ append(userId)
+ }
+ throw SecurityException(exceptionMessage)
+ }
+ }
+ }
+
+ /**
+ * If neither you nor the calling process of an IPC you are handling has been granted any of the
+ * permissions, throw a [SecurityException].
+ *
+ * @see Context.enforceCallingOrSelfPermission
+ */
+ private fun enforceCallingOrSelfAnyPermission(
+ message: String?,
+ vararg permissionNames: String
+ ) {
+ val hasAnyPermission = permissionNames.any { permissionName ->
+ context.checkCallingOrSelfPermission(permissionName) ==
+ PackageManager.PERMISSION_GRANTED
+ }
+ if (!hasAnyPermission) {
+ val exceptionMessage = buildString {
+ if (message != null) {
+ append(message)
+ append(": ")
+ }
+ append("Neither user ")
+ append(Binder.getCallingUid())
+ append(" nor current process has any of ")
+ permissionNames.joinTo(this, ", ")
+ }
+ throw SecurityException(exceptionMessage)
+ }
}
/**
@@ -761,6 +1509,17 @@ class PermissionService(
private val runtimePermissionRevokedUids = IntBooleanMap()
private val gidsChangedUids = IntSet()
+ private var isKillRuntimePermissionRevokedUidsSkipped = false
+ private val killRuntimePermissionRevokedUidsReasons = IndexedSet<String>()
+
+ fun MutateStateScope.skipKillRuntimePermissionRevokedUids() {
+ isKillRuntimePermissionRevokedUidsSkipped = true
+ }
+
+ fun MutateStateScope.addKillRuntimePermissionRevokedUidsReason(reason: String) {
+ killRuntimePermissionRevokedUidsReasons += reason
+ }
+
override fun onPermissionFlagsChanged(
appId: Int,
userId: Int,
@@ -799,14 +1558,22 @@ class PermissionService(
}
runtimePermissionChangedUids.clear()
- runtimePermissionRevokedUids.forEachIndexed {
- _, uid, areOnlyNotificationsPermissionsRevoked ->
- handler.post {
- if (areOnlyNotificationsPermissionsRevoked &&
- isAppBackupAndRestoreRunning(uid)) {
- return@post
+ if (!isKillRuntimePermissionRevokedUidsSkipped) {
+ val reason = if (killRuntimePermissionRevokedUidsReasons.isNotEmpty()) {
+ killRuntimePermissionRevokedUidsReasons.joinToString(", ")
+ } else {
+ PermissionManager.KILL_APP_REASON_PERMISSIONS_REVOKED
+ }
+ runtimePermissionRevokedUids.forEachIndexed {
+ _, uid, areOnlyNotificationsPermissionsRevoked ->
+ handler.post {
+ if (areOnlyNotificationsPermissionsRevoked &&
+ isAppBackupAndRestoreRunning(uid)
+ ) {
+ return@post
+ }
+ killUid(uid, reason)
}
- killUid(uid, PermissionManager.KILL_APP_REASON_PERMISSIONS_REVOKED)
}
}
runtimePermissionRevokedUids.clear()
@@ -815,6 +1582,9 @@ class PermissionService(
handler.post { killUid(uid, PermissionManager.KILL_APP_REASON_GIDS_CHANGED) }
}
gidsChangedUids.clear()
+
+ isKillRuntimePermissionRevokedUidsSkipped = false
+ killRuntimePermissionRevokedUidsReasons.clear()
}
private fun isAppBackupAndRestoreRunning(uid: Int): Boolean {
@@ -891,8 +1661,21 @@ class PermissionService(
@EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
private val BACKGROUND_RATIONALE_CHANGE_ID = 147316723L
+ private val FULLER_PERMISSIONS = IndexedMap<String, String>().apply {
+ this[Manifest.permission.ACCESS_COARSE_LOCATION] =
+ Manifest.permission.ACCESS_FINE_LOCATION
+ this[Manifest.permission.INTERACT_ACROSS_USERS] =
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL
+ }
+
private val NOTIFICATIONS_PERMISSIONS = indexedSetOf(
Manifest.permission.POST_NOTIFICATIONS
)
+
+ private const val REVIEW_REQUIRED_FLAGS = PermissionFlags.LEGACY_GRANTED or
+ PermissionFlags.IMPLICIT
+ private const val UNREQUESTABLE_MASK = PermissionFlags.RESTRICTION_REVOKED or
+ PermissionFlags.SYSTEM_FIXED or PermissionFlags.POLICY_FIXED or
+ PermissionFlags.USER_FIXED
}
}
diff --git a/services/permission/java/com/android/server/permission/access/util/BinderExtensions.kt b/services/permission/java/com/android/server/permission/access/util/BinderExtensions.kt
new file mode 100644
index 000000000000..a55897dabd07
--- /dev/null
+++ b/services/permission/java/com/android/server/permission/access/util/BinderExtensions.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.permission.access.util
+
+import android.os.Binder
+import kotlin.reflect.KClass
+
+inline fun <R> KClass<Binder>.withClearedCallingIdentity(action: () -> R): R {
+ val token = Binder.clearCallingIdentity()
+ try {
+ return action()
+ } finally {
+ Binder.restoreCallingIdentity(token)
+ }
+}