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.PackageUri 25 import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports 26 27 class PackageAppOpPolicy : BaseAppOpPolicy(PackageAppOpPersistence()) { 28 @Volatile 29 private var onAppOpModeChangedListeners = IndexedListSet<OnAppOpModeChangedListener>() 30 private val onAppOpModeChangedListenersLock = Any() 31 32 override val subjectScheme: String 33 get() = PackageUri.SCHEME 34 35 override fun GetStateScope.getDecision(subject: AccessUri, `object`: AccessUri): Int { 36 subject as PackageUri 37 `object` as AppOpUri 38 return getAppOpMode(subject.packageName, subject.userId, `object`.appOpName) 39 } 40 41 override fun MutateStateScope.setDecision( 42 subject: AccessUri, 43 `object`: AccessUri, 44 decision: Int 45 ) { 46 subject as PackageUri 47 `object` as AppOpUri 48 setAppOpMode(subject.packageName, subject.userId, `object`.appOpName, decision) 49 } 50 51 override fun GetStateScope.onStateMutated() { 52 onAppOpModeChangedListeners.forEachIndexed { _, it -> it.onStateMutated() } 53 } 54 55 override fun MutateStateScope.onPackageRemoved(packageName: String, appId: Int) { 56 newState.userStates.forEachIndexed { _, _, userState -> 57 userState.packageAppOpModes -= packageName 58 userState.requestWrite() 59 // Skip notifying the change listeners since the package no longer exists. 60 } 61 } 62 63 fun GetStateScope.getAppOpModes(packageName: String, userId: Int): IndexedMap<String, Int>? = 64 state.userStates[userId].packageAppOpModes[packageName] 65 66 fun MutateStateScope.removeAppOpModes(packageName: String, userId: Int): Boolean { 67 val userState = newState.userStates[userId] 68 val isChanged = userState.packageAppOpModes.remove(packageName) != null 69 if (isChanged) { 70 userState.requestWrite() 71 } 72 return isChanged 73 } 74 75 fun GetStateScope.getAppOpMode(packageName: String, userId: Int, appOpName: String): Int = 76 state.userStates[userId].packageAppOpModes[packageName] 77 .getWithDefault(appOpName, AppOpsManager.opToDefaultMode(appOpName)) 78 79 fun MutateStateScope.setAppOpMode( 80 packageName: String, 81 userId: Int, 82 appOpName: String, 83 mode: Int 84 ): Boolean { 85 val userState = newState.userStates[userId] 86 val packageAppOpModes = userState.packageAppOpModes 87 var appOpModes = packageAppOpModes[packageName] 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 packageAppOpModes[packageName] = appOpModes 96 } 97 appOpModes.putWithDefault(appOpName, mode, defaultMode) 98 if (appOpModes.isEmpty()) { 99 packageAppOpModes -= packageName 100 } 101 userState.requestWrite() 102 onAppOpModeChangedListeners.forEachIndexed { _, it -> 103 it.onAppOpModeChanged(packageName, 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 packageName: String, 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