1 /*
<lambda>null2  * Copyright (C) 2023 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.permission
18 
19 import android.permission.PermissionManager
20 import android.util.Slog
21 import com.android.modules.utils.BinaryXmlPullParser
22 import com.android.modules.utils.BinaryXmlSerializer
23 import com.android.server.permission.access.AccessState
24 import com.android.server.permission.access.DevicePermissionUri
25 import com.android.server.permission.access.GetStateScope
26 import com.android.server.permission.access.MutableAccessState
27 import com.android.server.permission.access.MutateStateScope
28 import com.android.server.permission.access.SchemePolicy
29 import com.android.server.permission.access.UidUri
30 import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports
31 import com.android.server.permission.access.immutable.* // ktlint-disable no-wildcard-imports
32 import com.android.server.permission.access.util.andInv
33 import com.android.server.pm.pkg.PackageState
34 
35 class DevicePermissionPolicy : SchemePolicy() {
36     private val persistence = DevicePermissionPersistence()
37 
38     @Volatile
39     private var listeners: IndexedListSet<OnDevicePermissionFlagsChangedListener> =
40         MutableIndexedListSet()
41     private val listenersLock = Any()
42 
43     override val subjectScheme: String
44         get() = UidUri.SCHEME
45 
46     override val objectScheme: String
47         get() = DevicePermissionUri.SCHEME
48 
49     override fun GetStateScope.onStateMutated() {
50         listeners.forEachIndexed { _, it -> it.onStateMutated() }
51     }
52 
53     override fun MutateStateScope.onAppIdRemoved(appId: Int) {
54         newState.userStates.forEachIndexed { userStateIndex, _, userState ->
55             if (appId in userState.appIdDevicePermissionFlags) {
56                 newState.mutateUserStateAt(userStateIndex).mutateAppIdDevicePermissionFlags() -=
57                     appId
58             }
59         }
60     }
61 
62     fun MutateStateScope.trimDevicePermissionStates(deviceIds: Set<String>) {
63         newState.userStates.forEachIndexed { _, userId, userState ->
64             userState.appIdDevicePermissionFlags.forEachReversedIndexed { _, appId, _ ->
65                 val appIdDevicePermissionFlags =
66                     newState.mutateUserState(userId)!!.mutateAppIdDevicePermissionFlags()
67                 val devicePermissionFlags =
68                     appIdDevicePermissionFlags.mutate(appId) ?: return@forEachReversedIndexed
69 
70                 devicePermissionFlags.forEachReversedIndexed { _, deviceId, _ ->
71                     if (deviceId !in deviceIds) {
72                         devicePermissionFlags -= deviceId
73                     }
74                 }
75             }
76         }
77     }
78 
79     fun MutateStateScope.onDeviceIdRemoved(deviceId: String) {
80         newState.userStates.forEachIndexed { _, userId, userState ->
81             userState.appIdDevicePermissionFlags.forEachReversedIndexed { _, appId, _ ->
82                 val appIdDevicePermissionFlags =
83                     newState.mutateUserState(userId)!!.mutateAppIdDevicePermissionFlags()
84                 val devicePermissionFlags =
85                     appIdDevicePermissionFlags.mutate(appId) ?: return@forEachReversedIndexed
86                 devicePermissionFlags -= deviceId
87             }
88         }
89     }
90 
91     override fun MutateStateScope.onStorageVolumeMounted(
92         volumeUuid: String?,
93         packageNames: List<String>,
94         isSystemUpdated: Boolean
95     ) {
96         packageNames.forEachIndexed { _, packageName ->
97             // The package may still be removed even if it was once notified as installed.
98             val packageState =
99                 newState.externalState.packageStates[packageName] ?: return@forEachIndexed
100             trimPermissionStates(packageState.appId)
101         }
102     }
103 
104     override fun MutateStateScope.onPackageAdded(packageState: PackageState) {
105         trimPermissionStates(packageState.appId)
106     }
107 
108     override fun MutateStateScope.onPackageRemoved(packageName: String, appId: Int) {
109         if (appId in newState.externalState.appIdPackageNames) {
110             trimPermissionStates(appId)
111         }
112     }
113 
114     override fun MutateStateScope.onPackageUninstalled(
115         packageName: String,
116         appId: Int,
117         userId: Int
118     ) {
119         resetRuntimePermissions(packageName, userId)
120     }
121 
122     /**
123      * Reset permission states for all permissions requested by the given package, if no other
124      * package (sharing the App ID) request these permissions.
125      */
126     fun MutateStateScope.resetRuntimePermissions(packageName: String, userId: Int) {
127         // It's okay to skip resetting permissions for packages that are removed,
128         // because their states will be trimmed in onPackageRemoved()/onAppIdRemoved()
129         val packageState = newState.externalState.packageStates[packageName] ?: return
130         val androidPackage = packageState.androidPackage ?: return
131         val appId = packageState.appId
132         // The user may happen removed due to DeletePackageHelper.removeUnusedPackagesLPw() calling
133         // deletePackageX() asynchronously.
134         val userState = newState.userStates[userId] ?: return
135         val devicePermissionFlags = userState.appIdDevicePermissionFlags[appId] ?: return
136         androidPackage.requestedPermissions.forEach { permissionName ->
137             val isRequestedByOtherPackages =
138                 anyPackageInAppId(appId) {
139                     it.packageName != packageName &&
140                         permissionName in it.androidPackage!!.requestedPermissions
141                 }
142             if (isRequestedByOtherPackages) {
143                 return@forEach
144             }
145             devicePermissionFlags.forEachIndexed { _, deviceId, _ ->
146                 setPermissionFlags(appId, deviceId, userId, permissionName, 0)
147             }
148         }
149     }
150 
151     // Trims permission state for permissions not requested by the App ID anymore.
152     private fun MutateStateScope.trimPermissionStates(appId: Int) {
153         val requestedPermissions = MutableIndexedSet<String>()
154         forEachPackageInAppId(appId) {
155             requestedPermissions += it.androidPackage!!.requestedPermissions
156         }
157         newState.userStates.forEachIndexed { _, userId, userState ->
158             userState.appIdDevicePermissionFlags[appId]?.forEachReversedIndexed {
159                 _,
160                 deviceId,
161                 permissionFlags ->
162                 permissionFlags.forEachReversedIndexed { _, permissionName, _ ->
163                     if (permissionName !in requestedPermissions) {
164                         setPermissionFlags(appId, deviceId, userId, permissionName, 0)
165                     }
166                 }
167             }
168         }
169     }
170 
171     private inline fun MutateStateScope.anyPackageInAppId(
172         appId: Int,
173         state: AccessState = newState,
174         predicate: (PackageState) -> Boolean
175     ): Boolean {
176         val packageNames = state.externalState.appIdPackageNames[appId]!!
177         return packageNames.anyIndexed { _, packageName ->
178             val packageState = state.externalState.packageStates[packageName]!!
179             packageState.androidPackage != null && predicate(packageState)
180         }
181     }
182 
183     private inline fun MutateStateScope.forEachPackageInAppId(
184         appId: Int,
185         state: AccessState = newState,
186         action: (PackageState) -> Unit
187     ) {
188         val packageNames = state.externalState.appIdPackageNames[appId]!!
189         packageNames.forEachIndexed { _, packageName ->
190             val packageState = state.externalState.packageStates[packageName]!!
191             if (packageState.androidPackage != null) {
192                 action(packageState)
193             }
194         }
195     }
196 
197     override fun BinaryXmlPullParser.parseUserState(state: MutableAccessState, userId: Int) {
198         with(persistence) { this@parseUserState.parseUserState(state, userId) }
199     }
200 
201     override fun BinaryXmlSerializer.serializeUserState(state: AccessState, userId: Int) {
202         with(persistence) { this@serializeUserState.serializeUserState(state, userId) }
203     }
204 
205     fun GetStateScope.getPermissionFlags(
206         appId: Int,
207         deviceId: String,
208         userId: Int,
209         permissionName: String
210     ): Int {
211         val flags =
212             state.userStates[userId]
213                 ?.appIdDevicePermissionFlags
214                 ?.get(appId)
215                 ?.get(deviceId)
216                 ?.getWithDefault(permissionName, 0)
217                 ?: 0
218         if (PermissionManager.DEBUG_DEVICE_PERMISSIONS) {
219             Slog.i(
220                 LOG_TAG,
221                 "getPermissionFlags: appId=$appId, userId=$userId," +
222                     " deviceId=$deviceId, permissionName=$permissionName," +
223                     " flags=${PermissionFlags.toString(flags)}"
224             )
225         }
226         return flags
227     }
228 
229     fun GetStateScope.getAllPermissionFlags(
230         appId: Int,
231         persistentDeviceId: String,
232         userId: Int
233     ): IndexedMap<String, Int>? =
234         state.userStates[userId]
235             ?.appIdDevicePermissionFlags
236             ?.get(appId)
237             ?.get(persistentDeviceId)
238 
239     fun MutateStateScope.setPermissionFlags(
240         appId: Int,
241         deviceId: String,
242         userId: Int,
243         permissionName: String,
244         flags: Int
245     ): Boolean =
246         updatePermissionFlags(
247             appId,
248             deviceId,
249             userId,
250             permissionName,
251             PermissionFlags.MASK_ALL,
252             flags
253         )
254 
255     private fun MutateStateScope.updatePermissionFlags(
256         appId: Int,
257         deviceId: String,
258         userId: Int,
259         permissionName: String,
260         flagMask: Int,
261         flagValues: Int
262     ): Boolean {
263         if (userId !in newState.userStates) {
264             // Despite that we check UserManagerInternal.exists() in PermissionService, we may still
265             // sometimes get race conditions between that check and the actual mutateState() call.
266             // This should rarely happen but at least we should not crash.
267             Slog.e(LOG_TAG, "Unable to update permission flags for missing user $userId")
268             return false
269         }
270         val oldFlags =
271             newState.userStates[userId]!!
272                 .appIdDevicePermissionFlags[appId]
273                 ?.get(deviceId)
274                 .getWithDefault(permissionName, 0)
275         val newFlags = (oldFlags andInv flagMask) or (flagValues and flagMask)
276         if (oldFlags == newFlags) {
277             return false
278         }
279         val appIdDevicePermissionFlags =
280             newState.mutateUserState(userId)!!.mutateAppIdDevicePermissionFlags()
281         val devicePermissionFlags =
282             appIdDevicePermissionFlags.mutateOrPut(appId) { MutableIndexedReferenceMap() }
283         if (PermissionManager.DEBUG_DEVICE_PERMISSIONS) {
284             Slog.i(
285                 LOG_TAG,
286                 "setPermissionFlags(): appId=$appId, userId=$userId," +
287                     " deviceId=$deviceId, permissionName=$permissionName," +
288                     " newFlags=${PermissionFlags.toString(newFlags)}"
289             )
290         }
291         val permissionFlags = devicePermissionFlags.mutateOrPut(deviceId) { MutableIndexedMap() }
292         permissionFlags.putWithDefault(permissionName, newFlags, 0)
293         if (permissionFlags.isEmpty()) {
294             devicePermissionFlags -= deviceId
295             if (devicePermissionFlags.isEmpty()) {
296                 appIdDevicePermissionFlags -= appId
297             }
298         }
299         listeners.forEachIndexed { _, it ->
300             it.onDevicePermissionFlagsChanged(
301                 appId,
302                 userId,
303                 deviceId,
304                 permissionName,
305                 oldFlags,
306                 newFlags
307             )
308         }
309         return true
310     }
311 
312     fun addOnPermissionFlagsChangedListener(listener: OnDevicePermissionFlagsChangedListener) {
313         synchronized(listenersLock) { listeners = listeners + listener }
314     }
315 
316     companion object {
317         private val LOG_TAG = DevicePermissionPolicy::class.java.simpleName
318     }
319 
320     /** Listener for permission flags changes. */
321     interface OnDevicePermissionFlagsChangedListener {
322         /**
323          * Called when a permission flags change has been made to the upcoming new state.
324          *
325          * Implementations should keep this method fast to avoid stalling the locked state mutation,
326          * and only call external code after [onStateMutated] when the new state has actually become
327          * the current state visible to external code.
328          */
329         fun onDevicePermissionFlagsChanged(
330             appId: Int,
331             userId: Int,
332             deviceId: String,
333             permissionName: String,
334             oldFlags: Int,
335             newFlags: Int
336         )
337 
338         /**
339          * Called when the upcoming new state has become the current state.
340          *
341          * Implementations should keep this method fast to avoid stalling the locked state mutation.
342          */
343         fun onStateMutated()
344     }
345 }
346