diff options
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) { |