1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.permission.access.appop 18 19 import android.app.AppOpsManager 20 import com.android.server.permission.access.AccessUri 21 import com.android.server.permission.access.AppOpUri 22 import com.android.server.permission.access.GetStateScope 23 import com.android.server.permission.access.MutateStateScope 24 import com.android.server.permission.access.UidUri 25 import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports 26 27 class UidAppOpPolicy : BaseAppOpPolicy(UidAppOpPersistence()) { 28 @Volatile 29 private var onAppOpModeChangedListeners = IndexedListSet<OnAppOpModeChangedListener>() 30 private val onAppOpModeChangedListenersLock = Any() 31 32 override val subjectScheme: String 33 get() = UidUri.SCHEME 34 35 override fun GetStateScope.getDecision(subject: AccessUri, `object`: AccessUri): Int { 36 subject as UidUri 37 `object` as AppOpUri 38 return getAppOpMode(subject.appId, subject.userId, `object`.appOpName) 39 } 40 41 override fun MutateStateScope.setDecision( 42 subject: AccessUri, 43 `object`: AccessUri, 44 decision: Int 45 ) { 46 subject as UidUri 47 `object` as AppOpUri 48 setAppOpMode(subject.appId, subject.userId, `object`.appOpName, decision) 49 } 50 51 override fun GetStateScope.onStateMutated() { 52 onAppOpModeChangedListeners.forEachIndexed { _, it -> it.onStateMutated() } 53 } 54 55 override fun MutateStateScope.onAppIdRemoved(appId: Int) { 56 newState.userStates.forEachIndexed { _, _, userState -> 57 userState.uidAppOpModes -= appId 58 userState.requestWrite() 59 // Skip notifying the change listeners since the app ID no longer exists. 60 } 61 } 62 63 fun GetStateScope.getAppOpModes(appId: Int, userId: Int): IndexedMap<String, Int>? = 64 state.userStates[userId].uidAppOpModes[appId] 65 66 fun MutateStateScope.removeAppOpModes(appId: Int, userId: Int): Boolean { 67 val userState = newState.userStates[userId] 68 val isChanged = userState.uidAppOpModes.removeReturnOld(appId) != null 69 if (isChanged) { 70 userState.requestWrite() 71 } 72 return isChanged 73 } 74 75 fun GetStateScope.getAppOpMode(appId: Int, userId: Int, appOpName: String): Int = 76 state.userStates[userId].uidAppOpModes[appId] 77 .getWithDefault(appOpName, AppOpsManager.opToDefaultMode(appOpName)) 78 79 fun MutateStateScope.setAppOpMode( 80 appId: Int, 81 userId: Int, 82 appOpName: String, 83 mode: Int 84 ): Boolean { 85 val userState = newState.userStates[userId] 86 val uidAppOpModes = userState.uidAppOpModes 87 var appOpModes = uidAppOpModes[appId] 88 val defaultMode = AppOpsManager.opToDefaultMode(appOpName) 89 val oldMode = appOpModes.getWithDefault(appOpName, defaultMode) 90 if (oldMode == mode) { 91 return false 92 } 93 if (appOpModes == null) { 94 appOpModes = IndexedMap() 95 uidAppOpModes[appId] = appOpModes 96 } 97 appOpModes.putWithDefault(appOpName, mode, defaultMode) 98 if (appOpModes.isEmpty()) { 99 uidAppOpModes -= appId 100 } 101 userState.requestWrite() 102 onAppOpModeChangedListeners.forEachIndexed { _, it -> 103 it.onAppOpModeChanged(appId, userId, appOpName, oldMode, mode) 104 } 105 return true 106 } 107 108 fun addOnAppOpModeChangedListener(listener: OnAppOpModeChangedListener) { 109 synchronized(onAppOpModeChangedListenersLock) { 110 onAppOpModeChangedListeners = onAppOpModeChangedListeners + listener 111 } 112 } 113 114 fun removeOnAppOpModeChangedListener(listener: OnAppOpModeChangedListener) { 115 synchronized(onAppOpModeChangedListenersLock) { 116 onAppOpModeChangedListeners = onAppOpModeChangedListeners - listener 117 } 118 } 119 120 /** 121 * Listener for app op mode changes. 122 */ 123 abstract class OnAppOpModeChangedListener { 124 /** 125 * Called when an app op mode change has been made to the upcoming new state. 126 * 127 * Implementations should keep this method fast to avoid stalling the locked state mutation, 128 * and only call external code after [onStateMutated] when the new state has actually become 129 * the current state visible to external code. 130 */ 131 abstract fun onAppOpModeChanged( 132 appId: Int, 133 userId: Int, 134 appOpName: String, 135 oldMode: Int, 136 newMode: Int 137 ) 138 139 /** 140 * Called when the upcoming new state has become the current state. 141 * 142 * Implementations should keep this method fast to avoid stalling the locked state mutation. 143 */ 144 abstract fun onStateMutated() 145 } 146 } 147