Merge "Permission compatibility layer for new subsystem"

This commit is contained in:
Manjeet Rulhania
2022-12-16 22:00:18 +00:00
committed by Android (Google) Code Review
4 changed files with 389 additions and 18 deletions

View File

@@ -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)

View File

@@ -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

View File

@@ -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
}
}

View File

@@ -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)