diff options
| author | 2022-12-12 03:41:29 -0800 | |
|---|---|---|
| committer | 2022-12-13 21:33:57 -0800 | |
| commit | e455c28fd01e292155ff921ab576ba4702c20a69 (patch) | |
| tree | 5687784d5dfc40ee55faefd943086099c680cb90 | |
| parent | 8ec0b69dba4e8d939f603ba99527c91a10d2e034 (diff) | |
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
6 files changed, 171 insertions, 104 deletions
diff --git a/services/permission/java/com/android/server/permission/access/AccessCheckingService.kt b/services/permission/java/com/android/server/permission/access/AccessCheckingService.kt index 73c2cccc550d..cc4c95f8327d 100644 --- a/services/permission/java/com/android/server/permission/access/AccessCheckingService.kt +++ b/services/permission/java/com/android/server/permission/access/AccessCheckingService.kt @@ -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() diff --git a/services/permission/java/com/android/server/permission/access/AccessPolicy.kt b/services/permission/java/com/android/server/permission/access/AccessPolicy.kt index 0201dd0d0c79..c4182f487c2e 100644 --- a/services/permission/java/com/android/server/permission/access/AccessPolicy.kt +++ b/services/permission/java/com/android/server/permission/access/AccessPolicy.kt @@ -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 - ) - } } diff --git a/services/permission/java/com/android/server/permission/access/appop/BaseAppOpPolicy.kt b/services/permission/java/com/android/server/permission/access/appop/BaseAppOpPolicy.kt index a1a5e2d90091..7f4e0f72537e 100644 --- a/services/permission/java/com/android/server/permission/access/appop/BaseAppOpPolicy.kt +++ b/services/permission/java/com/android/server/permission/access/appop/BaseAppOpPolicy.kt @@ -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) } diff --git a/services/permission/java/com/android/server/permission/access/appop/PackageAppOpPolicy.kt b/services/permission/java/com/android/server/permission/access/appop/PackageAppOpPolicy.kt index f4d6bfd0a000..607e5120fb37 100644 --- a/services/permission/java/com/android/server/permission/access/appop/PackageAppOpPolicy.kt +++ b/services/permission/java/com/android/server/permission/access/appop/PackageAppOpPolicy.kt @@ -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 + ) } } diff --git a/services/permission/java/com/android/server/permission/access/appop/UidAppOpPolicy.kt b/services/permission/java/com/android/server/permission/access/appop/UidAppOpPolicy.kt index 862db8f35390..0b0103815e12 100644 --- a/services/permission/java/com/android/server/permission/access/appop/UidAppOpPolicy.kt +++ b/services/permission/java/com/android/server/permission/access/appop/UidAppOpPolicy.kt @@ -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 + ) } } diff --git a/services/permission/java/com/android/server/permission/access/collection/IntMap.kt b/services/permission/java/com/android/server/permission/access/collection/IntMap.kt index 0044b7359ef3..21031015c8fa 100644 --- a/services/permission/java/com/android/server/permission/access/collection/IntMap.kt +++ b/services/permission/java/com/android/server/permission/access/collection/IntMap.kt @@ -120,6 +120,18 @@ inline fun <T> IntMap<T>.putWithDefault(key: Int, value: T, defaultValue: T): T } } +// SparseArray.removeReturnOld() is @hide, so a backup once we move to APIs. +fun <T> IntMap<T>.removeReturnOld(key: Int): T? { + val index = indexOfKey(key) + return if (index >= 0) { + val oldValue = valueAt(index) + removeAt(index) + oldValue + } else { + null + } +} + inline fun <T> IntMap<T>.removeAllIndexed(predicate: (Int, Int, T) -> Boolean): Boolean { var isChanged = false for (index in lastIndex downTo 0) { |