summaryrefslogtreecommitdiff
path: root/services/permission/java
diff options
context:
space:
mode:
author Manjeet Rulhania <mrulhania@google.com> 2022-12-09 07:14:50 +0000
committer Manjeet Rulhania <mrulhania@google.com> 2022-12-16 05:40:43 +0000
commit14417f4b4b367be6885963832c930d1643e00310 (patch)
tree490402255408d1c4624f188bd02934e586a68f4c /services/permission/java
parentfa5de8763ef0f337fad1ce817a735fb9f7e01ef7 (diff)
Permission compatibility layer for new subsystem
Bug: 252884423 Test: presubmit Change-Id: I9718cf13bec4fb4e5d3b39bddafe69e89310e2e1
Diffstat (limited to 'services/permission/java')
-rw-r--r--services/permission/java/com/android/server/permission/access/collection/IndexedMap.kt7
-rw-r--r--services/permission/java/com/android/server/permission/access/permission/Permission.kt9
-rw-r--r--services/permission/java/com/android/server/permission/access/permission/PermissionService.kt377
-rw-r--r--services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt14
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 b2f52cc814cb..022d966f1304 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
@@ -829,11 +829,17 @@ class UidPermissionPolicy : SchemePolicy() {
with(persistence) { this@serializeSystemState.serializeSystemState(systemState) }
}
- 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)