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

This commit is contained in:
Hai Zhang
2022-12-14 08:05:33 +00:00
committed by Android (Google) Code Review
6 changed files with 171 additions and 104 deletions

View File

@@ -154,7 +154,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()

View File

@@ -272,10 +272,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
@@ -288,30 +284,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) {}
@@ -340,13 +312,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
)
}
}

View File

@@ -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) }

View File

@@ -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
)
}
}

View File

@@ -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
)
}
}

View File

@@ -126,6 +126,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
forEachReversedIndexed { index, key, value ->