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

Commit 2a4ee723 authored by Evan Severson's avatar Evan Severson Committed by Android (Google) Code Review
Browse files

Merge "Reapply "Evaluate runtime permissions state for their appop"" into main

parents 9c6c9980 4090e1fb
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
@@ -2826,5 +2826,8 @@ class PermissionService(private val service: AccessCheckingService) :
            } else {
                emptySet<String>()
            }

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