summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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)
+ }
+}