diff options
| author | 2022-12-16 22:00:18 +0000 | |
|---|---|---|
| committer | 2022-12-16 22:00:18 +0000 | |
| commit | ac2625acb86875896c019a8c05fd4c9922f08896 (patch) | |
| tree | 2b27683d6788b231bcc042b4e4407ea92c17d43c /services/permission/java | |
| parent | 4e1fd41c374fb407c86adca9057167fef7cc0ffe (diff) | |
| parent | 14417f4b4b367be6885963832c930d1643e00310 (diff) | |
Merge "Permission compatibility layer for new subsystem"
Diffstat (limited to 'services/permission/java')
4 files changed, 389 insertions, 18 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 43f18e27f7aa..1e73be7923ae 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,6 +148,13 @@ 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> = + IndexedList<R>().also { destination -> + forEachIndexed { _, key, value -> + transform(key, value)?.let { destination += it } + } + } + @Suppress("NOTHING_TO_INLINE") inline operator fun <K, V> IndexedMap<K, V>.set(key: K, value: V) { put(key, value) diff --git a/services/permission/java/com/android/server/permission/access/permission/Permission.kt b/services/permission/java/com/android/server/permission/access/permission/Permission.kt index efa5bf3118d6..cbf94f565e63 100644 --- a/services/permission/java/com/android/server/permission/access/permission/Permission.kt +++ b/services/permission/java/com/android/server/permission/access/permission/Permission.kt @@ -31,6 +31,9 @@ data class Permission( inline val packageName: String get() = permissionInfo.packageName + inline val groupName: String? + get() = permissionInfo.group + inline val isDynamic: Boolean get() = type == TYPE_DYNAMIC @@ -40,6 +43,9 @@ data class Permission( inline val isRuntime: Boolean get() = permissionInfo.protection == PermissionInfo.PROTECTION_DANGEROUS + inline val isAppOp: Boolean + get() = permissionInfo.protection == PermissionInfo.PROTECTION_FLAG_APPOP + inline val isSoftRestricted: Boolean get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.FLAG_SOFT_RESTRICTED) @@ -109,6 +115,9 @@ data class Permission( inline val isKnownSigner: Boolean get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_KNOWN_SIGNER) + inline val hasGids: Boolean + get() = throw NotImplementedError() + inline val protectionLevel: Int @Suppress("DEPRECATION") get() = permissionInfo.protectionLevel 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 82017362da29..a70468e2400d 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 @@ -16,6 +16,11 @@ package com.android.server.permission.access.permission +import android.Manifest +import android.app.ActivityManager +import android.compat.annotation.ChangeId +import android.compat.annotation.EnabledAfter +import android.content.Context import android.content.pm.PackageManager import android.content.pm.PackageManagerInternal import android.content.pm.PermissionGroupInfo @@ -23,17 +28,32 @@ import android.content.pm.PermissionInfo import android.content.pm.permission.SplitPermissionInfoParcelable import android.os.Binder import android.os.Build +import android.os.Handler +import android.os.HandlerThread +import android.os.Looper +import android.os.Message import android.os.Process +import android.os.RemoteCallbackList +import android.os.RemoteException +import android.os.ServiceManager import android.os.UserHandle import android.permission.IOnPermissionsChangeListener +import android.permission.PermissionManager +import android.provider.Settings +import android.util.Log +import com.android.internal.compat.IPlatformCompat +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.pm.PackageManagerLocal import com.android.server.pm.permission.PermissionManagerServiceInterface import com.android.server.permission.access.AccessCheckingService 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.hasAnyBit import com.android.server.permission.access.util.hasBits import com.android.server.pm.UserManagerService import com.android.server.pm.permission.LegacyPermission @@ -53,21 +73,56 @@ class PermissionService( private val policy = service.getSchemePolicy(UidUri.SCHEME, PermissionUri.SCHEME) as UidPermissionPolicy + private val context = service.context private lateinit var packageManagerInternal: PackageManagerInternal private lateinit var packageManagerLocal: PackageManagerLocal + private lateinit var platformCompat: IPlatformCompat 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 permissionFlagsListener: OnPermissionFlagsChangedListener + fun initialize() { packageManagerInternal = LocalServices.getService(PackageManagerInternal::class.java) packageManagerLocal = LocalManagerRegistry.getManagerOrThrow(PackageManagerLocal::class.java) + platformCompat = IPlatformCompat.Stub.asInterface( + ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE) + ) userManagerService = UserManagerService.getInstance() + + handlerThread = ServiceThread(LOG_TAG, Process.THREAD_PRIORITY_BACKGROUND, true) + handler = Handler(handlerThread.looper) + + onPermissionsChangeListeners = OnPermissionsChangeListeners(FgThread.get().looper) + permissionFlagsListener = OnPermissionFlagsChangedListener() + policy.addOnPermissionFlagsChangedListener(permissionFlagsListener) } override fun getAllPermissionGroups(flags: Int): List<PermissionGroupInfo> { - TODO("Not yet implemented") + val callingUid = Binder.getCallingUid() + packageManagerLocal.withUnfilteredSnapshot().use { snapshot -> + if (snapshot.isUidInstantApp(callingUid)) { + return emptyList() + } + + val permissionGroups = service.getState { + with(policy) { getPermissionGroups() } + } + + return permissionGroups.mapNotNullIndexed { _, permissionGroup -> + if (snapshot.isPackageVisibleToUid(permissionGroup.packageName, callingUid)) { + permissionGroup.generatePermissionGroupInfo(flags) + } else { + null + } + } + } } override fun getPermissionGroupInfo( @@ -82,7 +137,7 @@ class PermissionService( } permissionGroup = service.getState { - with(policy) { getPermissionGroup(permissionGroupName) } + with(policy) { getPermissionGroups()[permissionGroupName] } } ?: return null val isPermissionGroupVisible = @@ -120,7 +175,7 @@ class PermissionService( } permission = service.getState { - with(policy) { getPermission(permissionName) } + with(policy) { getPermissions()[permissionName] } } ?: return null val isPermissionVisible = @@ -148,7 +203,7 @@ class PermissionService( */ private fun Permission.generatePermissionInfo( flags: Int, - targetSdkVersion: Int + targetSdkVersion: Int = Build.VERSION_CODES.CUR_DEVELOPMENT ): PermissionInfo = @Suppress("DEPRECATION") PermissionInfo(permissionInfo).apply { @@ -165,10 +220,40 @@ class PermissionService( } override fun queryPermissionsByGroup( - permissionGroupName: String, + permissionGroupName: String?, flags: Int - ): List<PermissionInfo> { - TODO("Not yet implemented") + ): List<PermissionInfo>? { + val callingUid = Binder.getCallingUid() + packageManagerLocal.withUnfilteredSnapshot().use { snapshot -> + if (snapshot.isUidInstantApp(callingUid)) { + return null + } + + if (permissionGroupName != null) { + val permissionGroup = service.getState { + with(policy) { getPermissionGroups()[permissionGroupName] } + } ?: return null + + if (!snapshot.isPackageVisibleToUid( + permissionGroup.packageName, callingUid + )) { + return null + } + } + + val permissions = service.getState { + with(policy) { getPermissions() } + } + + return permissions.mapNotNullIndexed { _, permission -> + if (permission.groupName == permissionGroupName && + snapshot.isPackageVisibleToUid(permission.packageName, callingUid)) { + permission.generatePermissionInfo(flags) + } else { + null + } + } + } } override fun getAllPermissionsWithProtection(protection: Int): List<PermissionInfo> { @@ -224,11 +309,11 @@ class PermissionService( } override fun addOnPermissionsChangeListener(listener: IOnPermissionsChangeListener) { - TODO("Not yet implemented") + onPermissionsChangeListeners.addListener(listener) } override fun removeOnPermissionsChangeListener(listener: IOnPermissionsChangeListener) { - TODO("Not yet implemented") + onPermissionsChangeListeners.removeListener(listener) } override fun getPermissionFlags(packageName: String, permissionName: String, userId: Int): Int { @@ -245,16 +330,49 @@ class PermissionService( permissionName: String, userId: Int ): Boolean { - TODO("Not yet implemented") + if (UserHandle.getCallingUserId() != userId) { + context.enforceCallingPermission( + Manifest.permission.INTERACT_ACROSS_USERS_FULL, + "isPermissionRevokedByPolicy for user $userId" + ) + } + + if (checkPermission(packageName, permissionName, userId) == + PackageManager.PERMISSION_GRANTED) { + return false + } + + val callingUid = Binder.getCallingUid() + if (packageManagerLocal.withUnfilteredSnapshot() + .use { !it.isPackageVisibleToUid(packageName, userId, callingUid) }) { + return false + } + + val permissionFlags = getPermissionFlagsUnchecked(packageName, + permissionName, callingUid, userId) + return permissionFlags.hasBits(PackageManager.FLAG_PERMISSION_POLICY_FIXED) + } + + private fun getPermissionFlagsUnchecked( + packageName: String, + permName: String, + callingUid: Int, + userId: Int + ): Int { + throw NotImplementedError() } override fun isPermissionsReviewRequired(packageName: String, userId: Int): Boolean { + requireNotNull(packageName) { "packageName" } + // TODO(b/173235285): Some caller may pass USER_ALL as userId. + // Preconditions.checkArgumentNonnegative(userId, "userId"); val packageState = packageManagerLocal.withUnfilteredSnapshot() .use { it.packageStates[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 { _, _, flags -> PermissionFlags.isReviewRequired(flags) + } } override fun shouldShowRequestPermissionRationale( @@ -262,7 +380,70 @@ class PermissionService( permissionName: String, userId: Int ): Boolean { - TODO("Not yet implemented") + val callingUid = Binder.getCallingUid() + if (UserHandle.getCallingUserId() != userId) { + context.enforceCallingPermission( + Manifest.permission.INTERACT_ACROSS_USERS_FULL, + "canShowRequestPermissionRationale for user $userId" + ) + } + + val appId = packageManagerLocal.withUnfilteredSnapshot().use { snapshot -> + snapshot.packageStates[packageName]?.appId ?: return false + } + if (UserHandle.getAppId(callingUid) != appId) { + return false + } + + if (checkPermission(packageName, permissionName, userId) == + PackageManager.PERMISSION_GRANTED) { + 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) + + if (permissionFlags.hasAnyBit(fixedFlags) || + permissionFlags.hasBits(PermissionFlags.RESTRICTION_REVOKED)) { + return false + } + + val token = Binder.clearCallingIdentity() + try { + if (permissionName == Manifest.permission.ACCESS_BACKGROUND_LOCATION && + platformCompat.isChangeEnabledByPackageName( + BACKGROUND_RATIONALE_CHANGE_ID, packageName, userId) + ) { + 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() } override fun updatePermissionFlags( @@ -327,7 +508,9 @@ class PermissionService( } override fun getSplitPermissions(): List<SplitPermissionInfoParcelable> { - TODO("Not yet implemented") + return PermissionManager.splitPermissionInfoListToParcelableList( + SystemConfig.getInstance().splitPermissions + ) } override fun getAppOpPermissionPackages(permissionName: String): Array<String> { @@ -335,7 +518,22 @@ class PermissionService( } override fun getAllAppOpPermissionPackages(): Map<String, Set<String>> { - TODO("Not yet implemented") + val appOpPermissionPackageNames = IndexedMap<String, IndexedSet<String>>() + val permissions = service.getState { with(policy) { getPermissions() } } + packageManagerLocal.withUnfilteredSnapshot().use { snapshot -> + snapshot.packageStates.forEach packageStates@{ (_, packageState) -> + val androidPackage = packageState.androidPackage ?: return@packageStates + androidPackage.requestedPermissions.forEach requestedPermissions@{ permissionName -> + val permission = permissions[permissionName] ?: return@requestedPermissions + if (permission.isAppOp) { + val packageNames = appOpPermissionPackageNames + .getOrPut(permissionName) { IndexedSet() } + packageNames += androidPackage.packageName + } + } + } + } + return appOpPermissionPackageNames } override fun getGidsForUid(uid: Int): IntArray { @@ -468,6 +666,28 @@ class PermissionService( } } + + /** + * This method should typically only be used when granting or revoking permissions, since the + * app may immediately restart after this call. + * + * If you're doing surgery on app code/data, use [PackageFreezer] to guard your work against + * the app being relaunched. + */ + private fun killUid(appId: Int, userId: Int, reason: String) { + val activityManager = ActivityManager.getService() + if (activityManager != null) { + val identity = Binder.clearCallingIdentity() + try { + activityManager.killUidForPermissionChange(appId, userId, reason) + } catch (e: RemoteException) { + /* ignore - same process */ + } finally { + Binder.restoreCallingIdentity(identity) + } + } + } + /** * Check whether a UID belongs to an instant app. */ @@ -504,4 +724,133 @@ class PermissionService( val user = UserHandle.of(userId) return filtered(uid, user).use { it.getPackageState(packageName) != null } } + + /** + * Callback invoked when interesting actions have been taken on a permission. + */ + private inner class OnPermissionFlagsChangedListener : + UidPermissionPolicy.OnPermissionFlagsChangedListener { + override fun onPermissionFlagsChanged( + appId: Int, + userId: Int, + permissionName: String, + oldFlags: Int, + newFlags: Int + ) { + val uid = UserHandle.getUid(userId, appId) + val permission = service.getState { + with(policy) { getPermissions()[permissionName] } + } ?: return + + val isPermissionGranted = !PermissionFlags.isPermissionGranted(oldFlags) && + PermissionFlags.isPermissionGranted(newFlags) + val isPermissionRevoked = PermissionFlags.isPermissionGranted(oldFlags) && + !PermissionFlags.isPermissionGranted(newFlags) + + if (isPermissionGranted) { + if (permission.isRuntime) { + onPermissionsChangeListeners.onPermissionsChanged(uid) + } + handler.post { + if (permission.hasGids) { + killUid(appId, userId, PermissionManager.KILL_APP_REASON_GIDS_CHANGED) + } + } + } else if (isPermissionRevoked) { + // TODO: STOPSHIP skip kill for revokePostNotificationPermissionWithoutKillForTest + if (permission.isRuntime) { + onPermissionsChangeListeners.onPermissionsChanged(uid) + handler.post { + if (!(permissionName == Manifest.permission.POST_NOTIFICATIONS && + isAppBackupAndRestoreRunning(uid))) { + killUid( + appId, userId, PermissionManager.KILL_APP_REASON_PERMISSIONS_REVOKED + ) + } + } + } + } else if (oldFlags != newFlags) { + onPermissionsChangeListeners.onPermissionsChanged(uid) + } + } + + private fun isAppBackupAndRestoreRunning(uid: Int): Boolean { + if (checkUidPermission(uid, Manifest.permission.BACKUP) != + PackageManager.PERMISSION_GRANTED) { + return false + } + return try { + val userId = UserHandle.getUserId(uid) + val isInSetup = Settings.Secure.getIntForUser( + context.contentResolver, Settings.Secure.USER_SETUP_COMPLETE, userId + ) == 0 + val isInDeferredSetup = Settings.Secure.getIntForUser( + context.contentResolver, + Settings.Secure.USER_SETUP_PERSONALIZATION_STATE, userId + ) == Settings.Secure.USER_SETUP_PERSONALIZATION_STARTED + isInSetup || isInDeferredSetup + } catch (e: Settings.SettingNotFoundException) { + Log.w(LOG_TAG, "Failed to check if the user is in restore: $e") + false + } + } + } + + private class OnPermissionsChangeListeners(looper: Looper) : Handler(looper) { + private val listeners = RemoteCallbackList<IOnPermissionsChangeListener>() + + override fun handleMessage(msg: Message) { + when (msg.what) { + MSG_ON_PERMISSIONS_CHANGED -> { + val uid = msg.arg1 + handleOnPermissionsChanged(uid) + } + } + } + + private fun handleOnPermissionsChanged(uid: Int) { + val count = listeners.beginBroadcast() + try { + for (i in 0 until count) { + val callback = listeners.getBroadcastItem(i) + try { + callback.onPermissionsChanged(uid) + } catch (e: RemoteException) { + Log.e(LOG_TAG, "Permission listener is dead", e) + } + } + } finally { + listeners.finishBroadcast() + } + } + + fun addListener(listener: IOnPermissionsChangeListener) { + listeners.register(listener) + } + + fun removeListener(listener: IOnPermissionsChangeListener) { + listeners.unregister(listener) + } + + fun onPermissionsChanged(uid: Int) { + if (listeners.registeredCallbackCount > 0) { + obtainMessage(MSG_ON_PERMISSIONS_CHANGED, uid, 0).sendToTarget() + } + } + companion object { + private const val MSG_ON_PERMISSIONS_CHANGED = 1 + } + } + + companion object { + private val LOG_TAG = PermissionService::class.java.simpleName + + /** + * This change makes it so that apps are told to show rationale for asking for background + * location access every time they request. + */ + @ChangeId + @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q) + private val BACKGROUND_RATIONALE_CHANGE_ID = 147316723L + } } diff --git a/services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt index 76d5ab0d3a85..ac82715f3220 100644 --- a/services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt +++ b/services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt @@ -836,11 +836,17 @@ class UidPermissionPolicy : SchemePolicy() { with(persistence) { this@serializeUserState.serializeUserState(state, userId) } } - fun GetStateScope.getPermissionGroup(permissionGroupName: String): PermissionGroupInfo? = - state.systemState.permissionGroups[permissionGroupName] + /** + * returns all permission group definitions available in the system + */ + fun GetStateScope.getPermissionGroups(): IndexedMap<String, PermissionGroupInfo> = + state.systemState.permissionGroups - fun GetStateScope.getPermission(permissionName: String): Permission? = - state.systemState.permissions[permissionName] + /** + * returns all permission definitions available in the system + */ + fun GetStateScope.getPermissions(): IndexedMap<String, Permission> = + state.systemState.permissions fun GetStateScope.getUidPermissionFlags(appId: Int, userId: Int): IndexedMap<String, Int>? = state.userStates[userId]?.uidPermissionFlags?.get(appId) |