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
This commit is contained in:
@@ -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()
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) }
|
||||
|
||||
@@ -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>? {
|
||||
override fun GetStateScope.getDecision(subject: AccessUri, `object`: AccessUri): Int {
|
||||
subject as PackageUri
|
||||
return state.userStates[subject.userId]?.packageAppOpModes?.get(subject.packageName)
|
||||
`object` as AppOpUri
|
||||
return getAppOpMode(subject.packageName, subject.userId, `object`.appOpName)
|
||||
}
|
||||
|
||||
override fun MutateStateScope.getOrCreateModes(subject: AccessUri): IndexedMap<String, Int> {
|
||||
override fun MutateStateScope.setDecision(
|
||||
subject: AccessUri,
|
||||
`object`: AccessUri,
|
||||
decision: Int
|
||||
) {
|
||||
subject as PackageUri
|
||||
return newState.userStates.getOrPut(subject.userId) { UserState() }
|
||||
.packageAppOpModes.getOrPut(subject.packageName) { IndexedMap() }
|
||||
}
|
||||
|
||||
override fun MutateStateScope.removeModes(subject: AccessUri) {
|
||||
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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>? {
|
||||
override fun GetStateScope.getDecision(subject: AccessUri, `object`: AccessUri): Int {
|
||||
subject as UidUri
|
||||
return state.userStates[subject.userId]?.uidAppOpModes?.get(subject.appId)
|
||||
`object` as AppOpUri
|
||||
return getAppOpMode(subject.appId, subject.userId, `object`.appOpName)
|
||||
}
|
||||
|
||||
override fun MutateStateScope.getOrCreateModes(subject: AccessUri): IndexedMap<String, Int> {
|
||||
override fun MutateStateScope.setDecision(
|
||||
subject: AccessUri,
|
||||
`object`: AccessUri,
|
||||
decision: Int
|
||||
) {
|
||||
subject as UidUri
|
||||
return newState.userStates.getOrPut(subject.userId) { UserState() }
|
||||
.uidAppOpModes.getOrPut(subject.appId) { IndexedMap() }
|
||||
}
|
||||
|
||||
override fun MutateStateScope.removeModes(subject: AccessUri) {
|
||||
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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user