• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.health.connect.HealthPermissions
21 import android.os.Build
22 import android.util.Slog
23 import com.android.server.permission.access.GetStateScope
24 import com.android.server.permission.access.MutateStateScope
25 import com.android.server.permission.access.immutable.* // ktlint-disable no-wildcard-imports
26 import com.android.server.permission.access.util.andInv
27 import com.android.server.permission.access.util.hasAnyBit
28 import com.android.server.permission.access.util.hasBits
29 import com.android.server.pm.pkg.PackageState
30 
31 class AppIdPermissionUpgrade(private val policy: AppIdPermissionPolicy) {
32     /**
33      * Upgrade the package permissions, if needed.
34      *
35      * @param version package version
36      * @see [com.android.server.permission.access.util.PackageVersionMigration.getVersion]
37      */
38     fun MutateStateScope.upgradePackageState(
39         packageState: PackageState,
40         userId: Int,
41         version: Int,
42     ) {
43         val packageName = packageState.packageName
44         if (version <= 3) {
45             Slog.v(
46                 LOG_TAG,
47                 "Allowlisting and upgrading background location permission for " +
48                     "package: $packageName, version: $version, user:$userId",
49             )
50             allowlistRestrictedPermissions(packageState, userId)
51             upgradeBackgroundLocationPermission(packageState, userId)
52         }
53         if (version <= 10) {
54             Slog.v(
55                 LOG_TAG,
56                 "Upgrading access media location permission for package: $packageName" +
57                     ", version: $version, user: $userId",
58             )
59             upgradeAccessMediaLocationPermission(packageState, userId)
60         }
61         // TODO Enable isAtLeastT check, when moving subsystem to mainline.
62         if (version <= 12 /*&& SdkLevel.isAtLeastT()*/) {
63             Slog.v(
64                 LOG_TAG,
65                 "Upgrading scoped media and body sensor permissions for package: $packageName" +
66                     ", version: $version, user: $userId",
67             )
68             upgradeAuralVisualMediaPermissions(packageState, userId)
69             upgradeBodySensorBackgroundPermissions(packageState, userId)
70         }
71         // TODO Enable isAtLeastU check, when moving subsystem to mainline.
72         if (version <= 14 /*&& SdkLevel.isAtLeastU()*/) {
73             Slog.v(
74                 LOG_TAG,
75                 "Upgrading visual media permission for package: $packageName" +
76                     ", version: $version, user: $userId",
77             )
78             upgradeUserSelectedVisualMediaPermission(packageState, userId)
79         }
80         // TODO Enable isAtLeastB check, when moving subsystem to mainline.
81         if (version <= 16 /*&& SdkLevel.isAtLeastB()*/) {
82             Slog.v(
83                 LOG_TAG,
84                 "Upgrading body sensor / read heart rate permissions for package: $packageName" +
85                     ", version: $version, user: $userId",
86             )
87             upgradeBodySensorReadHeartRatePermissions(packageState, userId)
88         }
89 
90         // Add a new upgrade step: if (packageVersion <= LATEST_VERSION) { .... }
91         // Also increase LATEST_VERSION
92     }
93 
94     private fun MutateStateScope.allowlistRestrictedPermissions(
95         packageState: PackageState,
96         userId: Int,
97     ) {
98         packageState.androidPackage!!.requestedPermissions.forEach { permissionName ->
99             if (permissionName in LEGACY_RESTRICTED_PERMISSIONS) {
100                 with(policy) {
101                     updatePermissionFlags(
102                         packageState.appId,
103                         userId,
104                         permissionName,
105                         PermissionFlags.UPGRADE_EXEMPT,
106                         PermissionFlags.UPGRADE_EXEMPT,
107                     )
108                 }
109             }
110         }
111     }
112 
113     private fun MutateStateScope.upgradeBackgroundLocationPermission(
114         packageState: PackageState,
115         userId: Int,
116     ) {
117         if (
118             Manifest.permission.ACCESS_BACKGROUND_LOCATION in
119                 packageState.androidPackage!!.requestedPermissions
120         ) {
121             val appId = packageState.appId
122             val accessFineLocationFlags =
123                 with(policy) {
124                     getPermissionFlags(appId, userId, Manifest.permission.ACCESS_FINE_LOCATION)
125                 }
126             val accessCoarseLocationFlags =
127                 with(policy) {
128                     getPermissionFlags(appId, userId, Manifest.permission.ACCESS_COARSE_LOCATION)
129                 }
130             val isForegroundLocationGranted =
131                 PermissionFlags.isAppOpGranted(accessFineLocationFlags) ||
132                     PermissionFlags.isAppOpGranted(accessCoarseLocationFlags)
133             if (isForegroundLocationGranted) {
134                 grantRuntimePermission(
135                     packageState,
136                     userId,
137                     Manifest.permission.ACCESS_BACKGROUND_LOCATION,
138                 )
139             }
140         }
141     }
142 
143     private fun MutateStateScope.upgradeAccessMediaLocationPermission(
144         packageState: PackageState,
145         userId: Int,
146     ) {
147         if (
148             Manifest.permission.ACCESS_MEDIA_LOCATION in
149                 packageState.androidPackage!!.requestedPermissions
150         ) {
151             val flags =
152                 with(policy) {
153                     getPermissionFlags(
154                         packageState.appId,
155                         userId,
156                         Manifest.permission.READ_EXTERNAL_STORAGE,
157                     )
158                 }
159             if (PermissionFlags.isAppOpGranted(flags)) {
160                 grantRuntimePermission(
161                     packageState,
162                     userId,
163                     Manifest.permission.ACCESS_MEDIA_LOCATION,
164                 )
165             }
166         }
167     }
168 
169     /** Upgrade permissions based on storage permissions grant */
170     private fun MutateStateScope.upgradeAuralVisualMediaPermissions(
171         packageState: PackageState,
172         userId: Int,
173     ) {
174         val androidPackage = packageState.androidPackage!!
175         if (androidPackage.targetSdkVersion < Build.VERSION_CODES.TIRAMISU) {
176             return
177         }
178         val requestedPermissionNames = androidPackage.requestedPermissions
179         val isStorageUserGranted =
180             STORAGE_PERMISSIONS.anyIndexed { _, permissionName ->
181                 if (permissionName !in requestedPermissionNames) {
182                     return@anyIndexed false
183                 }
184                 val flags =
185                     with(policy) { getPermissionFlags(packageState.appId, userId, permissionName) }
186                 PermissionFlags.isAppOpGranted(flags) && flags.hasBits(PermissionFlags.USER_SET)
187             }
188         if (isStorageUserGranted) {
189             AURAL_VISUAL_MEDIA_PERMISSIONS.forEachIndexed { _, permissionName ->
190                 if (permissionName in requestedPermissionNames) {
191                     grantRuntimePermission(packageState, userId, permissionName)
192                 }
193             }
194         }
195     }
196 
197     private fun MutateStateScope.upgradeBodySensorBackgroundPermissions(
198         packageState: PackageState,
199         userId: Int,
200     ) {
201         if (
202             Manifest.permission.BODY_SENSORS_BACKGROUND !in
203                 packageState.androidPackage!!.requestedPermissions
204         ) {
205             return
206         }
207 
208         // Should have been granted when first getting exempt as if the perm was just split
209         val appId = packageState.appId
210         val backgroundBodySensorsFlags =
211             with(policy) {
212                 getPermissionFlags(appId, userId, Manifest.permission.BODY_SENSORS_BACKGROUND)
213             }
214         if (backgroundBodySensorsFlags.hasAnyBit(PermissionFlags.MASK_EXEMPT)) {
215             return
216         }
217 
218         // Add Upgrade Exemption - BODY_SENSORS_BACKGROUND is a restricted permission
219         with(policy) {
220             updatePermissionFlags(
221                 appId,
222                 userId,
223                 Manifest.permission.BODY_SENSORS_BACKGROUND,
224                 PermissionFlags.UPGRADE_EXEMPT,
225                 PermissionFlags.UPGRADE_EXEMPT,
226             )
227         }
228 
229         val bodySensorsFlags =
230             with(policy) { getPermissionFlags(appId, userId, Manifest.permission.BODY_SENSORS) }
231         val isForegroundBodySensorsGranted = PermissionFlags.isAppOpGranted(bodySensorsFlags)
232         if (isForegroundBodySensorsGranted) {
233             grantRuntimePermission(
234                 packageState,
235                 userId,
236                 Manifest.permission.BODY_SENSORS_BACKGROUND,
237             )
238         }
239     }
240 
241     /** Upgrade permission based on the grant in [Manifest.permission_group.READ_MEDIA_VISUAL] */
242     private fun MutateStateScope.upgradeUserSelectedVisualMediaPermission(
243         packageState: PackageState,
244         userId: Int,
245     ) {
246         val androidPackage = packageState.androidPackage!!
247         if (androidPackage.targetSdkVersion < Build.VERSION_CODES.TIRAMISU) {
248             return
249         }
250         val requestedPermissionNames = androidPackage.requestedPermissions
251         val isVisualMediaUserGranted =
252             VISUAL_MEDIA_PERMISSIONS.anyIndexed { _, permissionName ->
253                 if (permissionName !in requestedPermissionNames) {
254                     return@anyIndexed false
255                 }
256                 val flags =
257                     with(policy) { getPermissionFlags(packageState.appId, userId, permissionName) }
258                 PermissionFlags.isAppOpGranted(flags) && flags.hasBits(PermissionFlags.USER_SET)
259             }
260         if (isVisualMediaUserGranted) {
261             if (Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED in requestedPermissionNames) {
262                 grantRuntimePermission(
263                     packageState,
264                     userId,
265                     Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED,
266                 )
267             }
268         }
269     }
270 
271     /**
272      * Upgrade permissions based on the body sensors and health permissions status.
273      *
274      * Starting in BAKLAVA, the BODY_SENSORS and BODY_SENSORS_BACKGROUND permissions are being
275      * replaced by the READ_HEART_RATE and READ_HEALTH_DATA_IN_BACKGROUND permissions respectively.
276      * To ensure that older apps can continue using BODY_SENSORS without breaking we need to keep
277      * their permission state in sync with the new health permissions.
278      *
279      * The approach we take is to be as conservative as possible. This means if either permission is
280      * not granted, then we want to ensure that both end up not granted to force the user to
281      * re-grant with the expanded scope.
282      */
283     private fun MutateStateScope.upgradeBodySensorReadHeartRatePermissions(
284         packageState: PackageState,
285         userId: Int,
286     ) {
287         val androidPackage = packageState.androidPackage!!
288         if (androidPackage.targetSdkVersion >= Build.VERSION_CODES.BAKLAVA) {
289             return
290         }
291 
292         // First sync BODY_SENSORS and READ_HEART_RATE, if required.
293         val isBodySensorsRequested =
294             Manifest.permission.BODY_SENSORS in androidPackage.requestedPermissions
295         val isReadHeartRateRequested =
296             HealthPermissions.READ_HEART_RATE in androidPackage.requestedPermissions
297         var isBodySensorsGranted =
298             isRuntimePermissionGranted(packageState, userId, Manifest.permission.BODY_SENSORS)
299         if (isBodySensorsRequested && isReadHeartRateRequested) {
300             val isReadHeartRateGranted =
301                 isRuntimePermissionGranted(packageState, userId, HealthPermissions.READ_HEART_RATE)
302             if (isBodySensorsGranted != isReadHeartRateGranted) {
303                 if (isBodySensorsGranted) {
304                     if (
305                         revokeRuntimePermission(
306                             packageState,
307                             userId,
308                             Manifest.permission.BODY_SENSORS,
309                         )
310                     ) {
311                         isBodySensorsGranted = false
312                     }
313                 }
314                 if (isReadHeartRateGranted) {
315                     revokeRuntimePermission(packageState, userId, HealthPermissions.READ_HEART_RATE)
316                 }
317             }
318         }
319 
320         // Then check to ensure we haven't put the background/foreground permissions out of sync.
321         var isBodySensorsBackgroundGranted =
322             isRuntimePermissionGranted(
323                 packageState,
324                 userId,
325                 Manifest.permission.BODY_SENSORS_BACKGROUND,
326             )
327         // Background permission should not be granted without the foreground permission.
328         if (!isBodySensorsGranted && isBodySensorsBackgroundGranted) {
329             if (
330                 revokeRuntimePermission(
331                     packageState,
332                     userId,
333                     Manifest.permission.BODY_SENSORS_BACKGROUND,
334                 )
335             ) {
336                 isBodySensorsBackgroundGranted = false
337             }
338         }
339 
340         // Finally sync BODY_SENSORS_BACKGROUND and READ_HEALTH_DATA_IN_BACKGROUND, if required.
341         val isBodySensorsBackgroundRequested =
342             Manifest.permission.BODY_SENSORS_BACKGROUND in androidPackage.requestedPermissions
343         val isReadHealthDataInBackgroundRequested =
344             HealthPermissions.READ_HEALTH_DATA_IN_BACKGROUND in androidPackage.requestedPermissions
345         if (isBodySensorsBackgroundRequested && isReadHealthDataInBackgroundRequested) {
346             val isReadHealthDataInBackgroundGranted =
347                 isRuntimePermissionGranted(
348                     packageState,
349                     userId,
350                     HealthPermissions.READ_HEALTH_DATA_IN_BACKGROUND,
351                 )
352             if (isBodySensorsBackgroundGranted != isReadHealthDataInBackgroundGranted) {
353                 if (isBodySensorsBackgroundGranted) {
354                     revokeRuntimePermission(
355                         packageState,
356                         userId,
357                         Manifest.permission.BODY_SENSORS_BACKGROUND,
358                     )
359                 }
360                 if (isReadHealthDataInBackgroundGranted) {
361                     revokeRuntimePermission(
362                         packageState,
363                         userId,
364                         HealthPermissions.READ_HEALTH_DATA_IN_BACKGROUND,
365                     )
366                 }
367             }
368         }
369     }
370 
371     private fun GetStateScope.isRuntimePermissionGranted(
372         packageState: PackageState,
373         userId: Int,
374         permissionName: String,
375     ): Boolean {
376         val permissionFlags =
377             with(policy) { getPermissionFlags(packageState.appId, userId, permissionName) }
378         return PermissionFlags.isAppOpGranted(permissionFlags)
379     }
380 
381     private fun MutateStateScope.grantRuntimePermission(
382         packageState: PackageState,
383         userId: Int,
384         permissionName: String,
385     ) {
386         Slog.v(
387             LOG_TAG,
388             "Granting runtime permission for package: ${packageState.packageName}, " +
389                 "permission: $permissionName, userId: $userId",
390         )
391         val permission = newState.systemState.permissions[permissionName]!!
392         if (packageState.getUserStateOrDefault(userId).isInstantApp && !permission.isInstant) {
393             return
394         }
395 
396         val appId = packageState.appId
397         var flags = with(policy) { getPermissionFlags(appId, userId, permissionName) }
398         if (flags.hasAnyBit(MASK_ANY_FIXED)) {
399             Slog.v(
400                 LOG_TAG,
401                 "Not allowed to grant $permissionName to package ${packageState.packageName}",
402             )
403             return
404         }
405 
406         flags = flags or PermissionFlags.RUNTIME_GRANTED
407         flags =
408             flags andInv
409                 (PermissionFlags.APP_OP_REVOKED or
410                     PermissionFlags.IMPLICIT or
411                     PermissionFlags.LEGACY_GRANTED or
412                     PermissionFlags.HIBERNATION or
413                     PermissionFlags.ONE_TIME)
414         with(policy) { setPermissionFlags(appId, userId, permissionName, flags) }
415     }
416 
417     /**
418      * Revoke a runtime permission for a given user from a given package.
419      *
420      * @return true if the permission was revoked, false otherwise.
421      */
422     private fun MutateStateScope.revokeRuntimePermission(
423         packageState: PackageState,
424         userId: Int,
425         permissionName: String,
426     ): Boolean {
427         Slog.v(
428             LOG_TAG,
429             "Revoking runtime permission for package: ${packageState.packageName}, " +
430                 "permission: $permissionName, userId: $userId",
431         )
432 
433         val appId = packageState.appId
434         var flags = with(policy) { getPermissionFlags(appId, userId, permissionName) }
435         if (flags.hasAnyBit(MASK_SYSTEM_OR_POLICY_FIXED)) {
436             Slog.v(
437                 LOG_TAG,
438                 "Cannot revoke fixed runtime permission from package: " +
439                     "${packageState.packageName}, permission: $permissionName, userId: $userId",
440             )
441             return false
442         }
443 
444         flags =
445             flags andInv
446                 (PermissionFlags.RUNTIME_GRANTED or
447                     MASK_USER_SETTABLE or
448                     PermissionFlags.PREGRANT or
449                     PermissionFlags.ROLE)
450         with(policy) { setPermissionFlags(appId, userId, permissionName, flags) }
451         return true
452     }
453 
454     companion object {
455         private val LOG_TAG = AppIdPermissionUpgrade::class.java.simpleName
456 
457         private const val MASK_ANY_FIXED =
458             PermissionFlags.USER_SET or
459                 PermissionFlags.ONE_TIME or
460                 PermissionFlags.USER_FIXED or
461                 PermissionFlags.POLICY_FIXED or
462                 PermissionFlags.SYSTEM_FIXED
463 
464         private const val MASK_SYSTEM_OR_POLICY_FIXED =
465             PermissionFlags.SYSTEM_FIXED or PermissionFlags.POLICY_FIXED
466 
467         private const val MASK_USER_SETTABLE =
468             PermissionFlags.USER_SET or
469                 PermissionFlags.USER_FIXED or
470                 PermissionFlags.APP_OP_REVOKED or
471                 PermissionFlags.ONE_TIME or
472                 PermissionFlags.HIBERNATION or
473                 PermissionFlags.USER_SELECTED
474 
475         private val LEGACY_RESTRICTED_PERMISSIONS =
476             indexedSetOf(
477                 Manifest.permission.ACCESS_BACKGROUND_LOCATION,
478                 Manifest.permission.READ_EXTERNAL_STORAGE,
479                 Manifest.permission.WRITE_EXTERNAL_STORAGE,
480                 Manifest.permission.SEND_SMS,
481                 Manifest.permission.RECEIVE_SMS,
482                 Manifest.permission.RECEIVE_WAP_PUSH,
483                 Manifest.permission.RECEIVE_MMS,
484                 Manifest.permission.READ_CELL_BROADCASTS,
485                 Manifest.permission.READ_CALL_LOG,
486                 Manifest.permission.WRITE_CALL_LOG,
487                 Manifest.permission.PROCESS_OUTGOING_CALLS,
488             )
489 
490         private val STORAGE_PERMISSIONS =
491             indexedSetOf(
492                 Manifest.permission.READ_EXTERNAL_STORAGE,
493                 Manifest.permission.WRITE_EXTERNAL_STORAGE,
494             )
495         private val AURAL_VISUAL_MEDIA_PERMISSIONS =
496             indexedSetOf(
497                 Manifest.permission.READ_MEDIA_AUDIO,
498                 Manifest.permission.READ_MEDIA_IMAGES,
499                 Manifest.permission.READ_MEDIA_VIDEO,
500                 Manifest.permission.ACCESS_MEDIA_LOCATION,
501                 Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED,
502             )
503         // Visual media permissions in T
504         private val VISUAL_MEDIA_PERMISSIONS =
505             indexedSetOf(
506                 Manifest.permission.READ_MEDIA_IMAGES,
507                 Manifest.permission.READ_MEDIA_VIDEO,
508                 Manifest.permission.ACCESS_MEDIA_LOCATION,
509             )
510     }
511 }
512