Loading core/java/android/permission/flags.aconfig +7 −0 Original line number Original line Diff line number Diff line Loading @@ -86,3 +86,10 @@ flag { description: "This flag is used to enabled the Wallet Role for all users on the device" description: "This flag is used to enabled the Wallet Role for all users on the device" bug: "283989236" bug: "283989236" } } flag { name: "runtime_permission_appops_mapping" namespace: "permissions" description: "Use runtime permission state to determine appop state" bug: "266164193" } services/permission/java/com/android/server/permission/access/appop/AppIdAppOpPolicy.kt +9 −0 Original line number Original line Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.server.permission.access.appop package com.android.server.permission.access.appop import android.app.AppOpsManager import android.app.AppOpsManager import android.util.Slog import com.android.server.permission.access.GetStateScope import com.android.server.permission.access.GetStateScope import com.android.server.permission.access.MutableAccessState import com.android.server.permission.access.MutableAccessState import com.android.server.permission.access.MutateStateScope import com.android.server.permission.access.MutateStateScope Loading Loading @@ -84,6 +85,10 @@ class AppIdAppOpPolicy : BaseAppOpPolicy(AppIdAppOpPersistence()) { appOpName: String, appOpName: String, mode: Int mode: Int ): Boolean { ): Boolean { if (userId !in newState.userStates) { Slog.e(LOG_TAG, "Unable to set app op mode for missing user $userId") return false } val defaultMode = AppOpsManager.opToDefaultMode(appOpName) val defaultMode = AppOpsManager.opToDefaultMode(appOpName) val oldMode = val oldMode = newState.userStates[userId]!! newState.userStates[userId]!! Loading Loading @@ -152,4 +157,8 @@ class AppIdAppOpPolicy : BaseAppOpPolicy(AppIdAppOpPersistence()) { */ */ abstract fun onStateMutated() abstract fun onStateMutated() } } companion object { private val LOG_TAG = AppIdAppOpPolicy::class.java.simpleName } } } services/permission/java/com/android/server/permission/access/appop/AppOpService.kt +291 −17 Original line number Original line Diff line number Diff line Loading @@ -17,29 +17,62 @@ package com.android.server.permission.access.appop package com.android.server.permission.access.appop import android.app.AppOpsManager import android.app.AppOpsManager import android.os.Binder import android.os.Handler import android.os.Handler import android.os.UserHandle import android.os.UserHandle import android.permission.flags.Flags import android.util.ArrayMap import android.util.ArrayMap import android.util.ArraySet import android.util.ArraySet import android.util.LongSparseArray import android.util.Slog import android.util.SparseArray import android.util.SparseBooleanArray import android.util.SparseBooleanArray import android.util.SparseIntArray import android.util.SparseIntArray import com.android.internal.annotations.VisibleForTesting import com.android.internal.annotations.VisibleForTesting import com.android.internal.util.IntPair import com.android.server.appop.AppOpsCheckingServiceInterface import com.android.server.appop.AppOpsCheckingServiceInterface import com.android.server.appop.AppOpsCheckingServiceInterface.AppOpsModeChangedListener import com.android.server.appop.AppOpsCheckingServiceInterface.AppOpsModeChangedListener import com.android.server.permission.access.AccessCheckingService import com.android.server.permission.access.AccessCheckingService import com.android.server.permission.access.AppOpUri import com.android.server.permission.access.AppOpUri import com.android.server.permission.access.GetStateScope import com.android.server.permission.access.PackageUri import com.android.server.permission.access.PackageUri import com.android.server.permission.access.PermissionUri import com.android.server.permission.access.UidUri import com.android.server.permission.access.UidUri import com.android.server.permission.access.appop.AppOpModes.MODE_ALLOWED import com.android.server.permission.access.appop.AppOpModes.MODE_FOREGROUND import com.android.server.permission.access.appop.AppOpModes.MODE_IGNORED import com.android.server.permission.access.collection.forEachIndexed import com.android.server.permission.access.collection.forEachIndexed import com.android.server.permission.access.collection.set import com.android.server.permission.access.collection.set import com.android.server.permission.access.permission.AppIdPermissionPolicy import com.android.server.permission.access.permission.PermissionFlags import com.android.server.permission.access.permission.PermissionService class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingServiceInterface { class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingServiceInterface { private val packagePolicy = private val packagePolicy = service.getSchemePolicy(PackageUri.SCHEME, AppOpUri.SCHEME) as PackageAppOpPolicy service.getSchemePolicy(PackageUri.SCHEME, AppOpUri.SCHEME) as PackageAppOpPolicy private val appIdPolicy = private val appIdPolicy = service.getSchemePolicy(UidUri.SCHEME, AppOpUri.SCHEME) as AppIdAppOpPolicy service.getSchemePolicy(UidUri.SCHEME, AppOpUri.SCHEME) as AppIdAppOpPolicy private val permissionPolicy = service.getSchemePolicy(UidUri.SCHEME, PermissionUri.SCHEME) as AppIdPermissionPolicy private val context = service.context private val context = service.context // Maps appop code to its runtime permission private val runtimeAppOpToPermissionNames = SparseArray<String>() // Maps runtime permission to its appop codes private val runtimePermissionNameToAppOp = ArrayMap<String, Int>() private var foregroundableOps = SparseBooleanArray() /* Maps foreground permissions to their background permission. Background permissions aren't required to be runtime */ private val foregroundToBackgroundPermissionName = ArrayMap<String, String>() /* Maps background permissions to their foreground permissions. Background permissions aren't required to be runtime */ private val backgroundToForegroundPermissionNames = ArrayMap<String, ArraySet<String>>() private lateinit var handler: Handler private lateinit var handler: Handler @Volatile private var listeners = ArraySet<AppOpsModeChangedListener>() @Volatile private var listeners = ArraySet<AppOpsModeChangedListener>() Loading Loading @@ -68,11 +101,58 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS } } override fun systemReady() { override fun systemReady() { // Not implemented because upgrades are handled automatically. if (useRuntimePermissionAppOpMapping()) { createPermissionAppOpMapping() permissionPolicy.addOnPermissionFlagsChangedListener(OnPermissionFlagsChangedListener()) } } private fun createPermissionAppOpMapping() { val permissions = service.getState { with(permissionPolicy) { getPermissions() } } for (appOpCode in 0 until AppOpsManager._NUM_OP) { AppOpsManager.opToPermission(appOpCode)?.let { permissionName -> // Multiple ops might map to a single permission but only one is considered the // runtime appop calculations. if (appOpCode == AppOpsManager.permissionToOpCode(permissionName)) { val permission = permissions[permissionName]!! if (permission.isRuntime) { runtimePermissionNameToAppOp[permissionName] = appOpCode runtimeAppOpToPermissionNames[appOpCode] = permissionName permission.permissionInfo.backgroundPermission?.let { backgroundPermissionName -> // Note: background permission may not be runtime, // e.g. microphone/camera. foregroundableOps[appOpCode] = true foregroundToBackgroundPermissionName[permissionName] = backgroundPermissionName backgroundToForegroundPermissionNames .getOrPut(backgroundPermissionName, ::ArraySet) .add(permissionName) } } } } } } } override fun getNonDefaultUidModes(uid: Int, persistentDeviceId: String): SparseIntArray { override fun getNonDefaultUidModes(uid: Int, persistentDeviceId: String): SparseIntArray { return opNameMapToOpSparseArray(getUidModes(uid)) val appId = UserHandle.getAppId(uid) val userId = UserHandle.getUserId(uid) service.getState { val modes = with(appIdPolicy) { opNameMapToOpSparseArray(getAppOpModes(appId, userId)?.map) } if (useRuntimePermissionAppOpMapping()) { runtimePermissionNameToAppOp.forEachIndexed { _, permissionName, appOpCode -> val mode = getUidModeFromPermissionState(appId, userId, permissionName) if (mode != AppOpsManager.opToDefaultMode(appOpCode)) { modes[appOpCode] = mode } } } return modes } } } override fun getNonDefaultPackageModes(packageName: String, userId: Int): SparseIntArray { override fun getNonDefaultPackageModes(packageName: String, userId: Int): SparseIntArray { Loading @@ -83,7 +163,13 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS val appId = UserHandle.getAppId(uid) val appId = UserHandle.getAppId(uid) val userId = UserHandle.getUserId(uid) val userId = UserHandle.getUserId(uid) val opName = AppOpsManager.opToPublicName(op) val opName = AppOpsManager.opToPublicName(op) return service.getState { with(appIdPolicy) { getAppOpMode(appId, userId, opName) } } val permissionName = runtimeAppOpToPermissionNames[op] return if (!useRuntimePermissionAppOpMapping() || permissionName == null) { service.getState { with(appIdPolicy) { getAppOpMode(appId, userId, opName) } } } else { service.getState { getUidModeFromPermissionState(appId, userId, permissionName) } } } } private fun getUidModes(uid: Int): ArrayMap<String, Int>? { private fun getUidModes(uid: Int): ArrayMap<String, Int>? { Loading @@ -92,13 +178,63 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS return service.getState { with(appIdPolicy) { getAppOpModes(appId, userId) } }?.map return service.getState { with(appIdPolicy) { getAppOpModes(appId, userId) } }?.map } } override fun setUidMode(uid: Int, persistentDeviceId: String, op: Int, mode: Int): Boolean { private fun GetStateScope.getUidModeFromPermissionState( appId: Int, userId: Int, permissionName: String ): Int { val permissionFlags = with(permissionPolicy) { getPermissionFlags(appId, userId, permissionName) } val backgroundPermissionName = foregroundToBackgroundPermissionName[permissionName] val backgroundPermissionFlags = if (backgroundPermissionName != null) { with(permissionPolicy) { getPermissionFlags(appId, userId, backgroundPermissionName) } } else { PermissionFlags.RUNTIME_GRANTED } val result = evaluateModeFromPermissionFlags(permissionFlags, backgroundPermissionFlags) if (result != MODE_IGNORED) { return result } val fullerPermissionName = PermissionService.getFullerPermission(permissionName) ?: return result return getUidModeFromPermissionState(appId, userId, fullerPermissionName) } private fun evaluateModeFromPermissionFlags( foregroundFlags: Int, backgroundFlags: Int = PermissionFlags.RUNTIME_GRANTED ): Int = if (PermissionFlags.isAppOpGranted(foregroundFlags)) { if (PermissionFlags.isAppOpGranted(backgroundFlags)) { MODE_ALLOWED } else { MODE_FOREGROUND } } else { MODE_IGNORED } override fun setUidMode(uid: Int, persistentDeviceId: String, code: Int, mode: Int): Boolean { if (useRuntimePermissionAppOpMapping() && code in runtimeAppOpToPermissionNames) { Slog.w( LOG_TAG, "Cannot set UID mode for runtime permission app op, uid = $uid," + " code = ${AppOpsManager.opToName(code)}, mode = ${AppOpsManager.modeToName(mode)}", RuntimeException() ) return false } val appId = UserHandle.getAppId(uid) val appId = UserHandle.getAppId(uid) val userId = UserHandle.getUserId(uid) val userId = UserHandle.getUserId(uid) val opName = AppOpsManager.opToPublicName(op) val appOpName = AppOpsManager.opToPublicName(code) var wasChanged = false var wasChanged: Boolean service.mutateState { service.mutateState { wasChanged = with(appIdPolicy) { setAppOpMode(appId, userId, opName, mode) } wasChanged = with(appIdPolicy) { setAppOpMode(appId, userId, appOpName, mode) } } } return wasChanged return wasChanged } } Loading @@ -113,10 +249,22 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS private fun getPackageModes(packageName: String, userId: Int): ArrayMap<String, Int>? = private fun getPackageModes(packageName: String, userId: Int): ArrayMap<String, Int>? = service.getState { with(packagePolicy) { getAppOpModes(packageName, userId) } }?.map service.getState { with(packagePolicy) { getAppOpModes(packageName, userId) } }?.map override fun setPackageMode(packageName: String, op: Int, mode: Int, userId: Int) { override fun setPackageMode(packageName: String, appOpCode: Int, mode: Int, userId: Int) { val opName = AppOpsManager.opToPublicName(op) val appOpName = AppOpsManager.opToPublicName(appOpCode) if ( useRuntimePermissionAppOpMapping() && runtimeAppOpToPermissionNames.contains(appOpCode) ) { Slog.w( LOG_TAG, "(packageName=$packageName, userId=$userId)'s appop state" + " for runtime op $appOpName should not be set directly.", RuntimeException() ) return } service.mutateState { service.mutateState { with(packagePolicy) { setAppOpMode(packageName, userId, opName, mode) } with(packagePolicy) { setAppOpMode(packageName, userId, appOpName, mode) } } } } } Loading @@ -127,7 +275,7 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS } } override fun removePackage(packageName: String, userId: Int): Boolean { override fun removePackage(packageName: String, userId: Int): Boolean { var wasChanged = false var wasChanged: Boolean service.mutateState { service.mutateState { wasChanged = with(packagePolicy) { removeAppOpModes(packageName, userId) } wasChanged = with(packagePolicy) { removeAppOpModes(packageName, userId) } } } Loading Loading @@ -157,6 +305,13 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS this[AppOpsManager.strOpToOp(op)] = true this[AppOpsManager.strOpToOp(op)] = true } } } } if (useRuntimePermissionAppOpMapping()) { foregroundableOps.forEachIndexed { _, op, _ -> if (getUidMode(uid, persistentDeviceId, op) == AppOpsManager.MODE_FOREGROUND) { this[op] = true } } } } } } } Loading @@ -167,6 +322,13 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS this[AppOpsManager.strOpToOp(op)] = true this[AppOpsManager.strOpToOp(op)] = true } } } } if (useRuntimePermissionAppOpMapping()) { foregroundableOps.forEachIndexed { _, op, _ -> if (getPackageMode(packageName, op, userId) == AppOpsManager.MODE_FOREGROUND) { this[op] = true } } } } } } } Loading @@ -188,9 +350,10 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS } } } } inner class OnAppIdAppOpModeChangedListener : AppIdAppOpPolicy.OnAppOpModeChangedListener() { private inner class OnAppIdAppOpModeChangedListener : AppIdAppOpPolicy.OnAppOpModeChangedListener() { // (uid, appOpCode) -> newMode // (uid, appOpCode) -> newMode val pendingChanges = ArrayMap<Pair<Int, Int>, Int>() private val pendingChanges = LongSparseArray<Int>() override fun onAppOpModeChanged( override fun onAppOpModeChanged( appId: Int, appId: Int, Loading @@ -201,7 +364,7 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS ) { ) { val uid = UserHandle.getUid(userId, appId) val uid = UserHandle.getUid(userId, appId) val appOpCode = AppOpsManager.strOpToOp(appOpName) val appOpCode = AppOpsManager.strOpToOp(appOpName) val key = Pair(uid, appOpCode) val key = IntPair.of(uid, appOpCode) pendingChanges[key] = newMode pendingChanges[key] = newMode } } Loading @@ -210,8 +373,8 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS val listenersLocal = listeners val listenersLocal = listeners pendingChanges.forEachIndexed { _, key, mode -> pendingChanges.forEachIndexed { _, key, mode -> listenersLocal.forEachIndexed { _, listener -> listenersLocal.forEachIndexed { _, listener -> val uid = key.first val uid = IntPair.first(key) val appOpCode = key.second val appOpCode = IntPair.second(key) listener.onUidModeChanged(uid, appOpCode, mode) listener.onUidModeChanged(uid, appOpCode, mode) } } Loading @@ -224,7 +387,7 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS private inner class OnPackageAppOpModeChangedListener : private inner class OnPackageAppOpModeChangedListener : PackageAppOpPolicy.OnAppOpModeChangedListener() { PackageAppOpPolicy.OnAppOpModeChangedListener() { // (packageName, userId, appOpCode) -> newMode // (packageName, userId, appOpCode) -> newMode val pendingChanges = ArrayMap<Triple<String, Int, Int>, Int>() private val pendingChanges = ArrayMap<Triple<String, Int, Int>, Int>() override fun onAppOpModeChanged( override fun onAppOpModeChanged( packageName: String, packageName: String, Loading Loading @@ -254,4 +417,115 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS pendingChanges.clear() pendingChanges.clear() } } } } private inner class OnPermissionFlagsChangedListener : AppIdPermissionPolicy.OnPermissionFlagsChangedListener { // (uid, appOpCode) -> newMode private val pendingChanges = LongSparseArray<Int>() override fun onPermissionFlagsChanged( appId: Int, userId: Int, permissionName: String, oldFlags: Int, newFlags: Int ) { backgroundToForegroundPermissionNames[permissionName]?.let { foregroundPermissions -> // This is a background permission; there may be multiple foreground permissions // affected. foregroundPermissions.forEachIndexed { _, foregroundPermissionName -> runtimePermissionNameToAppOp[foregroundPermissionName]?.let { appOpCode -> val foregroundPermissionFlags = getPermissionFlags(appId, userId, foregroundPermissionName) addPendingChangedModeIfNeeded( appId, userId, appOpCode, foregroundPermissionFlags, oldFlags, foregroundPermissionFlags, newFlags ) } } } ?: foregroundToBackgroundPermissionName[permissionName]?.let { backgroundPermission -> runtimePermissionNameToAppOp[permissionName]?.let { appOpCode -> val backgroundPermissionFlags = getPermissionFlags(appId, userId, backgroundPermission) addPendingChangedModeIfNeeded( appId, userId, appOpCode, oldFlags, backgroundPermissionFlags, newFlags, backgroundPermissionFlags ) } } ?: runtimePermissionNameToAppOp[permissionName]?.let { appOpCode -> addPendingChangedModeIfNeeded( appId, userId, appOpCode, oldFlags, PermissionFlags.RUNTIME_GRANTED, newFlags, PermissionFlags.RUNTIME_GRANTED ) } } private fun getPermissionFlags(appId: Int, userId: Int, permissionName: String): Int = service.getState { with(permissionPolicy) { getPermissionFlags(appId, userId, permissionName) } } private fun addPendingChangedModeIfNeeded( appId: Int, userId: Int, appOpCode: Int, oldForegroundFlags: Int, oldBackgroundFlags: Int, newForegroundFlags: Int, newBackgroundFlags: Int, ) { val oldMode = evaluateModeFromPermissionFlags(oldForegroundFlags, oldBackgroundFlags) val newMode = evaluateModeFromPermissionFlags(newForegroundFlags, newBackgroundFlags) if (oldMode != newMode) { val uid = UserHandle.getUid(userId, appId) pendingChanges[IntPair.of(uid, appOpCode)] = newMode } } override fun onStateMutated() { val listenersLocal = listeners pendingChanges.forEachIndexed { _, key, mode -> listenersLocal.forEachIndexed { _, listener -> val uid = IntPair.first(key) val appOpCode = IntPair.second(key) listener.onUidModeChanged(uid, appOpCode, mode) } } pendingChanges.clear() } } companion object { private val LOG_TAG = AppOpService::class.java.simpleName private fun useRuntimePermissionAppOpMapping(): Boolean { val token = Binder.clearCallingIdentity() return try { Flags.runtimePermissionAppopsMapping() } finally { Binder.restoreCallingIdentity(token) } } } } } services/permission/java/com/android/server/permission/access/appop/PackageAppOpPolicy.kt +9 −0 Original line number Original line Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.server.permission.access.appop package com.android.server.permission.access.appop import android.app.AppOpsManager import android.app.AppOpsManager import android.util.Slog import com.android.server.permission.access.GetStateScope import com.android.server.permission.access.GetStateScope import com.android.server.permission.access.MutableAccessState import com.android.server.permission.access.MutableAccessState import com.android.server.permission.access.MutateStateScope import com.android.server.permission.access.MutateStateScope Loading Loading @@ -87,6 +88,10 @@ class PackageAppOpPolicy : BaseAppOpPolicy(PackageAppOpPersistence()) { appOpName: String, appOpName: String, mode: Int mode: Int ): Boolean { ): Boolean { if (userId !in newState.userStates) { Slog.e(LOG_TAG, "Unable to set app op mode for missing user $userId") return false } val defaultMode = AppOpsManager.opToDefaultMode(appOpName) val defaultMode = AppOpsManager.opToDefaultMode(appOpName) val oldMode = val oldMode = newState.userStates[userId]!! newState.userStates[userId]!! Loading Loading @@ -155,4 +160,8 @@ class PackageAppOpPolicy : BaseAppOpPolicy(PackageAppOpPersistence()) { */ */ abstract fun onStateMutated() abstract fun onStateMutated() } } companion object { private val LOG_TAG = PackageAppOpPolicy::class.java.simpleName } } } services/permission/java/com/android/server/permission/access/collection/LongSparseArrayExtensions.kt 0 → 100644 +105 −0 Original line number Original line Diff line number Diff line /* * 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.collection import android.util.LongSparseArray inline fun <T> LongSparseArray<T>.allIndexed(predicate: (Int, Long, T) -> Boolean): Boolean { forEachIndexed { index, key, value -> if (!predicate(index, key, value)) { return false } } return true } inline fun <T> LongSparseArray<T>.anyIndexed(predicate: (Int, Long, T) -> Boolean): Boolean { forEachIndexed { index, key, value -> if (predicate(index, key, value)) { return true } } return false } inline fun <T> LongSparseArray<T>.forEachIndexed(action: (Int, Long, T) -> Unit) { for (index in 0 until size) { action(index, keyAt(index), valueAt(index)) } } inline fun <T> LongSparseArray<T>.forEachReversedIndexed(action: (Int, Long, T) -> Unit) { for (index in lastIndex downTo 0) { action(index, keyAt(index), valueAt(index)) } } inline fun <T> LongSparseArray<T>.getOrPut(key: Long, defaultValue: () -> T): T { val index = indexOfKey(key) return if (index >= 0) { valueAt(index) } else { defaultValue().also { put(key, it) } } } inline val <T> LongSparseArray<T>.lastIndex: Int get() = size - 1 @Suppress("NOTHING_TO_INLINE") inline operator fun <T> LongSparseArray<T>.minusAssign(key: Long) { delete(key) } inline fun <T> LongSparseArray<T>.noneIndexed(predicate: (Int, Long, T) -> Boolean): Boolean { forEachIndexed { index, key, value -> if (predicate(index, key, value)) { return false } } return true } inline fun <T> LongSparseArray<T>.removeAllIndexed(predicate: (Int, Long, T) -> Boolean): Boolean { var isChanged = false forEachReversedIndexed { index, key, value -> if (predicate(index, key, value)) { removeAt(index) isChanged = true } } return isChanged } inline fun <T> LongSparseArray<T>.retainAllIndexed(predicate: (Int, Long, T) -> Boolean): Boolean { var isChanged = false forEachReversedIndexed { index, key, value -> if (!predicate(index, key, value)) { removeAt(index) isChanged = true } } return isChanged } inline val <T> LongSparseArray<T>.size: Int get() = size() @Suppress("NOTHING_TO_INLINE") inline operator fun <T> LongSparseArray<T>.set(key: Long, value: T) { put(key, value) } Loading
core/java/android/permission/flags.aconfig +7 −0 Original line number Original line Diff line number Diff line Loading @@ -86,3 +86,10 @@ flag { description: "This flag is used to enabled the Wallet Role for all users on the device" description: "This flag is used to enabled the Wallet Role for all users on the device" bug: "283989236" bug: "283989236" } } flag { name: "runtime_permission_appops_mapping" namespace: "permissions" description: "Use runtime permission state to determine appop state" bug: "266164193" }
services/permission/java/com/android/server/permission/access/appop/AppIdAppOpPolicy.kt +9 −0 Original line number Original line Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.server.permission.access.appop package com.android.server.permission.access.appop import android.app.AppOpsManager import android.app.AppOpsManager import android.util.Slog import com.android.server.permission.access.GetStateScope import com.android.server.permission.access.GetStateScope import com.android.server.permission.access.MutableAccessState import com.android.server.permission.access.MutableAccessState import com.android.server.permission.access.MutateStateScope import com.android.server.permission.access.MutateStateScope Loading Loading @@ -84,6 +85,10 @@ class AppIdAppOpPolicy : BaseAppOpPolicy(AppIdAppOpPersistence()) { appOpName: String, appOpName: String, mode: Int mode: Int ): Boolean { ): Boolean { if (userId !in newState.userStates) { Slog.e(LOG_TAG, "Unable to set app op mode for missing user $userId") return false } val defaultMode = AppOpsManager.opToDefaultMode(appOpName) val defaultMode = AppOpsManager.opToDefaultMode(appOpName) val oldMode = val oldMode = newState.userStates[userId]!! newState.userStates[userId]!! Loading Loading @@ -152,4 +157,8 @@ class AppIdAppOpPolicy : BaseAppOpPolicy(AppIdAppOpPersistence()) { */ */ abstract fun onStateMutated() abstract fun onStateMutated() } } companion object { private val LOG_TAG = AppIdAppOpPolicy::class.java.simpleName } } }
services/permission/java/com/android/server/permission/access/appop/AppOpService.kt +291 −17 Original line number Original line Diff line number Diff line Loading @@ -17,29 +17,62 @@ package com.android.server.permission.access.appop package com.android.server.permission.access.appop import android.app.AppOpsManager import android.app.AppOpsManager import android.os.Binder import android.os.Handler import android.os.Handler import android.os.UserHandle import android.os.UserHandle import android.permission.flags.Flags import android.util.ArrayMap import android.util.ArrayMap import android.util.ArraySet import android.util.ArraySet import android.util.LongSparseArray import android.util.Slog import android.util.SparseArray import android.util.SparseBooleanArray import android.util.SparseBooleanArray import android.util.SparseIntArray import android.util.SparseIntArray import com.android.internal.annotations.VisibleForTesting import com.android.internal.annotations.VisibleForTesting import com.android.internal.util.IntPair import com.android.server.appop.AppOpsCheckingServiceInterface import com.android.server.appop.AppOpsCheckingServiceInterface import com.android.server.appop.AppOpsCheckingServiceInterface.AppOpsModeChangedListener import com.android.server.appop.AppOpsCheckingServiceInterface.AppOpsModeChangedListener import com.android.server.permission.access.AccessCheckingService import com.android.server.permission.access.AccessCheckingService import com.android.server.permission.access.AppOpUri import com.android.server.permission.access.AppOpUri import com.android.server.permission.access.GetStateScope import com.android.server.permission.access.PackageUri import com.android.server.permission.access.PackageUri import com.android.server.permission.access.PermissionUri import com.android.server.permission.access.UidUri import com.android.server.permission.access.UidUri import com.android.server.permission.access.appop.AppOpModes.MODE_ALLOWED import com.android.server.permission.access.appop.AppOpModes.MODE_FOREGROUND import com.android.server.permission.access.appop.AppOpModes.MODE_IGNORED import com.android.server.permission.access.collection.forEachIndexed import com.android.server.permission.access.collection.forEachIndexed import com.android.server.permission.access.collection.set import com.android.server.permission.access.collection.set import com.android.server.permission.access.permission.AppIdPermissionPolicy import com.android.server.permission.access.permission.PermissionFlags import com.android.server.permission.access.permission.PermissionService class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingServiceInterface { class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingServiceInterface { private val packagePolicy = private val packagePolicy = service.getSchemePolicy(PackageUri.SCHEME, AppOpUri.SCHEME) as PackageAppOpPolicy service.getSchemePolicy(PackageUri.SCHEME, AppOpUri.SCHEME) as PackageAppOpPolicy private val appIdPolicy = private val appIdPolicy = service.getSchemePolicy(UidUri.SCHEME, AppOpUri.SCHEME) as AppIdAppOpPolicy service.getSchemePolicy(UidUri.SCHEME, AppOpUri.SCHEME) as AppIdAppOpPolicy private val permissionPolicy = service.getSchemePolicy(UidUri.SCHEME, PermissionUri.SCHEME) as AppIdPermissionPolicy private val context = service.context private val context = service.context // Maps appop code to its runtime permission private val runtimeAppOpToPermissionNames = SparseArray<String>() // Maps runtime permission to its appop codes private val runtimePermissionNameToAppOp = ArrayMap<String, Int>() private var foregroundableOps = SparseBooleanArray() /* Maps foreground permissions to their background permission. Background permissions aren't required to be runtime */ private val foregroundToBackgroundPermissionName = ArrayMap<String, String>() /* Maps background permissions to their foreground permissions. Background permissions aren't required to be runtime */ private val backgroundToForegroundPermissionNames = ArrayMap<String, ArraySet<String>>() private lateinit var handler: Handler private lateinit var handler: Handler @Volatile private var listeners = ArraySet<AppOpsModeChangedListener>() @Volatile private var listeners = ArraySet<AppOpsModeChangedListener>() Loading Loading @@ -68,11 +101,58 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS } } override fun systemReady() { override fun systemReady() { // Not implemented because upgrades are handled automatically. if (useRuntimePermissionAppOpMapping()) { createPermissionAppOpMapping() permissionPolicy.addOnPermissionFlagsChangedListener(OnPermissionFlagsChangedListener()) } } private fun createPermissionAppOpMapping() { val permissions = service.getState { with(permissionPolicy) { getPermissions() } } for (appOpCode in 0 until AppOpsManager._NUM_OP) { AppOpsManager.opToPermission(appOpCode)?.let { permissionName -> // Multiple ops might map to a single permission but only one is considered the // runtime appop calculations. if (appOpCode == AppOpsManager.permissionToOpCode(permissionName)) { val permission = permissions[permissionName]!! if (permission.isRuntime) { runtimePermissionNameToAppOp[permissionName] = appOpCode runtimeAppOpToPermissionNames[appOpCode] = permissionName permission.permissionInfo.backgroundPermission?.let { backgroundPermissionName -> // Note: background permission may not be runtime, // e.g. microphone/camera. foregroundableOps[appOpCode] = true foregroundToBackgroundPermissionName[permissionName] = backgroundPermissionName backgroundToForegroundPermissionNames .getOrPut(backgroundPermissionName, ::ArraySet) .add(permissionName) } } } } } } } override fun getNonDefaultUidModes(uid: Int, persistentDeviceId: String): SparseIntArray { override fun getNonDefaultUidModes(uid: Int, persistentDeviceId: String): SparseIntArray { return opNameMapToOpSparseArray(getUidModes(uid)) val appId = UserHandle.getAppId(uid) val userId = UserHandle.getUserId(uid) service.getState { val modes = with(appIdPolicy) { opNameMapToOpSparseArray(getAppOpModes(appId, userId)?.map) } if (useRuntimePermissionAppOpMapping()) { runtimePermissionNameToAppOp.forEachIndexed { _, permissionName, appOpCode -> val mode = getUidModeFromPermissionState(appId, userId, permissionName) if (mode != AppOpsManager.opToDefaultMode(appOpCode)) { modes[appOpCode] = mode } } } return modes } } } override fun getNonDefaultPackageModes(packageName: String, userId: Int): SparseIntArray { override fun getNonDefaultPackageModes(packageName: String, userId: Int): SparseIntArray { Loading @@ -83,7 +163,13 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS val appId = UserHandle.getAppId(uid) val appId = UserHandle.getAppId(uid) val userId = UserHandle.getUserId(uid) val userId = UserHandle.getUserId(uid) val opName = AppOpsManager.opToPublicName(op) val opName = AppOpsManager.opToPublicName(op) return service.getState { with(appIdPolicy) { getAppOpMode(appId, userId, opName) } } val permissionName = runtimeAppOpToPermissionNames[op] return if (!useRuntimePermissionAppOpMapping() || permissionName == null) { service.getState { with(appIdPolicy) { getAppOpMode(appId, userId, opName) } } } else { service.getState { getUidModeFromPermissionState(appId, userId, permissionName) } } } } private fun getUidModes(uid: Int): ArrayMap<String, Int>? { private fun getUidModes(uid: Int): ArrayMap<String, Int>? { Loading @@ -92,13 +178,63 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS return service.getState { with(appIdPolicy) { getAppOpModes(appId, userId) } }?.map return service.getState { with(appIdPolicy) { getAppOpModes(appId, userId) } }?.map } } override fun setUidMode(uid: Int, persistentDeviceId: String, op: Int, mode: Int): Boolean { private fun GetStateScope.getUidModeFromPermissionState( appId: Int, userId: Int, permissionName: String ): Int { val permissionFlags = with(permissionPolicy) { getPermissionFlags(appId, userId, permissionName) } val backgroundPermissionName = foregroundToBackgroundPermissionName[permissionName] val backgroundPermissionFlags = if (backgroundPermissionName != null) { with(permissionPolicy) { getPermissionFlags(appId, userId, backgroundPermissionName) } } else { PermissionFlags.RUNTIME_GRANTED } val result = evaluateModeFromPermissionFlags(permissionFlags, backgroundPermissionFlags) if (result != MODE_IGNORED) { return result } val fullerPermissionName = PermissionService.getFullerPermission(permissionName) ?: return result return getUidModeFromPermissionState(appId, userId, fullerPermissionName) } private fun evaluateModeFromPermissionFlags( foregroundFlags: Int, backgroundFlags: Int = PermissionFlags.RUNTIME_GRANTED ): Int = if (PermissionFlags.isAppOpGranted(foregroundFlags)) { if (PermissionFlags.isAppOpGranted(backgroundFlags)) { MODE_ALLOWED } else { MODE_FOREGROUND } } else { MODE_IGNORED } override fun setUidMode(uid: Int, persistentDeviceId: String, code: Int, mode: Int): Boolean { if (useRuntimePermissionAppOpMapping() && code in runtimeAppOpToPermissionNames) { Slog.w( LOG_TAG, "Cannot set UID mode for runtime permission app op, uid = $uid," + " code = ${AppOpsManager.opToName(code)}, mode = ${AppOpsManager.modeToName(mode)}", RuntimeException() ) return false } val appId = UserHandle.getAppId(uid) val appId = UserHandle.getAppId(uid) val userId = UserHandle.getUserId(uid) val userId = UserHandle.getUserId(uid) val opName = AppOpsManager.opToPublicName(op) val appOpName = AppOpsManager.opToPublicName(code) var wasChanged = false var wasChanged: Boolean service.mutateState { service.mutateState { wasChanged = with(appIdPolicy) { setAppOpMode(appId, userId, opName, mode) } wasChanged = with(appIdPolicy) { setAppOpMode(appId, userId, appOpName, mode) } } } return wasChanged return wasChanged } } Loading @@ -113,10 +249,22 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS private fun getPackageModes(packageName: String, userId: Int): ArrayMap<String, Int>? = private fun getPackageModes(packageName: String, userId: Int): ArrayMap<String, Int>? = service.getState { with(packagePolicy) { getAppOpModes(packageName, userId) } }?.map service.getState { with(packagePolicy) { getAppOpModes(packageName, userId) } }?.map override fun setPackageMode(packageName: String, op: Int, mode: Int, userId: Int) { override fun setPackageMode(packageName: String, appOpCode: Int, mode: Int, userId: Int) { val opName = AppOpsManager.opToPublicName(op) val appOpName = AppOpsManager.opToPublicName(appOpCode) if ( useRuntimePermissionAppOpMapping() && runtimeAppOpToPermissionNames.contains(appOpCode) ) { Slog.w( LOG_TAG, "(packageName=$packageName, userId=$userId)'s appop state" + " for runtime op $appOpName should not be set directly.", RuntimeException() ) return } service.mutateState { service.mutateState { with(packagePolicy) { setAppOpMode(packageName, userId, opName, mode) } with(packagePolicy) { setAppOpMode(packageName, userId, appOpName, mode) } } } } } Loading @@ -127,7 +275,7 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS } } override fun removePackage(packageName: String, userId: Int): Boolean { override fun removePackage(packageName: String, userId: Int): Boolean { var wasChanged = false var wasChanged: Boolean service.mutateState { service.mutateState { wasChanged = with(packagePolicy) { removeAppOpModes(packageName, userId) } wasChanged = with(packagePolicy) { removeAppOpModes(packageName, userId) } } } Loading Loading @@ -157,6 +305,13 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS this[AppOpsManager.strOpToOp(op)] = true this[AppOpsManager.strOpToOp(op)] = true } } } } if (useRuntimePermissionAppOpMapping()) { foregroundableOps.forEachIndexed { _, op, _ -> if (getUidMode(uid, persistentDeviceId, op) == AppOpsManager.MODE_FOREGROUND) { this[op] = true } } } } } } } Loading @@ -167,6 +322,13 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS this[AppOpsManager.strOpToOp(op)] = true this[AppOpsManager.strOpToOp(op)] = true } } } } if (useRuntimePermissionAppOpMapping()) { foregroundableOps.forEachIndexed { _, op, _ -> if (getPackageMode(packageName, op, userId) == AppOpsManager.MODE_FOREGROUND) { this[op] = true } } } } } } } Loading @@ -188,9 +350,10 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS } } } } inner class OnAppIdAppOpModeChangedListener : AppIdAppOpPolicy.OnAppOpModeChangedListener() { private inner class OnAppIdAppOpModeChangedListener : AppIdAppOpPolicy.OnAppOpModeChangedListener() { // (uid, appOpCode) -> newMode // (uid, appOpCode) -> newMode val pendingChanges = ArrayMap<Pair<Int, Int>, Int>() private val pendingChanges = LongSparseArray<Int>() override fun onAppOpModeChanged( override fun onAppOpModeChanged( appId: Int, appId: Int, Loading @@ -201,7 +364,7 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS ) { ) { val uid = UserHandle.getUid(userId, appId) val uid = UserHandle.getUid(userId, appId) val appOpCode = AppOpsManager.strOpToOp(appOpName) val appOpCode = AppOpsManager.strOpToOp(appOpName) val key = Pair(uid, appOpCode) val key = IntPair.of(uid, appOpCode) pendingChanges[key] = newMode pendingChanges[key] = newMode } } Loading @@ -210,8 +373,8 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS val listenersLocal = listeners val listenersLocal = listeners pendingChanges.forEachIndexed { _, key, mode -> pendingChanges.forEachIndexed { _, key, mode -> listenersLocal.forEachIndexed { _, listener -> listenersLocal.forEachIndexed { _, listener -> val uid = key.first val uid = IntPair.first(key) val appOpCode = key.second val appOpCode = IntPair.second(key) listener.onUidModeChanged(uid, appOpCode, mode) listener.onUidModeChanged(uid, appOpCode, mode) } } Loading @@ -224,7 +387,7 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS private inner class OnPackageAppOpModeChangedListener : private inner class OnPackageAppOpModeChangedListener : PackageAppOpPolicy.OnAppOpModeChangedListener() { PackageAppOpPolicy.OnAppOpModeChangedListener() { // (packageName, userId, appOpCode) -> newMode // (packageName, userId, appOpCode) -> newMode val pendingChanges = ArrayMap<Triple<String, Int, Int>, Int>() private val pendingChanges = ArrayMap<Triple<String, Int, Int>, Int>() override fun onAppOpModeChanged( override fun onAppOpModeChanged( packageName: String, packageName: String, Loading Loading @@ -254,4 +417,115 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS pendingChanges.clear() pendingChanges.clear() } } } } private inner class OnPermissionFlagsChangedListener : AppIdPermissionPolicy.OnPermissionFlagsChangedListener { // (uid, appOpCode) -> newMode private val pendingChanges = LongSparseArray<Int>() override fun onPermissionFlagsChanged( appId: Int, userId: Int, permissionName: String, oldFlags: Int, newFlags: Int ) { backgroundToForegroundPermissionNames[permissionName]?.let { foregroundPermissions -> // This is a background permission; there may be multiple foreground permissions // affected. foregroundPermissions.forEachIndexed { _, foregroundPermissionName -> runtimePermissionNameToAppOp[foregroundPermissionName]?.let { appOpCode -> val foregroundPermissionFlags = getPermissionFlags(appId, userId, foregroundPermissionName) addPendingChangedModeIfNeeded( appId, userId, appOpCode, foregroundPermissionFlags, oldFlags, foregroundPermissionFlags, newFlags ) } } } ?: foregroundToBackgroundPermissionName[permissionName]?.let { backgroundPermission -> runtimePermissionNameToAppOp[permissionName]?.let { appOpCode -> val backgroundPermissionFlags = getPermissionFlags(appId, userId, backgroundPermission) addPendingChangedModeIfNeeded( appId, userId, appOpCode, oldFlags, backgroundPermissionFlags, newFlags, backgroundPermissionFlags ) } } ?: runtimePermissionNameToAppOp[permissionName]?.let { appOpCode -> addPendingChangedModeIfNeeded( appId, userId, appOpCode, oldFlags, PermissionFlags.RUNTIME_GRANTED, newFlags, PermissionFlags.RUNTIME_GRANTED ) } } private fun getPermissionFlags(appId: Int, userId: Int, permissionName: String): Int = service.getState { with(permissionPolicy) { getPermissionFlags(appId, userId, permissionName) } } private fun addPendingChangedModeIfNeeded( appId: Int, userId: Int, appOpCode: Int, oldForegroundFlags: Int, oldBackgroundFlags: Int, newForegroundFlags: Int, newBackgroundFlags: Int, ) { val oldMode = evaluateModeFromPermissionFlags(oldForegroundFlags, oldBackgroundFlags) val newMode = evaluateModeFromPermissionFlags(newForegroundFlags, newBackgroundFlags) if (oldMode != newMode) { val uid = UserHandle.getUid(userId, appId) pendingChanges[IntPair.of(uid, appOpCode)] = newMode } } override fun onStateMutated() { val listenersLocal = listeners pendingChanges.forEachIndexed { _, key, mode -> listenersLocal.forEachIndexed { _, listener -> val uid = IntPair.first(key) val appOpCode = IntPair.second(key) listener.onUidModeChanged(uid, appOpCode, mode) } } pendingChanges.clear() } } companion object { private val LOG_TAG = AppOpService::class.java.simpleName private fun useRuntimePermissionAppOpMapping(): Boolean { val token = Binder.clearCallingIdentity() return try { Flags.runtimePermissionAppopsMapping() } finally { Binder.restoreCallingIdentity(token) } } } } }
services/permission/java/com/android/server/permission/access/appop/PackageAppOpPolicy.kt +9 −0 Original line number Original line Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.server.permission.access.appop package com.android.server.permission.access.appop import android.app.AppOpsManager import android.app.AppOpsManager import android.util.Slog import com.android.server.permission.access.GetStateScope import com.android.server.permission.access.GetStateScope import com.android.server.permission.access.MutableAccessState import com.android.server.permission.access.MutableAccessState import com.android.server.permission.access.MutateStateScope import com.android.server.permission.access.MutateStateScope Loading Loading @@ -87,6 +88,10 @@ class PackageAppOpPolicy : BaseAppOpPolicy(PackageAppOpPersistence()) { appOpName: String, appOpName: String, mode: Int mode: Int ): Boolean { ): Boolean { if (userId !in newState.userStates) { Slog.e(LOG_TAG, "Unable to set app op mode for missing user $userId") return false } val defaultMode = AppOpsManager.opToDefaultMode(appOpName) val defaultMode = AppOpsManager.opToDefaultMode(appOpName) val oldMode = val oldMode = newState.userStates[userId]!! newState.userStates[userId]!! Loading Loading @@ -155,4 +160,8 @@ class PackageAppOpPolicy : BaseAppOpPolicy(PackageAppOpPersistence()) { */ */ abstract fun onStateMutated() abstract fun onStateMutated() } } companion object { private val LOG_TAG = PackageAppOpPolicy::class.java.simpleName } } }
services/permission/java/com/android/server/permission/access/collection/LongSparseArrayExtensions.kt 0 → 100644 +105 −0 Original line number Original line Diff line number Diff line /* * 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.collection import android.util.LongSparseArray inline fun <T> LongSparseArray<T>.allIndexed(predicate: (Int, Long, T) -> Boolean): Boolean { forEachIndexed { index, key, value -> if (!predicate(index, key, value)) { return false } } return true } inline fun <T> LongSparseArray<T>.anyIndexed(predicate: (Int, Long, T) -> Boolean): Boolean { forEachIndexed { index, key, value -> if (predicate(index, key, value)) { return true } } return false } inline fun <T> LongSparseArray<T>.forEachIndexed(action: (Int, Long, T) -> Unit) { for (index in 0 until size) { action(index, keyAt(index), valueAt(index)) } } inline fun <T> LongSparseArray<T>.forEachReversedIndexed(action: (Int, Long, T) -> Unit) { for (index in lastIndex downTo 0) { action(index, keyAt(index), valueAt(index)) } } inline fun <T> LongSparseArray<T>.getOrPut(key: Long, defaultValue: () -> T): T { val index = indexOfKey(key) return if (index >= 0) { valueAt(index) } else { defaultValue().also { put(key, it) } } } inline val <T> LongSparseArray<T>.lastIndex: Int get() = size - 1 @Suppress("NOTHING_TO_INLINE") inline operator fun <T> LongSparseArray<T>.minusAssign(key: Long) { delete(key) } inline fun <T> LongSparseArray<T>.noneIndexed(predicate: (Int, Long, T) -> Boolean): Boolean { forEachIndexed { index, key, value -> if (predicate(index, key, value)) { return false } } return true } inline fun <T> LongSparseArray<T>.removeAllIndexed(predicate: (Int, Long, T) -> Boolean): Boolean { var isChanged = false forEachReversedIndexed { index, key, value -> if (predicate(index, key, value)) { removeAt(index) isChanged = true } } return isChanged } inline fun <T> LongSparseArray<T>.retainAllIndexed(predicate: (Int, Long, T) -> Boolean): Boolean { var isChanged = false forEachReversedIndexed { index, key, value -> if (!predicate(index, key, value)) { removeAt(index) isChanged = true } } return isChanged } inline val <T> LongSparseArray<T>.size: Int get() = size() @Suppress("NOTHING_TO_INLINE") inline operator fun <T> LongSparseArray<T>.set(key: Long, value: T) { put(key, value) }