Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4090e1fb authored by Evan Severson's avatar Evan Severson
Browse files

Reapply "Evaluate runtime permissions state for their appop"

This reverts commit bca87150.

Bug: 266164193
Test: Boot + appops migration
Test: atest \
          CtsAppOpsTestCases \
          CtsAppOps2TestCases \
          CtsPermissionTestCases \
          CtsPermissionMultiDeviceTestCases \
          CtsPermissionMultiUserTestCases \
          CtsPermissionPolicyTestCases \
          CtsPermissionUiTestCases \
          CtsRoleTestCases \
          CtsSafetyCenterTestCases
Change-Id: Ibb5e68ffbc64d360e33d49950357fdf0aa9ff13f
parent ef23ee9e
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -138,3 +138,11 @@ flag {
    bug: "325356776"
}

flag {
    name: "runtime_permission_appops_mapping_enabled"
    is_fixed_read_only: true
    namespace: "permissions"
    description: "Use runtime permission state to determine appop state"
    bug: "266164193"
}
+319 −19
Original line number Diff line number Diff line
@@ -20,27 +20,63 @@ import android.app.AppOpsManager
import android.companion.virtual.VirtualDeviceManager
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.DevicePermissionUri
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.DevicePermissionPolicy
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 devicePermissionPolicy =
        service.getSchemePolicy(UidUri.SCHEME, DevicePermissionUri.SCHEME) as DevicePermissionPolicy

    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>()
@@ -69,11 +105,60 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS
    }

    override fun systemReady() {
        // Not implemented because upgrades are handled automatically.
        if (Flags.runtimePermissionAppopsMappingEnabled()) {
            createPermissionAppOpMapping()
            val permissionListener = OnPermissionFlagsChangedListener()
            permissionPolicy.addOnPermissionFlagsChangedListener(permissionListener)
            devicePermissionPolicy.addOnPermissionFlagsChangedListener(permissionListener)
        }
    }

    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 (Flags.runtimePermissionAppopsMappingEnabled()) {
                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 {
@@ -84,7 +169,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 (!Flags.runtimePermissionAppopsMappingEnabled() || 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>? {
@@ -93,13 +184,66 @@ 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 (
            Flags.runtimePermissionAppopsMappingEnabled() && 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
    }
@@ -114,10 +258,23 @@ 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 (
            Flags.runtimePermissionAppopsMappingEnabled() &&
                appOpCode in runtimeAppOpToPermissionNames
        ) {
            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) }
        }
    }

@@ -128,7 +285,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) }
        }
@@ -158,6 +315,13 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS
                    this[AppOpsManager.strOpToOp(op)] = true
                }
            }
            if (Flags.runtimePermissionAppopsMappingEnabled()) {
                foregroundableOps.forEachIndexed { _, op, _ ->
                    if (getUidMode(uid, persistentDeviceId, op) == AppOpsManager.MODE_FOREGROUND) {
                        this[op] = true
                    }
                }
            }
        }
    }

@@ -168,6 +332,13 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS
                    this[AppOpsManager.strOpToOp(op)] = true
                }
            }
            if (Flags.runtimePermissionAppopsMappingEnabled()) {
                foregroundableOps.forEachIndexed { _, op, _ ->
                    if (getPackageMode(packageName, op, userId) == AppOpsManager.MODE_FOREGROUND) {
                        this[op] = true
                    }
                }
            }
        }
    }

@@ -189,9 +360,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,
@@ -202,7 +374,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
        }
@@ -211,13 +383,15 @@ 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,
                    listener.onUidModeChanged(
                        uid,
                        appOpCode,
                        mode,
                        VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT)
                        VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT
                    )
                }
            }

@@ -228,7 +402,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,
@@ -258,4 +432,130 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS
            pendingChanges.clear()
        }
    }

    private inner class OnPermissionFlagsChangedListener :
        AppIdPermissionPolicy.OnPermissionFlagsChangedListener,
        DevicePermissionPolicy.OnDevicePermissionFlagsChangedListener {
        // (uid, deviceId, appOpCode) -> newMode
        private val pendingChanges = ArrayMap<Triple<Int, String, Int>, Int>()

        override fun onPermissionFlagsChanged(
            appId: Int,
            userId: Int,
            permissionName: String,
            oldFlags: Int,
            newFlags: Int
        ) {
            onDevicePermissionFlagsChanged(
                appId,
                userId,
                VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT,
                permissionName,
                oldFlags,
                newFlags
            )
        }

        override fun onDevicePermissionFlagsChanged(
            appId: Int,
            userId: Int,
            deviceId: String,
            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,
                            deviceId,
                            appOpCode,
                            foregroundPermissionFlags,
                            oldFlags,
                            foregroundPermissionFlags,
                            newFlags
                        )
                    }
                }
            }
                ?: foregroundToBackgroundPermissionName[permissionName]?.let { backgroundPermission
                    ->
                    runtimePermissionNameToAppOp[permissionName]?.let { appOpCode ->
                        val backgroundPermissionFlags =
                            getPermissionFlags(appId, userId, backgroundPermission)
                        addPendingChangedModeIfNeeded(
                            appId,
                            userId,
                            deviceId,
                            appOpCode,
                            oldFlags,
                            backgroundPermissionFlags,
                            newFlags,
                            backgroundPermissionFlags
                        )
                    }
                }
                ?: runtimePermissionNameToAppOp[permissionName]?.let { appOpCode ->
                    addPendingChangedModeIfNeeded(
                        appId,
                        userId,
                        deviceId,
                        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,
            deviceId: String,
            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[Triple(uid, deviceId, appOpCode)] = newMode
            }
        }

        override fun onStateMutated() {
            val listenersLocal = listeners
            pendingChanges.forEachIndexed { _, key, mode ->
                listenersLocal.forEachIndexed { _, listener ->
                    val uid = key.first
                    val deviceId = key.second
                    val appOpCode = key.third

                    listener.onUidModeChanged(uid, appOpCode, mode, deviceId)
                }
            }

            pendingChanges.clear()
        }
    }

    companion object {
        private val LOG_TAG = AppOpService::class.java.simpleName
    }
}
+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)
}
+120 −0

File added.

Preview size limit exceeded, changes collapsed.

+3 −0
Original line number Diff line number Diff line
@@ -2905,5 +2905,8 @@ class PermissionService(private val service: AccessCheckingService) :
            } else {
                emptySet<String>()
            }

        fun getFullerPermission(permissionName: String): String? =
            FULLER_PERMISSIONS[permissionName]
    }
}