Loading services/permission/java/com/android/server/permission/access/AccessCheckingService.kt +1 −1 Original line number Diff line number Diff line Loading @@ -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() Loading services/permission/java/com/android/server/permission/access/AccessPolicy.kt +0 −37 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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) {} Loading Loading @@ -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 ) } } services/permission/java/com/android/server/permission/access/appop/BaseAppOpPolicy.kt +5 −38 Original line number Diff line number Diff line Loading @@ -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) } Loading services/permission/java/com/android/server/permission/access/appop/PackageAppOpPolicy.kt +75 −14 Original line number Diff line number Diff line Loading @@ -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 ) } } services/permission/java/com/android/server/permission/access/appop/UidAppOpPolicy.kt +78 −14 Original line number Diff line number Diff line Loading @@ -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 ) } } Loading
services/permission/java/com/android/server/permission/access/AccessCheckingService.kt +1 −1 Original line number Diff line number Diff line Loading @@ -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() Loading
services/permission/java/com/android/server/permission/access/AccessPolicy.kt +0 −37 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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) {} Loading Loading @@ -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 ) } }
services/permission/java/com/android/server/permission/access/appop/BaseAppOpPolicy.kt +5 −38 Original line number Diff line number Diff line Loading @@ -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) } Loading
services/permission/java/com/android/server/permission/access/appop/PackageAppOpPolicy.kt +75 −14 Original line number Diff line number Diff line Loading @@ -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 ) } }
services/permission/java/com/android/server/permission/access/appop/UidAppOpPolicy.kt +78 −14 Original line number Diff line number Diff line Loading @@ -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 ) } }