1 /*
<lambda>null2  * 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 android.util.Slog
21 import com.android.server.permission.access.GetStateScope
22 import com.android.server.permission.access.MutableAccessState
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 import com.android.server.permission.access.immutable.* // ktlint-disable no-wildcard-imports
27 import com.android.server.pm.pkg.PackageState
28 
29 class AppIdAppOpPolicy : BaseAppOpPolicy(AppIdAppOpPersistence()) {
30     private val migration = AppIdAppOpMigration()
31 
32     private val upgrade = AppIdAppOpUpgrade(this)
33 
34     @Volatile
35     private var onAppOpModeChangedListeners: IndexedListSet<OnAppOpModeChangedListener> =
36         MutableIndexedListSet()
37     private val onAppOpModeChangedListenersLock = Any()
38 
39     override val subjectScheme: String
40         get() = UidUri.SCHEME
41 
42     override fun GetStateScope.onStateMutated() {
43         onAppOpModeChangedListeners.forEachIndexed { _, it -> it.onStateMutated() }
44     }
45 
46     override fun MutateStateScope.onAppIdRemoved(appId: Int) {
47         newState.userStates.forEachIndexed { userStateIndex, _, userState ->
48             val appIdIndex = userState.appIdAppOpModes.indexOfKey(appId)
49             if (appIdIndex >= 0) {
50                 newState
51                     .mutateUserStateAt(userStateIndex)
52                     .mutateAppIdAppOpModes()
53                     .removeAt(appIdIndex)
54                 // Skip notifying the change listeners since the app ID no longer exists.
55             }
56         }
57     }
58 
59     fun GetStateScope.getAppOpModes(appId: Int, userId: Int): IndexedMap<String, Int>? =
60         state.userStates[userId]?.appIdAppOpModes?.get(appId)
61 
62     fun MutateStateScope.removeAppOpModes(appId: Int, userId: Int): Boolean {
63         val userStateIndex = newState.userStates.indexOfKey(userId)
64         if (userStateIndex < 0) {
65             return false
66         }
67         val appIdIndex =
68             newState.userStates.valueAt(userStateIndex).appIdAppOpModes.indexOfKey(appId)
69         if (appIdIndex < 0) {
70             return false
71         }
72         newState.mutateUserStateAt(userStateIndex).mutateAppIdAppOpModes().removeAt(appIdIndex)
73         return true
74     }
75 
76     fun GetStateScope.getAppOpMode(appId: Int, userId: Int, appOpName: String): Int =
77         state.userStates[userId]
78             ?.appIdAppOpModes
79             ?.get(appId)
80             .getWithDefault(appOpName, AppOpsManager.opToDefaultMode(appOpName))
81 
82     fun MutateStateScope.setAppOpMode(
83         appId: Int,
84         userId: Int,
85         appOpName: String,
86         mode: Int
87     ): Boolean {
88         if (userId !in newState.userStates) {
89             Slog.e(LOG_TAG, "Unable to set app op mode for missing user $userId")
90             return false
91         }
92         val defaultMode = AppOpsManager.opToDefaultMode(appOpName)
93         val oldMode =
94             newState.userStates[userId]!!
95                 .appIdAppOpModes[appId]
96                 .getWithDefault(appOpName, defaultMode)
97         if (oldMode == mode) {
98             return false
99         }
100         val appIdAppOpModes = newState.mutateUserState(userId)!!.mutateAppIdAppOpModes()
101         val appOpModes = appIdAppOpModes.mutateOrPut(appId) { MutableIndexedMap() }
102         appOpModes.putWithDefault(appOpName, mode, defaultMode)
103         if (appOpModes.isEmpty()) {
104             appIdAppOpModes -= appId
105         }
106         onAppOpModeChangedListeners.forEachIndexed { _, it ->
107             it.onAppOpModeChanged(appId, userId, appOpName, oldMode, mode)
108         }
109         return true
110     }
111 
112     fun addOnAppOpModeChangedListener(listener: OnAppOpModeChangedListener) {
113         synchronized(onAppOpModeChangedListenersLock) {
114             onAppOpModeChangedListeners = onAppOpModeChangedListeners + listener
115         }
116     }
117 
118     fun removeOnAppOpModeChangedListener(listener: OnAppOpModeChangedListener) {
119         synchronized(onAppOpModeChangedListenersLock) {
120             onAppOpModeChangedListeners = onAppOpModeChangedListeners - listener
121         }
122     }
123 
124     override fun migrateUserState(state: MutableAccessState, userId: Int) {
125         with(migration) { migrateUserState(state, userId) }
126     }
127 
128     override fun MutateStateScope.upgradePackageState(
129         packageState: PackageState,
130         userId: Int,
131         version: Int,
132     ) {
133         with(upgrade) { upgradePackageState(packageState, userId, version) }
134     }
135 
136     /** Listener for app op mode changes. */
137     abstract class OnAppOpModeChangedListener {
138         /**
139          * Called when an app op mode change has been made to the upcoming new state.
140          *
141          * Implementations should keep this method fast to avoid stalling the locked state mutation,
142          * and only call external code after [onStateMutated] when the new state has actually become
143          * the current state visible to external code.
144          */
145         abstract fun onAppOpModeChanged(
146             appId: Int,
147             userId: Int,
148             appOpName: String,
149             oldMode: Int,
150             newMode: Int
151         )
152 
153         /**
154          * Called when the upcoming new state has become the current state.
155          *
156          * Implementations should keep this method fast to avoid stalling the locked state mutation.
157          */
158         abstract fun onStateMutated()
159     }
160 
161     companion object {
162         private val LOG_TAG = AppIdAppOpPolicy::class.java.simpleName
163     }
164 }
165