Merge "Expose app op mode getter/setter/change listener directly on policy."
This commit is contained in:
@@ -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()
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 ->
|
||||
|
||||
Reference in New Issue
Block a user