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.Manifest
20 import android.os.Build
21 import android.util.Slog
22 import com.android.server.permission.access.MutateStateScope
23 import com.android.server.permission.access.immutable.* // ktlint-disable no-wildcard-imports
24 import com.android.server.permission.access.util.andInv
25 import com.android.server.permission.access.util.hasAnyBit
26 import com.android.server.permission.access.util.hasBits
27 import com.android.server.pm.pkg.PackageState
28 
29 class AppIdPermissionUpgrade(private val policy: AppIdPermissionPolicy) {
30     /**
31      * Upgrade the package permissions, if needed.
32      *
33      * @param version package version
34      * @see [com.android.server.permission.access.util.PackageVersionMigration.getVersion]
35      */
36     fun MutateStateScope.upgradePackageState(
37         packageState: PackageState,
38         userId: Int,
39         version: Int
40     ) {
41         val packageName = packageState.packageName
42         if (version <= 3) {
43             Slog.v(
44                 LOG_TAG,
45                 "Allowlisting and upgrading background location permission for " +
46                     "package: $packageName, version: $version, user:$userId"
47             )
48             allowlistRestrictedPermissions(packageState, userId)
49             upgradeBackgroundLocationPermission(packageState, userId)
50         }
51         if (version <= 10) {
52             Slog.v(
53                 LOG_TAG,
54                 "Upgrading access media location permission for package: $packageName" +
55                     ", version: $version, user: $userId"
56             )
57             upgradeAccessMediaLocationPermission(packageState, userId)
58         }
59         // TODO Enable isAtLeastT check, when moving subsystem to mainline.
60         if (version <= 12 /*&& SdkLevel.isAtLeastT()*/) {
61             Slog.v(
62                 LOG_TAG,
63                 "Upgrading scoped media and body sensor permissions for package: $packageName" +
64                     ", version: $version, user: $userId"
65             )
66             upgradeAuralVisualMediaPermissions(packageState, userId)
67             upgradeBodySensorPermissions(packageState, userId)
68         }
69         // TODO Enable isAtLeastU check, when moving subsystem to mainline.
70         if (version <= 14 /*&& SdkLevel.isAtLeastU()*/) {
71             Slog.v(
72                 LOG_TAG,
73                 "Upgrading visual media permission for package: $packageName" +
74                     ", version: $version, user: $userId"
75             )
76             upgradeUserSelectedVisualMediaPermission(packageState, userId)
77         }
78         // Add a new upgrade step: if (packageVersion <= LATEST_VERSION) { .... }
79         // Also increase LATEST_VERSION
80     }
81 
82     private fun MutateStateScope.allowlistRestrictedPermissions(
83         packageState: PackageState,
84         userId: Int
85     ) {
86         packageState.androidPackage!!.requestedPermissions.forEach { permissionName ->
87             if (permissionName in LEGACY_RESTRICTED_PERMISSIONS) {
88                 with(policy) {
89                     updatePermissionFlags(
90                         packageState.appId,
91                         userId,
92                         permissionName,
93                         PermissionFlags.UPGRADE_EXEMPT,
94                         PermissionFlags.UPGRADE_EXEMPT
95                     )
96                 }
97             }
98         }
99     }
100 
101     private fun MutateStateScope.upgradeBackgroundLocationPermission(
102         packageState: PackageState,
103         userId: Int
104     ) {
105         if (
106             Manifest.permission.ACCESS_BACKGROUND_LOCATION in
107                 packageState.androidPackage!!.requestedPermissions
108         ) {
109             val appId = packageState.appId
110             val accessFineLocationFlags =
111                 with(policy) {
112                     getPermissionFlags(appId, userId, Manifest.permission.ACCESS_FINE_LOCATION)
113                 }
114             val accessCoarseLocationFlags =
115                 with(policy) {
116                     getPermissionFlags(appId, userId, Manifest.permission.ACCESS_COARSE_LOCATION)
117                 }
118             val isForegroundLocationGranted =
119                 PermissionFlags.isAppOpGranted(accessFineLocationFlags) ||
120                     PermissionFlags.isAppOpGranted(accessCoarseLocationFlags)
121             if (isForegroundLocationGranted) {
122                 grantRuntimePermission(
123                     packageState,
124                     userId,
125                     Manifest.permission.ACCESS_BACKGROUND_LOCATION
126                 )
127             }
128         }
129     }
130 
131     private fun MutateStateScope.upgradeAccessMediaLocationPermission(
132         packageState: PackageState,
133         userId: Int
134     ) {
135         if (
136             Manifest.permission.ACCESS_MEDIA_LOCATION in
137                 packageState.androidPackage!!.requestedPermissions
138         ) {
139             val flags =
140                 with(policy) {
141                     getPermissionFlags(
142                         packageState.appId,
143                         userId,
144                         Manifest.permission.READ_EXTERNAL_STORAGE
145                     )
146                 }
147             if (PermissionFlags.isAppOpGranted(flags)) {
148                 grantRuntimePermission(
149                     packageState,
150                     userId,
151                     Manifest.permission.ACCESS_MEDIA_LOCATION
152                 )
153             }
154         }
155     }
156 
157     /** Upgrade permissions based on storage permissions grant */
158     private fun MutateStateScope.upgradeAuralVisualMediaPermissions(
159         packageState: PackageState,
160         userId: Int
161     ) {
162         val androidPackage = packageState.androidPackage!!
163         if (androidPackage.targetSdkVersion < Build.VERSION_CODES.TIRAMISU) {
164             return
165         }
166         val requestedPermissionNames = androidPackage.requestedPermissions
167         val isStorageUserGranted =
168             STORAGE_PERMISSIONS.anyIndexed { _, permissionName ->
169                 if (permissionName !in requestedPermissionNames) {
170                     return@anyIndexed false
171                 }
172                 val flags =
173                     with(policy) { getPermissionFlags(packageState.appId, userId, permissionName) }
174                 PermissionFlags.isAppOpGranted(flags) && flags.hasBits(PermissionFlags.USER_SET)
175             }
176         if (isStorageUserGranted) {
177             AURAL_VISUAL_MEDIA_PERMISSIONS.forEachIndexed { _, permissionName ->
178                 if (permissionName in requestedPermissionNames) {
179                     grantRuntimePermission(packageState, userId, permissionName)
180                 }
181             }
182         }
183     }
184 
185     private fun MutateStateScope.upgradeBodySensorPermissions(
186         packageState: PackageState,
187         userId: Int
188     ) {
189         if (
190             Manifest.permission.BODY_SENSORS_BACKGROUND !in
191                 packageState.androidPackage!!.requestedPermissions
192         ) {
193             return
194         }
195 
196         // Should have been granted when first getting exempt as if the perm was just split
197         val appId = packageState.appId
198         val backgroundBodySensorsFlags =
199             with(policy) {
200                 getPermissionFlags(appId, userId, Manifest.permission.BODY_SENSORS_BACKGROUND)
201             }
202         if (backgroundBodySensorsFlags.hasAnyBit(PermissionFlags.MASK_EXEMPT)) {
203             return
204         }
205 
206         // Add Upgrade Exemption - BODY_SENSORS_BACKGROUND is a restricted permission
207         with(policy) {
208             updatePermissionFlags(
209                 appId,
210                 userId,
211                 Manifest.permission.BODY_SENSORS_BACKGROUND,
212                 PermissionFlags.UPGRADE_EXEMPT,
213                 PermissionFlags.UPGRADE_EXEMPT,
214             )
215         }
216 
217         val bodySensorsFlags =
218             with(policy) { getPermissionFlags(appId, userId, Manifest.permission.BODY_SENSORS) }
219         val isForegroundBodySensorsGranted = PermissionFlags.isAppOpGranted(bodySensorsFlags)
220         if (isForegroundBodySensorsGranted) {
221             grantRuntimePermission(
222                 packageState,
223                 userId,
224                 Manifest.permission.BODY_SENSORS_BACKGROUND
225             )
226         }
227     }
228 
229     /** Upgrade permission based on the grant in [Manifest.permission_group.READ_MEDIA_VISUAL] */
230     private fun MutateStateScope.upgradeUserSelectedVisualMediaPermission(
231         packageState: PackageState,
232         userId: Int
233     ) {
234         val androidPackage = packageState.androidPackage!!
235         if (androidPackage.targetSdkVersion < Build.VERSION_CODES.TIRAMISU) {
236             return
237         }
238         val requestedPermissionNames = androidPackage.requestedPermissions
239         val isVisualMediaUserGranted =
240             VISUAL_MEDIA_PERMISSIONS.anyIndexed { _, permissionName ->
241                 if (permissionName !in requestedPermissionNames) {
242                     return@anyIndexed false
243                 }
244                 val flags =
245                     with(policy) { getPermissionFlags(packageState.appId, userId, permissionName) }
246                 PermissionFlags.isAppOpGranted(flags) && flags.hasBits(PermissionFlags.USER_SET)
247             }
248         if (isVisualMediaUserGranted) {
249             if (Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED in requestedPermissionNames) {
250                 grantRuntimePermission(
251                     packageState,
252                     userId,
253                     Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED
254                 )
255             }
256         }
257     }
258 
259     private fun MutateStateScope.grantRuntimePermission(
260         packageState: PackageState,
261         userId: Int,
262         permissionName: String
263     ) {
264         Slog.v(
265             LOG_TAG,
266             "Granting runtime permission for package: ${packageState.packageName}, " +
267                 "permission: $permissionName, userId: $userId"
268         )
269         val permission = newState.systemState.permissions[permissionName]!!
270         if (packageState.getUserStateOrDefault(userId).isInstantApp && !permission.isInstant) {
271             return
272         }
273 
274         val appId = packageState.appId
275         var flags = with(policy) { getPermissionFlags(appId, userId, permissionName) }
276         if (flags.hasAnyBit(MASK_ANY_FIXED)) {
277             Slog.v(
278                 LOG_TAG,
279                 "Not allowed to grant $permissionName to package ${packageState.packageName}"
280             )
281             return
282         }
283 
284         flags = flags or PermissionFlags.RUNTIME_GRANTED
285         flags =
286             flags andInv
287                 (PermissionFlags.APP_OP_REVOKED or
288                     PermissionFlags.IMPLICIT or
289                     PermissionFlags.LEGACY_GRANTED or
290                     PermissionFlags.HIBERNATION or
291                     PermissionFlags.ONE_TIME)
292         with(policy) { setPermissionFlags(appId, userId, permissionName, flags) }
293     }
294 
295     companion object {
296         private val LOG_TAG = AppIdPermissionUpgrade::class.java.simpleName
297 
298         private const val MASK_ANY_FIXED =
299             PermissionFlags.USER_SET or
300                 PermissionFlags.USER_FIXED or
301                 PermissionFlags.POLICY_FIXED or
302                 PermissionFlags.SYSTEM_FIXED
303 
304         private val LEGACY_RESTRICTED_PERMISSIONS =
305             indexedSetOf(
306                 Manifest.permission.ACCESS_BACKGROUND_LOCATION,
307                 Manifest.permission.READ_EXTERNAL_STORAGE,
308                 Manifest.permission.WRITE_EXTERNAL_STORAGE,
309                 Manifest.permission.SEND_SMS,
310                 Manifest.permission.RECEIVE_SMS,
311                 Manifest.permission.RECEIVE_WAP_PUSH,
312                 Manifest.permission.RECEIVE_MMS,
313                 Manifest.permission.READ_CELL_BROADCASTS,
314                 Manifest.permission.READ_CALL_LOG,
315                 Manifest.permission.WRITE_CALL_LOG,
316                 Manifest.permission.PROCESS_OUTGOING_CALLS
317             )
318 
319         private val STORAGE_PERMISSIONS =
320             indexedSetOf(
321                 Manifest.permission.READ_EXTERNAL_STORAGE,
322                 Manifest.permission.WRITE_EXTERNAL_STORAGE
323             )
324         private val AURAL_VISUAL_MEDIA_PERMISSIONS =
325             indexedSetOf(
326                 Manifest.permission.READ_MEDIA_AUDIO,
327                 Manifest.permission.READ_MEDIA_IMAGES,
328                 Manifest.permission.READ_MEDIA_VIDEO,
329                 Manifest.permission.ACCESS_MEDIA_LOCATION,
330                 Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED
331             )
332         // Visual media permissions in T
333         private val VISUAL_MEDIA_PERMISSIONS =
334             indexedSetOf(
335                 Manifest.permission.READ_MEDIA_IMAGES,
336                 Manifest.permission.READ_MEDIA_VIDEO,
337                 Manifest.permission.ACCESS_MEDIA_LOCATION
338             )
339     }
340 }
341