summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Hai Zhang <zhanghai@google.com> 2022-12-12 03:41:29 -0800
committer Hai Zhang <zhanghai@google.com> 2022-12-13 21:33:57 -0800
commite455c28fd01e292155ff921ab576ba4702c20a69 (patch)
tree5687784d5dfc40ee55faefd943086099c680cb90
parent8ec0b69dba4e8d939f603ba99527c91a10d2e034 (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
-rw-r--r--services/permission/java/com/android/server/permission/access/AccessCheckingService.kt2
-rw-r--r--services/permission/java/com/android/server/permission/access/AccessPolicy.kt37
-rw-r--r--services/permission/java/com/android/server/permission/access/appop/BaseAppOpPolicy.kt43
-rw-r--r--services/permission/java/com/android/server/permission/access/appop/PackageAppOpPolicy.kt89
-rw-r--r--services/permission/java/com/android/server/permission/access/appop/UidAppOpPolicy.kt92
-rw-r--r--services/permission/java/com/android/server/permission/access/collection/IntMap.kt12
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) {