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

Commit e455c28f authored by Hai Zhang's avatar Hai Zhang
Browse files

Expose app op mode getter/setter/change listener directly on policy.

This allows the compat layer to call them with less ceremony of going
through URIs.

Also call UserState.requestWrite() upon changes.

It is unfortunate that we have to duplicate some code this way, but we
have to do it because Java/Kotlin generics doesn't allow primitive
ints and we want to avoid auto-boxing.

Also made mutateState()'s lambda crossinline so that the caller don't
accidentally stop the state mutation mid-way.

Bug: 182523293
Test: presubmit
Change-Id: I1f61c35352a9c614939976bf98ee2eeb9bb8d0e0
parent 8ec0b69d
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -140,7 +140,7 @@ class AccessCheckingService(context: Context) : SystemService(context) {
    internal inline fun <T> getState(action: GetStateScope.() -> T): T =
        GetStateScope(state).action()

    internal inline fun mutateState(action: MutateStateScope.() -> Unit) {
    internal inline fun mutateState(crossinline action: MutateStateScope.() -> Unit) {
        synchronized(stateLock) {
            val oldState = state
            val newState = oldState.copy()
+0 −37
Original line number Diff line number Diff line
@@ -241,10 +241,6 @@ class AccessPolicy private constructor(
}

abstract class SchemePolicy {
    @Volatile
    private var onDecisionChangedListeners = IndexedListSet<OnDecisionChangedListener>()
    private val onDecisionChangedListenersLock = Any()

    abstract val subjectScheme: String

    abstract val objectScheme: String
@@ -257,30 +253,6 @@ abstract class SchemePolicy {
        decision: Int
    )

    fun addOnDecisionChangedListener(listener: OnDecisionChangedListener) {
        synchronized(onDecisionChangedListenersLock) {
            onDecisionChangedListeners = onDecisionChangedListeners + listener
        }
    }

    fun removeOnDecisionChangedListener(listener: OnDecisionChangedListener) {
        synchronized(onDecisionChangedListenersLock) {
            onDecisionChangedListeners = onDecisionChangedListeners - listener
        }
    }

    protected fun notifyOnDecisionChangedListeners(
        subject: AccessUri,
        `object`: AccessUri,
        oldDecision: Int,
        newDecision: Int
    ) {
        val listeners = onDecisionChangedListeners
        listeners.forEachIndexed { _, it ->
            it.onDecisionChanged(subject, `object`, oldDecision, newDecision)
        }
    }

    open fun MutateStateScope.onUserAdded(userId: Int) {}

    open fun MutateStateScope.onUserRemoved(userId: Int) {}
@@ -313,13 +285,4 @@ abstract class SchemePolicy {
    open fun BinaryXmlPullParser.parseUserState(userId: Int, userState: UserState) {}

    open fun BinaryXmlSerializer.serializeUserState(userId: Int, userState: UserState) {}

    fun interface OnDecisionChangedListener {
        fun onDecisionChanged(
            subject: AccessUri,
            `object`: AccessUri,
            oldDecision: Int,
            newDecision: Int
        )
    }
}
+5 −38
Original line number Diff line number Diff line
@@ -16,50 +16,17 @@

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

import android.app.AppOpsManager
import com.android.modules.utils.BinaryXmlPullParser
import com.android.modules.utils.BinaryXmlSerializer
import com.android.server.permission.access.AccessUri
import com.android.server.permission.access.AppOpUri
import com.android.server.permission.access.GetStateScope
import com.android.server.permission.access.MutateStateScope
import com.android.server.permission.access.SchemePolicy
import com.android.server.permission.access.UserState
import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports

abstract class BaseAppOpPolicy(private val persistence: BaseAppOpPersistence) : SchemePolicy() {
    override fun GetStateScope.getDecision(subject: AccessUri, `object`: AccessUri): Int {
        `object` as AppOpUri
        return getModes(subject)
            .getWithDefault(`object`.appOpName, opToDefaultMode(`object`.appOpName))
    }

    override fun MutateStateScope.setDecision(
        subject: AccessUri,
        `object`: AccessUri,
        decision: Int
    ) {
        `object` as AppOpUri
        val modes = getOrCreateModes(subject)
        val oldMode = modes.putWithDefault(`object`.appOpName, decision,
            opToDefaultMode(`object`.appOpName))
        if (modes.isEmpty()) {
            removeModes(subject)
        }
        if (oldMode != decision) {
            notifyOnDecisionChangedListeners(subject, `object`, oldMode, decision)
        }
    }

    abstract fun GetStateScope.getModes(subject: AccessUri): IndexedMap<String, Int>?

    abstract fun MutateStateScope.getOrCreateModes(subject: AccessUri): IndexedMap<String, Int>

    abstract fun MutateStateScope.removeModes(subject: AccessUri)

    // TODO need to check that [AppOpsManager.getSystemAlertWindowDefault] works; likely no issue
    //  since running in system process.
    private fun opToDefaultMode(appOpName: String) = AppOpsManager.opToDefaultMode(appOpName)
abstract class BaseAppOpPolicy(
    private val persistence: BaseAppOpPersistence
) : SchemePolicy() {
    override val objectScheme: String
        get() = AppOpUri.SCHEME

    override fun BinaryXmlPullParser.parseUserState(userId: Int, userState: UserState) {
        with(persistence) { this@parseUserState.parseUserState(userId, userState) }
+75 −14
Original line number Diff line number Diff line
@@ -16,40 +16,101 @@

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

import android.app.AppOpsManager
import com.android.server.permission.access.AccessUri
import com.android.server.permission.access.AppOpUri
import com.android.server.permission.access.GetStateScope
import com.android.server.permission.access.MutateStateScope
import com.android.server.permission.access.PackageUri
import com.android.server.permission.access.UserState
import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports

class PackageAppOpPolicy : BaseAppOpPolicy(PackageAppOpPersistence()) {
    @Volatile
    private var onAppOpModeChangedListeners = IndexedListSet<OnAppOpModeChangedListener>()
    private val onAppOpModeChangedListenersLock = Any()

    override val subjectScheme: String
        get() = PackageUri.SCHEME

    override val objectScheme: String
        get() = AppOpUri.SCHEME

    override fun GetStateScope.getModes(subject: AccessUri): IndexedMap<String, Int>? {
        subject as PackageUri
        return state.userStates[subject.userId]?.packageAppOpModes?.get(subject.packageName)
    }

    override fun MutateStateScope.getOrCreateModes(subject: AccessUri): IndexedMap<String, Int> {
    override fun GetStateScope.getDecision(subject: AccessUri, `object`: AccessUri): Int {
        subject as PackageUri
        return newState.userStates.getOrPut(subject.userId) { UserState() }
            .packageAppOpModes.getOrPut(subject.packageName) { IndexedMap() }
        `object` as AppOpUri
        return getAppOpMode(subject.packageName, subject.userId, `object`.appOpName)
    }

    override fun MutateStateScope.removeModes(subject: AccessUri) {
    override fun MutateStateScope.setDecision(
        subject: AccessUri,
        `object`: AccessUri,
        decision: Int
    ) {
        subject as PackageUri
        newState.userStates[subject.userId]?.packageAppOpModes?.remove(subject.packageName)
        `object` as AppOpUri
        setAppOpMode(subject.packageName, subject.userId, `object`.appOpName, decision)
    }

    override fun MutateStateScope.onPackageRemoved(packageName: String, appId: Int) {
        newState.userStates.forEachIndexed { _, _, userState ->
            userState.packageAppOpModes -= packageName
            userState.requestWrite()
            // Skip notifying the change listeners since the package no longer exists.
        }
    }

    fun MutateStateScope.removeAppOpModes(packageName: String, userId: Int): Boolean =
        newState.userStates[userId].packageAppOpModes.remove(packageName) != null

    fun GetStateScope.getAppOpMode(packageName: String, userId: Int, appOpName: String): Int =
        state.userStates[userId].packageAppOpModes[packageName]
            .getWithDefault(appOpName, AppOpsManager.opToDefaultMode(appOpName))

    fun MutateStateScope.setAppOpMode(
        packageName: String,
        userId: Int,
        appOpName: String,
        mode: Int
    ): Boolean {
        val userState = newState.userStates[userId]
        val packageAppOpModes = userState.packageAppOpModes
        var appOpModes = packageAppOpModes[packageName]
        val defaultMode = AppOpsManager.opToDefaultMode(appOpName)
        val oldMode = appOpModes.getWithDefault(appOpName, defaultMode)
        if (oldMode == mode) {
            return false
        }
        if (appOpModes == null) {
            appOpModes = IndexedMap()
            packageAppOpModes[packageName] = appOpModes
        }
        appOpModes.putWithDefault(appOpName, mode, defaultMode)
        if (appOpModes.isEmpty()) {
            packageAppOpModes -= packageName
        }
        userState.requestWrite()
        onAppOpModeChangedListeners.forEachIndexed { _, it ->
            it.onAppOpModeChanged(packageName, userId, appOpName, oldMode, mode)
        }
        return true
    }

    fun addOnAppOpModeChangedListener(listener: OnAppOpModeChangedListener) {
        synchronized(onAppOpModeChangedListenersLock) {
            onAppOpModeChangedListeners = onAppOpModeChangedListeners + listener
        }
    }

    fun removeOnAppOpModeChangedListener(listener: OnAppOpModeChangedListener) {
        synchronized(onAppOpModeChangedListenersLock) {
            onAppOpModeChangedListeners = onAppOpModeChangedListeners - listener
        }
    }

    fun interface OnAppOpModeChangedListener {
        fun onAppOpModeChanged(
            packageName: String,
            userId: Int,
            appOpName: String,
            oldMode: Int,
            newMode: Int
        )
    }
}
+78 −14
Original line number Diff line number Diff line
@@ -16,40 +16,104 @@

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

import android.app.AppOpsManager
import com.android.server.permission.access.AccessUri
import com.android.server.permission.access.AppOpUri
import com.android.server.permission.access.GetStateScope
import com.android.server.permission.access.MutateStateScope
import com.android.server.permission.access.UidUri
import com.android.server.permission.access.UserState
import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports

class UidAppOpPolicy : BaseAppOpPolicy(UidAppOpPersistence()) {
    @Volatile
    private var onAppOpModeChangedListeners = IndexedListSet<OnAppOpModeChangedListener>()
    private val onAppOpModeChangedListenersLock = Any()

    override val subjectScheme: String
        get() = UidUri.SCHEME

    override val objectScheme: String
        get() = AppOpUri.SCHEME

    override fun GetStateScope.getModes(subject: AccessUri): IndexedMap<String, Int>? {
        subject as UidUri
        return state.userStates[subject.userId]?.uidAppOpModes?.get(subject.appId)
    }

    override fun MutateStateScope.getOrCreateModes(subject: AccessUri): IndexedMap<String, Int> {
    override fun GetStateScope.getDecision(subject: AccessUri, `object`: AccessUri): Int {
        subject as UidUri
        return newState.userStates.getOrPut(subject.userId) { UserState() }
            .uidAppOpModes.getOrPut(subject.appId) { IndexedMap() }
        `object` as AppOpUri
        return getAppOpMode(subject.appId, subject.userId, `object`.appOpName)
    }

    override fun MutateStateScope.removeModes(subject: AccessUri) {
    override fun MutateStateScope.setDecision(
        subject: AccessUri,
        `object`: AccessUri,
        decision: Int
    ) {
        subject as UidUri
        newState.userStates[subject.userId]?.uidAppOpModes?.remove(subject.appId)
        `object` as AppOpUri
        setAppOpMode(subject.appId, subject.userId, `object`.appOpName, decision)
    }

    override fun MutateStateScope.onAppIdRemoved(appId: Int) {
        newState.userStates.forEachIndexed { _, _, userState ->
            userState.uidAppOpModes -= appId
            userState.requestWrite()
            // Skip notifying the change listeners since the app ID no longer exists.
        }
    }

    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 GetStateScope.getAppOpMode(appId: Int, userId: Int, appOpName: String): Int =
        state.userStates[userId].uidAppOpModes[appId]
            .getWithDefault(appOpName, AppOpsManager.opToDefaultMode(appOpName))

    fun MutateStateScope.setAppOpMode(
        appId: Int,
        userId: Int,
        appOpName: String,
        mode: Int
    ): Boolean {
        val userState = newState.userStates[userId]
        val uidAppOpModes = userState.uidAppOpModes
        var appOpModes = uidAppOpModes[appId]
        val defaultMode = AppOpsManager.opToDefaultMode(appOpName)
        val oldMode = appOpModes.getWithDefault(appOpName, defaultMode)
        if (oldMode == mode) {
            return false
        }
        if (appOpModes == null) {
            appOpModes = IndexedMap()
            uidAppOpModes[appId] = appOpModes
        }
        appOpModes.putWithDefault(appOpName, mode, defaultMode)
        if (appOpModes.isEmpty()) {
            uidAppOpModes -= appId
        }
        userState.requestWrite()
        onAppOpModeChangedListeners.forEachIndexed { _, it ->
            it.onAppOpModeChanged(appId, userId, appOpName, oldMode, mode)
        }
        return true
    }

    fun addOnAppOpModeChangedListener(listener: OnAppOpModeChangedListener) {
        synchronized(onAppOpModeChangedListenersLock) {
            onAppOpModeChangedListeners = onAppOpModeChangedListeners + listener
        }
    }

    fun removeOnAppOpModeChangedListener(listener: OnAppOpModeChangedListener) {
        synchronized(onAppOpModeChangedListenersLock) {
            onAppOpModeChangedListeners = onAppOpModeChangedListeners - listener
        }
    }

    fun interface OnAppOpModeChangedListener {
        fun onAppOpModeChanged(
            appId: Int,
            userId: Int,
            appOpName: String,
            oldMode: Int,
            newMode: Int
        )
    }
}
Loading