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

Commit 1dc4baaa authored by Jay Sullivan's avatar Jay Sullivan Committed by Android (Google) Code Review
Browse files

Merge "Implement app-op compatibility layer"

parents 72c4f6d3 8f61ec1d
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -218,13 +218,13 @@ public class AppOpsCheckingServiceImpl implements AppOpsCheckingServiceInterface
    }

    @Override
    public boolean arePackageModesDefault(String packageMode, @UserIdInt int userId) {
    public boolean arePackageModesDefault(@NonNull String packageName, @UserIdInt int userId) {
        synchronized (mLock) {
            ArrayMap<String, SparseIntArray> packageModes = mUserPackageModes.get(userId, null);
            if (packageModes == null) {
                return true;
            }
            SparseIntArray opModes = packageModes.get(packageMode);
            SparseIntArray opModes = packageModes.get(packageName);
            return (opModes == null || opModes.size() <= 0);
        }
    }
+2 −0
Original line number Diff line number Diff line
@@ -92,7 +92,9 @@ class SystemState private constructor(
class UserState private constructor(
    // A map of (appId to a map of (permissionName to permissionFlags))
    val uidPermissionFlags: IntMap<IndexedMap<String, Int>>,
    // appId -> opName -> opCode
    val uidAppOpModes: IntMap<IndexedMap<String, Int>>,
    // packageName -> opName -> opCode
    val packageAppOpModes: IndexedMap<String, IndexedMap<String, Int>>
) : WritableState() {
    constructor() : this(
+350 −30
Original line number Diff line number Diff line
@@ -16,88 +16,214 @@

package com.android.server.permission.access.appop

import android.util.ArraySet
import android.Manifest
import android.annotation.UserIdInt
import android.app.AppGlobals
import android.app.AppOpsManager
import android.content.pm.PackageManager
import android.os.Binder
import android.os.Handler
import android.os.RemoteException
import android.os.UserHandle
import android.util.SparseBooleanArray
import android.util.SparseIntArray
import com.android.internal.util.ArrayUtils
import com.android.internal.util.function.pooled.PooledLambda
import com.android.server.appop.AppOpsCheckingServiceInterface
import com.android.server.appop.OnOpModeChangedListener
import com.android.server.permission.access.AccessCheckingService
import com.android.server.permission.access.AppOpUri
import com.android.server.permission.access.PackageUri
import com.android.server.permission.access.UidUri
import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports
import com.android.server.permission.access.util.hasBits
import libcore.util.EmptyArray
import java.io.PrintWriter

class AppOpService(
    private val service: AccessCheckingService
) : AppOpsCheckingServiceInterface {
    private val packagePolicy = service.getSchemePolicy(PackageUri.SCHEME, AppOpUri.SCHEME)
        as PackageAppOpPolicy
    private val uidPolicy = service.getSchemePolicy(UidUri.SCHEME, AppOpUri.SCHEME)
        as UidAppOpPolicy

    private val context = service.context
    private lateinit var handler: Handler
    private lateinit var lock: Any
    private lateinit var switchedOps: IntMap<IntArray>

    fun initialize() {
        TODO("Not yet implemented")
        // TODO(b/252883039): Wrong handler. Inject main thread handler here.
        handler = Handler(context.mainLooper)
        // TODO(b/252883039): Wrong lock object. Inject AppOpsService here.
        lock = Any()

        switchedOps = IntMap()
        for (switchedCode in 0 until AppOpsManager._NUM_OP) {
            val switchCode = AppOpsManager.opToSwitch(switchedCode)
            switchedOps.put(switchCode,
                ArrayUtils.appendInt(switchedOps.get(switchCode), switchedCode))
        }
    }

    override fun getNonDefaultUidModes(uid: Int): SparseIntArray {
        TODO("Not yet implemented")
        return opNameMapToOpIntMap(getUidModes(uid))
    }

    override fun getUidMode(uid: Int, op: Int): Int {
        TODO("Not yet implemented")
        val appId = UserHandle.getAppId(uid)
        val userId = UserHandle.getUserId(uid)
        val opName = AppOpsManager.opToPublicName(op)
        return service.getState {
            with(uidPolicy) { getAppOpMode(appId, userId, opName) }
        }
    }

    private fun getUidModes(uid: Int): IndexedMap<String, Int>? {
        val appId = UserHandle.getAppId(uid)
        val userId = UserHandle.getUserId(uid)
        return service.getState {
            with(uidPolicy) { getAppOpModes(appId, userId) }
        }
    }

    override fun setUidMode(uid: Int, op: Int, mode: Int): Boolean {
        TODO("Not yet implemented")
        val appId = UserHandle.getAppId(uid)
        val userId = UserHandle.getUserId(uid)
        val opName = AppOpsManager.opToPublicName(op)
        var wasChanged = false
        service.mutateState {
            wasChanged = with(uidPolicy) { setAppOpMode(appId, userId, opName, mode) }
        }
        return wasChanged
    }

    override fun getPackageMode(packageName: String, op: Int, userId: Int): Int {
        TODO("Not yet implemented")
        val opName = AppOpsManager.opToPublicName(op)
        return service.getState {
            with(packagePolicy) { getAppOpMode(packageName, userId, opName) }
        }
    }

    private fun getPackageModes(
        packageName: String,
        userId: Int
    ): IndexedMap<String, Int>? =
        service.getState { with(packagePolicy) { getAppOpModes(packageName, userId) } }

    override fun setPackageMode(packageName: String, op: Int, mode: Int, userId: Int) {
        TODO("Not yet implemented")
        val opName = AppOpsManager.opToPublicName(op)
        service.mutateState {
            with(packagePolicy) { setAppOpMode(packageName, userId, opName, mode) }
        }
    }

    override fun removeUid(uid: Int) {
        val appId = UserHandle.getAppId(uid)
        val userId = UserHandle.getUserId(uid)
        service.mutateState {
            with(uidPolicy) { removeAppOpModes(appId, userId) }
        }
    }

    override fun removePackage(packageName: String, userId: Int): Boolean {
        TODO("Not yet implemented")
        var wasChanged = false
        service.mutateState {
            wasChanged = with (packagePolicy) { removeAppOpModes(packageName, userId) }
        }
        return wasChanged
    }

    override fun removeUid(uid: Int) {
        TODO("Not yet implemented")
    private fun opNameMapToOpIntMap(modes: IndexedMap<String, Int>?): SparseIntArray =
        if (modes == null) {
            SparseIntArray()
        } else {
            val opIntMap = SparseIntArray(modes.size)
            modes.forEachIndexed { _, opName, opMode ->
                opIntMap.put(AppOpsManager.strOpToOp(opName), opMode)
            }
            opIntMap
        }

    override fun areUidModesDefault(uid: Int): Boolean {
        TODO("Not yet implemented")
        val modes = getUidModes(uid)
        return modes == null || modes.isEmpty()
    }

    override fun arePackageModesDefault(packageName: String, userId: Int): Boolean {
        TODO("Not yet implemented")
        val modes = service.getState { getPackageModes(packageName, userId) }
        return modes == null || modes.isEmpty()
    }

    override fun clearAllModes() {
        TODO("Not yet implemented")
        // We don't need to implement this because it's only called in AppOpsService#readState
        // and we have our own persistence.
    }

    // code -> listeners
    private val opModeWatchers = IntMap<IndexedSet<OnOpModeChangedListener>>()

    // packageName -> listeners
    private val packageModeWatchers = IndexedMap<String, IndexedSet<OnOpModeChangedListener>>()

    override fun startWatchingOpModeChanged(changedListener: OnOpModeChangedListener, op: Int) {
        TODO("Not yet implemented")
        synchronized(lock) {
            opModeWatchers.getOrPut(op) { IndexedSet() } += changedListener
        }
    }

    override fun startWatchingPackageModeChanged(
        changedListener: OnOpModeChangedListener,
        packageName: String
    ) {
        TODO("Not yet implemented")
        synchronized(lock) {
            packageModeWatchers.getOrPut(packageName) { IndexedSet() } += changedListener
        }
    }

    override fun removeListener(changedListener: OnOpModeChangedListener) {
        TODO("Not yet implemented")
        synchronized(lock) {
            opModeWatchers.removeAllIndexed { _, _, listeners ->
                listeners -= changedListener
                listeners.isEmpty()
            }
            packageModeWatchers.removeAllIndexed { _, _, listeners ->
                listeners -= changedListener
                listeners.isEmpty()
            }
        }
    }

    override fun getOpModeChangedListeners(op: Int): ArraySet<OnOpModeChangedListener> {
        TODO("Not yet implemented")
    override fun getOpModeChangedListeners(op: Int): IndexedSet<OnOpModeChangedListener> {
        synchronized(lock) {
            val listeners = opModeWatchers[op]
            return if (listeners == null) {
                IndexedSet()
            } else {
                IndexedSet(listeners)
            }
        }
    }

    override fun getPackageModeChangedListeners(
        packageName: String
    ): ArraySet<OnOpModeChangedListener> {
        TODO("Not yet implemented")
    ): IndexedSet<OnOpModeChangedListener> {
        synchronized(lock) {
            val listeners = packageModeWatchers[packageName]
            return if (listeners == null) {
                IndexedSet()
            } else {
                IndexedSet(listeners)
            }
        }
    }

    override fun notifyWatchersOfChange(op: Int, uid: Int) {
        TODO("Not yet implemented")
        val listeners = getOpModeChangedListeners(op)
        listeners.forEachIndexed { _, listener ->
            notifyOpChanged(listener, op, uid, null)
        }
    }

    override fun notifyOpChanged(
@@ -106,31 +232,159 @@ class AppOpService(
        uid: Int,
        packageName: String?
    ) {
        TODO("Not yet implemented")
        if (uid != UID_ANY &&
            changedListener.watchingUid >= 0 &&
            changedListener.watchingUid != uid
        ) {
            return
        }

        // See CALL_BACK_ON_CHANGED_LISTENER_WITH_SWITCHED_OP_CHANGE
        val switchedCodes = when (changedListener.watchedOpCode) {
            ALL_OPS -> switchedOps.get(op)
            AppOpsManager.OP_NONE -> intArrayOf(op)
            else -> intArrayOf(changedListener.watchedOpCode)
        }

        for (switchedCode in switchedCodes) {
            // There are features watching for mode changes such as window manager
            // and location manager which are in our process. The callbacks in these
            // features may require permissions our remote caller does not have.
            val identity = Binder.clearCallingIdentity()
            try {
                if (!shouldIgnoreCallback(switchedCode, changedListener)) {
                    changedListener.onOpModeChanged(switchedCode, uid, packageName)
                }
            } catch (e: RemoteException) {
                /* ignore */
            } finally {
                Binder.restoreCallingIdentity(identity)
            }
        }
    }

    private fun shouldIgnoreCallback(op: Int, listener: OnOpModeChangedListener): Boolean {
        // If it's a restricted read op, ignore it if watcher doesn't have manage ops permission,
        // as watcher should not use this to signal if the value is changed.
        return AppOpsManager.opRestrictsRead(op) && context.checkPermission(
            Manifest.permission.MANAGE_APPOPS,
            listener.callingPid,
            listener.callingUid
        ) != PackageManager.PERMISSION_GRANTED
    }

    /**
     * Construct a map from each listener (listening to the given op, uid) to all of its associated
     * packageNames (by reverse-indexing opModeWatchers and packageModeWatchers), then invoke
     * notifyOpChanged for each listener.
     */
    override fun notifyOpChangedForAllPkgsInUid(
        op: Int,
        uid: Int,
        onlyForeground: Boolean,
        callbackToIgnore: OnOpModeChangedListener?
    ) {
        TODO("Not yet implemented")
        val uidPackageNames = getPackagesForUid(uid)
        val callbackSpecs = IndexedMap<OnOpModeChangedListener, IndexedSet<String>>()

        fun associateListenerWithPackageNames(
            listener: OnOpModeChangedListener,
            packageNames: Array<String>
        ) {
            val listenerIsForeground =
                listener.flags.hasBits(AppOpsManager.WATCH_FOREGROUND_CHANGES)
            if (onlyForeground && !listenerIsForeground) {
                return
            }
            val changedPackages = callbackSpecs.getOrPut(listener) { IndexedSet() }
            changedPackages.addAll(packageNames)
        }

        synchronized(lock) {
            // Collect all listeners from opModeWatchers and pckageModeWatchers
            val listeners = opModeWatchers[op]
            listeners?.forEachIndexed { _, listener ->
                associateListenerWithPackageNames(listener, uidPackageNames)
            }
            uidPackageNames.forEachIndexed { _, uidPackageName ->
                val packageListeners = packageModeWatchers[uidPackageName]
                packageListeners?.forEachIndexed { _, listener ->
                    associateListenerWithPackageNames(listener, arrayOf(uidPackageName))
                }
            }
            // Remove ignored listeners
            if (callbackToIgnore != null) {
                callbackSpecs.remove(callbackToIgnore)
            }
        }

        // For each (listener, packageName) pair, invoke notifyOpChanged
        callbackSpecs.forEachIndexed { _, listener, reportedPackageNames ->
            reportedPackageNames.forEachIndexed { _, reportedPackageName ->
                handler.sendMessage(
                    PooledLambda.obtainMessage(
                        AppOpService::notifyOpChanged, this, listener,
                        op, uid, reportedPackageName
                    )
                )
            }
        }
    }

    private fun getPackagesForUid(uid: Int): Array<String> {
        // Very early during boot the package manager is not yet or not yet fully started. At this
        // time there are no packages yet.
        return try {
            AppGlobals.getPackageManager()?.getPackagesForUid(uid) ?: EmptyArray.STRING
        } catch (e: RemoteException) {
            EmptyArray.STRING
        }
    }

    override fun evalForegroundUidOps(
        uid: Int,
        foregroundOps: SparseBooleanArray?
    ): SparseBooleanArray {
        TODO("Not yet implemented")
    ): SparseBooleanArray? {
        synchronized(lock) {
            val uidModes = getUidModes(uid)
            return evalForegroundOps(uidModes, foregroundOps)
        }
    }

    override fun evalForegroundPackageOps(
        packageName: String,
        foregroundOps: SparseBooleanArray?,
        userId: Int
    ): SparseBooleanArray {
        TODO("Not yet implemented")
        @UserIdInt userId: Int
    ): SparseBooleanArray? {
        synchronized(lock) {
            val ops = service.getState { getPackageModes(packageName, userId) }
            return evalForegroundOps(ops, foregroundOps)
        }
    }

    private fun evalForegroundOps(
        ops: IndexedMap<String, Int>?,
        foregroundOps: SparseBooleanArray?
    ): SparseBooleanArray? {
        var foregroundOps = foregroundOps
        ops?.forEachIndexed { _, opName, opMode ->
            if (opMode == AppOpsManager.MODE_FOREGROUND) {
                if (foregroundOps == null) {
                    foregroundOps = SparseBooleanArray()
                }
                evalForegroundWatchers(opName, foregroundOps!!)
            }
        }
        return foregroundOps
    }

    private fun evalForegroundWatchers(opName: String, foregroundOps: SparseBooleanArray) {
        val opCode = AppOpsManager.strOpToOp(opName)
        val listeners = opModeWatchers[opCode]
        val hasForegroundListeners = foregroundOps[opCode] || listeners?.anyIndexed { _, listener ->
            listener.flags.hasBits(AppOpsManager.WATCH_FOREGROUND_CHANGES)
        } ?: false
        foregroundOps.put(opCode, hasForegroundListeners)
    }

    override fun dumpListeners(
@@ -139,10 +393,76 @@ class AppOpService(
        dumpPackage: String?,
        printWriter: PrintWriter
    ): Boolean {
        TODO("Not yet implemented")
        var needSep = false
        if (opModeWatchers.size() > 0) {
            var printedHeader = false
            opModeWatchers.forEachIndexed { _, op, modeChangedListenerSet ->
                if (dumpOp >= 0 && dumpOp != op) {
                    return@forEachIndexed // continue
                }
                val opName = AppOpsManager.opToName(op)
                var printedOpHeader = false
                modeChangedListenerSet.forEachIndexed listenerLoop@ { listenerIndex, listener ->
                    with(printWriter) {
                        if (dumpPackage != null &&
                            dumpUid != UserHandle.getAppId(listener.watchingUid)) {
                            return@listenerLoop // continue
                        }
                        needSep = true
                        if (!printedHeader) {
                            println("  Op mode watchers:")
                            printedHeader = true
                        }
                        if (!printedOpHeader) {
                            print("    Op ")
                            print(opName)
                            println(":")
                            printedOpHeader = true
                        }
                        print("      #")
                        print(listenerIndex)
                        print(opName)
                        print(": ")
                        println(listener.toString())
                    }
                }
            }
        }

        if (packageModeWatchers.size > 0 && dumpOp < 0) {
            var printedHeader = false
            packageModeWatchers.forEachIndexed { _, packageName, listeners ->
                with(printWriter) {
                    if (dumpPackage != null && dumpPackage != packageName) {
                        return@forEachIndexed // continue
                    }
                    needSep = true
                    if (!printedHeader) {
                        println("  Package mode watchers:")
                        printedHeader = true
                    }
                    print("    Pkg ")
                    print(packageName)
                    println(":")
                    listeners.forEachIndexed { listenerIndex, listener ->
                        print("      #")
                        print(listenerIndex)
                        print(": ")
                        println(listener.toString())
                    }
                }
            }
        }
        return needSep
    }

    companion object {
        private val LOG_TAG = AppOpService::class.java.simpleName

        // Constant meaning that any UID should be matched when dispatching callbacks
        private const val UID_ANY = -2

        // If watchedOpCode==ALL_OPS, notify for ops affected by the switch-op
        private const val ALL_OPS = -2
    }
}
+11 −2
Original line number Diff line number Diff line
@@ -56,8 +56,17 @@ class PackageAppOpPolicy : BaseAppOpPolicy(PackageAppOpPersistence()) {
        }
    }

    fun MutateStateScope.removeAppOpModes(packageName: String, userId: Int): Boolean =
        newState.userStates[userId].packageAppOpModes.remove(packageName) != null
    fun GetStateScope.getAppOpModes(packageName: String, userId: Int): IndexedMap<String, Int>? =
        state.userStates[userId].packageAppOpModes[packageName]

    fun MutateStateScope.removeAppOpModes(packageName: String, userId: Int): Boolean {
        val userState = newState.userStates[userId]
        val isChanged = userState.packageAppOpModes.remove(packageName) != null
        if (isChanged) {
            userState.requestWrite()
        }
        return isChanged
    }

    fun GetStateScope.getAppOpMode(packageName: String, userId: Int, appOpName: String): Int =
        state.userStates[userId].packageAppOpModes[packageName]
+8 −2
Original line number Diff line number Diff line
@@ -59,8 +59,14 @@ class UidAppOpPolicy : BaseAppOpPolicy(UidAppOpPersistence()) {
    fun GetStateScope.getAppOpModes(appId: Int, userId: Int): IndexedMap<String, Int>? =
        state.userStates[userId].uidAppOpModes[appId]

    fun MutateStateScope.removeAppOpModes(appId: Int, userId: Int): Boolean =
        newState.userStates[userId].uidAppOpModes.removeReturnOld(appId) != null
    fun MutateStateScope.removeAppOpModes(appId: Int, userId: Int): Boolean {
        val userState = newState.userStates[userId]
        val isChanged = userState.uidAppOpModes.removeReturnOld(appId) != null
        if (isChanged) {
            userState.requestWrite()
        }
        return isChanged
    }

    fun GetStateScope.getAppOpMode(appId: Int, userId: Int, appOpName: String): Int =
        state.userStates[userId].uidAppOpModes[appId]