Loading core/java/android/permission/flags.aconfig +7 −0 Original line number 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" 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 Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.server.permission.access.appop import android.app.AppOpsManager import android.util.Slog import com.android.server.permission.access.GetStateScope import com.android.server.permission.access.MutableAccessState import com.android.server.permission.access.MutateStateScope Loading Loading @@ -84,6 +85,10 @@ class AppIdAppOpPolicy : BaseAppOpPolicy(AppIdAppOpPersistence()) { appOpName: String, mode: Int ): 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 oldMode = newState.userStates[userId]!! Loading Loading @@ -152,4 +157,8 @@ class AppIdAppOpPolicy : BaseAppOpPolicy(AppIdAppOpPersistence()) { */ 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 Diff line number Diff line Loading @@ -17,29 +17,62 @@ package com.android.server.permission.access.appop import android.app.AppOpsManager import android.os.Binder import android.os.Handler import android.os.UserHandle import android.permission.flags.Flags import android.util.ArrayMap import android.util.ArraySet import android.util.LongSparseArray import android.util.Slog import android.util.SparseArray import android.util.SparseBooleanArray import android.util.SparseIntArray import com.android.internal.annotations.VisibleForTesting import com.android.internal.util.IntPair import com.android.server.appop.AppOpsCheckingServiceInterface import com.android.server.appop.AppOpsCheckingServiceInterface.AppOpsModeChangedListener import com.android.server.permission.access.AccessCheckingService 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.PermissionUri 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.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 { private val packagePolicy = service.getSchemePolicy(PackageUri.SCHEME, AppOpUri.SCHEME) as PackageAppOpPolicy private val appIdPolicy = service.getSchemePolicy(UidUri.SCHEME, AppOpUri.SCHEME) as AppIdAppOpPolicy private val permissionPolicy = service.getSchemePolicy(UidUri.SCHEME, PermissionUri.SCHEME) as AppIdPermissionPolicy 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 @Volatile private var listeners = ArraySet<AppOpsModeChangedListener>() Loading Loading @@ -68,11 +101,58 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS } 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 { 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 { Loading @@ -83,7 +163,13 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS val appId = UserHandle.getAppId(uid) val userId = UserHandle.getUserId(uid) 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>? { Loading @@ -92,13 +178,63 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS 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 userId = UserHandle.getUserId(uid) val opName = AppOpsManager.opToPublicName(op) var wasChanged = false val appOpName = AppOpsManager.opToPublicName(code) var wasChanged: Boolean service.mutateState { wasChanged = with(appIdPolicy) { setAppOpMode(appId, userId, opName, mode) } wasChanged = with(appIdPolicy) { setAppOpMode(appId, userId, appOpName, mode) } } return wasChanged } Loading @@ -113,10 +249,22 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS private fun getPackageModes(packageName: String, userId: Int): ArrayMap<String, Int>? = service.getState { with(packagePolicy) { getAppOpModes(packageName, userId) } }?.map override fun setPackageMode(packageName: String, op: Int, mode: Int, userId: Int) { val opName = AppOpsManager.opToPublicName(op) override fun setPackageMode(packageName: String, appOpCode: Int, mode: Int, userId: Int) { 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 { 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 { var wasChanged = false var wasChanged: Boolean service.mutateState { wasChanged = with(packagePolicy) { removeAppOpModes(packageName, userId) } } Loading Loading @@ -157,6 +305,13 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS 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 } } 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 val pendingChanges = ArrayMap<Pair<Int, Int>, Int>() private val pendingChanges = LongSparseArray<Int>() override fun onAppOpModeChanged( appId: Int, Loading @@ -201,7 +364,7 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS ) { val uid = UserHandle.getUid(userId, appId) val appOpCode = AppOpsManager.strOpToOp(appOpName) val key = Pair(uid, appOpCode) val key = IntPair.of(uid, appOpCode) pendingChanges[key] = newMode } Loading @@ -210,8 +373,8 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS val listenersLocal = listeners pendingChanges.forEachIndexed { _, key, mode -> listenersLocal.forEachIndexed { _, listener -> val uid = key.first val appOpCode = key.second val uid = IntPair.first(key) val appOpCode = IntPair.second(key) listener.onUidModeChanged(uid, appOpCode, mode) } Loading @@ -224,7 +387,7 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS private inner class OnPackageAppOpModeChangedListener : PackageAppOpPolicy.OnAppOpModeChangedListener() { // (packageName, userId, appOpCode) -> newMode val pendingChanges = ArrayMap<Triple<String, Int, Int>, Int>() private val pendingChanges = ArrayMap<Triple<String, Int, Int>, Int>() override fun onAppOpModeChanged( packageName: String, Loading Loading @@ -254,4 +417,115 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS 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 Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.server.permission.access.appop import android.app.AppOpsManager import android.util.Slog import com.android.server.permission.access.GetStateScope import com.android.server.permission.access.MutableAccessState import com.android.server.permission.access.MutateStateScope Loading Loading @@ -87,6 +88,10 @@ class PackageAppOpPolicy : BaseAppOpPolicy(PackageAppOpPersistence()) { appOpName: String, mode: Int ): 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 oldMode = newState.userStates[userId]!! Loading Loading @@ -155,4 +160,8 @@ class PackageAppOpPolicy : BaseAppOpPolicy(PackageAppOpPersistence()) { */ 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 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 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" 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 Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.server.permission.access.appop import android.app.AppOpsManager import android.util.Slog import com.android.server.permission.access.GetStateScope import com.android.server.permission.access.MutableAccessState import com.android.server.permission.access.MutateStateScope Loading Loading @@ -84,6 +85,10 @@ class AppIdAppOpPolicy : BaseAppOpPolicy(AppIdAppOpPersistence()) { appOpName: String, mode: Int ): 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 oldMode = newState.userStates[userId]!! Loading Loading @@ -152,4 +157,8 @@ class AppIdAppOpPolicy : BaseAppOpPolicy(AppIdAppOpPersistence()) { */ 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 Diff line number Diff line Loading @@ -17,29 +17,62 @@ package com.android.server.permission.access.appop import android.app.AppOpsManager import android.os.Binder import android.os.Handler import android.os.UserHandle import android.permission.flags.Flags import android.util.ArrayMap import android.util.ArraySet import android.util.LongSparseArray import android.util.Slog import android.util.SparseArray import android.util.SparseBooleanArray import android.util.SparseIntArray import com.android.internal.annotations.VisibleForTesting import com.android.internal.util.IntPair import com.android.server.appop.AppOpsCheckingServiceInterface import com.android.server.appop.AppOpsCheckingServiceInterface.AppOpsModeChangedListener import com.android.server.permission.access.AccessCheckingService 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.PermissionUri 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.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 { private val packagePolicy = service.getSchemePolicy(PackageUri.SCHEME, AppOpUri.SCHEME) as PackageAppOpPolicy private val appIdPolicy = service.getSchemePolicy(UidUri.SCHEME, AppOpUri.SCHEME) as AppIdAppOpPolicy private val permissionPolicy = service.getSchemePolicy(UidUri.SCHEME, PermissionUri.SCHEME) as AppIdPermissionPolicy 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 @Volatile private var listeners = ArraySet<AppOpsModeChangedListener>() Loading Loading @@ -68,11 +101,58 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS } 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 { 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 { Loading @@ -83,7 +163,13 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS val appId = UserHandle.getAppId(uid) val userId = UserHandle.getUserId(uid) 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>? { Loading @@ -92,13 +178,63 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS 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 userId = UserHandle.getUserId(uid) val opName = AppOpsManager.opToPublicName(op) var wasChanged = false val appOpName = AppOpsManager.opToPublicName(code) var wasChanged: Boolean service.mutateState { wasChanged = with(appIdPolicy) { setAppOpMode(appId, userId, opName, mode) } wasChanged = with(appIdPolicy) { setAppOpMode(appId, userId, appOpName, mode) } } return wasChanged } Loading @@ -113,10 +249,22 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS private fun getPackageModes(packageName: String, userId: Int): ArrayMap<String, Int>? = service.getState { with(packagePolicy) { getAppOpModes(packageName, userId) } }?.map override fun setPackageMode(packageName: String, op: Int, mode: Int, userId: Int) { val opName = AppOpsManager.opToPublicName(op) override fun setPackageMode(packageName: String, appOpCode: Int, mode: Int, userId: Int) { 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 { 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 { var wasChanged = false var wasChanged: Boolean service.mutateState { wasChanged = with(packagePolicy) { removeAppOpModes(packageName, userId) } } Loading Loading @@ -157,6 +305,13 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS 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 } } 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 val pendingChanges = ArrayMap<Pair<Int, Int>, Int>() private val pendingChanges = LongSparseArray<Int>() override fun onAppOpModeChanged( appId: Int, Loading @@ -201,7 +364,7 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS ) { val uid = UserHandle.getUid(userId, appId) val appOpCode = AppOpsManager.strOpToOp(appOpName) val key = Pair(uid, appOpCode) val key = IntPair.of(uid, appOpCode) pendingChanges[key] = newMode } Loading @@ -210,8 +373,8 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS val listenersLocal = listeners pendingChanges.forEachIndexed { _, key, mode -> listenersLocal.forEachIndexed { _, listener -> val uid = key.first val appOpCode = key.second val uid = IntPair.first(key) val appOpCode = IntPair.second(key) listener.onUidModeChanged(uid, appOpCode, mode) } Loading @@ -224,7 +387,7 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS private inner class OnPackageAppOpModeChangedListener : PackageAppOpPolicy.OnAppOpModeChangedListener() { // (packageName, userId, appOpCode) -> newMode val pendingChanges = ArrayMap<Triple<String, Int, Int>, Int>() private val pendingChanges = ArrayMap<Triple<String, Int, Int>, Int>() override fun onAppOpModeChanged( packageName: String, Loading Loading @@ -254,4 +417,115 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS 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 Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.server.permission.access.appop import android.app.AppOpsManager import android.util.Slog import com.android.server.permission.access.GetStateScope import com.android.server.permission.access.MutableAccessState import com.android.server.permission.access.MutateStateScope Loading Loading @@ -87,6 +88,10 @@ class PackageAppOpPolicy : BaseAppOpPolicy(PackageAppOpPersistence()) { appOpName: String, mode: Int ): 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 oldMode = newState.userStates[userId]!! Loading Loading @@ -155,4 +160,8 @@ class PackageAppOpPolicy : BaseAppOpPolicy(PackageAppOpPersistence()) { */ 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 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) }