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

Commit 5b6153e8 authored by Evan Severson's avatar Evan Severson
Browse files

Evaluate runtime permissions state for their appop

Bug: 266164193
Test: Boot + appops migration
        + atest CtsAppOpsTestCases \
		CtsAppOps2TestCases \
		CtsPermissionTestCases \
		CtsPermissionMultiDeviceTestCases \
		CtsPermissionMultiUserTestCases \
		CtsPermissionPolicyTestCases \
		CtsPermissionUiTestCases \
		CtsRoleTestCases \
		CtsSafetyCenterTestCases \
		--retry-any-failure 5

Change-Id: Ic3d91b7b2ce6c9da3cca498e0b3f94d9e7a85b8a
parent cd0852f1
Loading
Loading
Loading
Loading
+7 −0
Original line number Original line Diff line number Diff line
@@ -78,3 +78,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"
}
+291 −17
Original line number Original line Diff line number Diff line
@@ -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>()
@@ -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 {
@@ -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>? {
@@ -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
    }
    }
@@ -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) }
        }
        }
    }
    }


@@ -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) }
        }
        }
@@ -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
                    }
                }
            }
        }
        }
    }
    }


@@ -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
                    }
                }
            }
        }
        }
    }
    }


@@ -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,
@@ -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
        }
        }
@@ -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)
                }
                }
@@ -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,
@@ -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)
            }
        }
    }
}
}
+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)
}
+120 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2023 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.SparseIntArray

inline fun SparseIntArray.allIndexed(predicate: (Int, Int, Int) -> Boolean): Boolean {
    forEachIndexed { index, key, value ->
        if (!predicate(index, key, value)) {
            return false
        }
    }
    return true
}

inline fun SparseIntArray.anyIndexed(predicate: (Int, Int, Int) -> Boolean): Boolean {
    forEachIndexed { index, key, value ->
        if (predicate(index, key, value)) {
            return true
        }
    }
    return false
}

inline fun SparseIntArray.forEachIndexed(action: (Int, Int, Int) -> Unit) {
    for (index in 0 until size) {
        action(index, keyAt(index), valueAt(index))
    }
}

inline fun SparseIntArray.forEachReversedIndexed(action: (Int, Int, Int) -> Unit) {
    for (index in lastIndex downTo 0) {
        action(index, keyAt(index), valueAt(index))
    }
}

inline fun SparseIntArray.getOrPut(key: Int, defaultValue: () -> Int): Int {
    val index = indexOfKey(key)
    return if (index >= 0) {
        valueAt(index)
    } else {
        defaultValue().also { put(key, it) }
    }
}

inline val SparseIntArray.lastIndex: Int
    get() = size - 1

@Suppress("NOTHING_TO_INLINE")
inline operator fun SparseIntArray.minusAssign(key: Int) {
    delete(key)
}

inline fun SparseIntArray.noneIndexed(predicate: (Int, Int, Int) -> Boolean): Boolean {
    forEachIndexed { index, key, value ->
        if (predicate(index, key, value)) {
            return false
        }
    }
    return true
}

fun SparseIntArray.remove(key: Int) {
    delete(key)
}

fun SparseIntArray.remove(key: Int, defaultValue: Int): Int {
    val index = indexOfKey(key)
    return if (index >= 0) {
        val oldValue = valueAt(index)
        removeAt(index)
        oldValue
    } else {
        defaultValue
    }
}

inline fun SparseIntArray.removeAllIndexed(predicate: (Int, Int, Int) -> Boolean): Boolean {
    var isChanged = false
    forEachReversedIndexed { index, key, value ->
        if (predicate(index, key, value)) {
            removeAt(index)
            isChanged = true
        }
    }
    return isChanged
}

inline fun SparseIntArray.retainAllIndexed(predicate: (Int, Int, Int) -> Boolean): Boolean {
    var isChanged = false
    forEachReversedIndexed { index, key, value ->
        if (!predicate(index, key, value)) {
            removeAt(index)
            isChanged = true
        }
    }
    return isChanged
}

@Suppress("NOTHING_TO_INLINE")
inline operator fun SparseIntArray.set(key: Int, value: Int) {
    put(key, value)
}

inline val SparseIntArray.size: Int
    get() = size()
+3 −0
Original line number Original line Diff line number Diff line
@@ -2870,5 +2870,8 @@ class PermissionService(private val service: AccessCheckingService) :
            } else {
            } else {
                emptySet<String>()
                emptySet<String>()
            }
            }

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