• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2019 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.permissioncontroller.permission.service
18 
19 import android.Manifest.permission
20 import android.Manifest.permission_group
21 import android.content.Context
22 import android.content.pm.PackageInfo
23 import android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT
24 import android.content.pm.PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE
25 import android.content.pm.PermissionInfo
26 import android.os.Build
27 import android.os.Process.myUserHandle
28 import android.permission.PermissionManager
29 import android.util.Log
30 import com.android.modules.utils.build.SdkLevel
31 import com.android.permissioncontroller.DeviceUtils
32 import com.android.permissioncontroller.PermissionControllerStatsLog
33 import com.android.permissioncontroller.PermissionControllerStatsLog.RUNTIME_PERMISSIONS_UPGRADE_RESULT
34 import com.android.permissioncontroller.permission.data.LightAppPermGroupLiveData
35 import com.android.permissioncontroller.permission.data.LightPermInfoLiveData
36 import com.android.permissioncontroller.permission.data.PreinstalledUserPackageInfosLiveData
37 import com.android.permissioncontroller.permission.data.SmartUpdateMediatorLiveData
38 import com.android.permissioncontroller.permission.data.UserPackageInfosLiveData
39 import com.android.permissioncontroller.permission.data.get
40 import com.android.permissioncontroller.permission.model.livedatatypes.LightAppPermGroup
41 import com.android.permissioncontroller.permission.model.livedatatypes.LightPackageInfo
42 import com.android.permissioncontroller.permission.model.livedatatypes.LightPermission
43 import com.android.permissioncontroller.permission.utils.IPC
44 import com.android.permissioncontroller.permission.utils.KotlinUtils.grantBackgroundRuntimePermissions
45 import com.android.permissioncontroller.permission.utils.KotlinUtils.grantForegroundRuntimePermissions
46 import com.android.permissioncontroller.permission.utils.PermissionMapping
47 import com.android.permissioncontroller.permission.utils.PermissionMapping.getPlatformPermissionNamesOfGroup
48 import com.android.permissioncontroller.permission.utils.PermissionMapping.getRuntimePlatformPermissionNames
49 import com.android.permissioncontroller.permission.utils.Utils.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT
50 import com.android.permissioncontroller.permission.utils.application
51 import kotlinx.coroutines.GlobalScope
52 import kotlinx.coroutines.launch
53 
54 /** This class handles upgrading the runtime permissions database */
55 object RuntimePermissionsUpgradeController {
56     private val LOG_TAG = RuntimePermissionsUpgradeController::class.java.simpleName
57     private const val DEBUG = false
58 
59     // The latest version of the runtime permissions database
60     private val LATEST_VERSION =
61         if (SdkLevel.isAtLeastU()) {
62             11
63         } else if (SdkLevel.isAtLeastT()) {
64             10
65         } else {
66             9
67         }
68 
69     @Suppress("MissingPermission")
70     fun upgradeIfNeeded(context: Context, onComplete: Runnable) {
71         val permissionManager = context.getSystemService(PermissionManager::class.java)
72         val storedVersion = permissionManager!!.runtimePermissionsVersion
73         val currentVersion = minOf(storedVersion, LATEST_VERSION)
74 
75         GlobalScope.launch(IPC) {
76             val upgradedVersion = onUpgradeLocked(context, currentVersion)
77             if (upgradedVersion != LATEST_VERSION) {
78                 Log.wtf(
79                     "PermissionControllerService",
80                     "warning: upgrading permission database" +
81                         " to version $LATEST_VERSION left it at $currentVersion instead; this is " +
82                         "probably a bug. Did you update LATEST_VERSION?",
83                     Throwable()
84                 )
85                 throw RuntimeException("db upgrade error")
86             }
87 
88             if (storedVersion != upgradedVersion) {
89                 permissionManager.runtimePermissionsVersion = LATEST_VERSION
90             }
91             onComplete.run()
92         }
93     }
94 
95     /**
96      * Create exemptions for select restricted permissions of select apps.
97      *
98      * @param permissionInfos permissions to exempt
99      * @param pkgs packages to exempt
100      * @return the exemptions to apply
101      */
102     private fun getExemptions(
103         permissions: Set<String>,
104         pkgs: List<LightPackageInfo>,
105         flags: Int = FLAG_PERMISSION_WHITELIST_UPGRADE
106     ): List<RestrictionExemption> {
107         val exemptions = mutableListOf<RestrictionExemption>()
108 
109         for (pkg in pkgs) {
110             for (permission in permissions intersect pkg.requestedPermissions) {
111                 exemptions.add(RestrictionExemption(pkg.packageName, permission, flags))
112             }
113         }
114 
115         return exemptions
116     }
117 
118     /**
119      * You must perform all necessary mutations to bring the runtime permissions database from the
120      * old to the new version. When you add a new upgrade step you *must* update LATEST_VERSION.
121      *
122      * <p> NOTE: Relies upon the fact that the system will attempt to upgrade every version after
123      * currentVersion in order, without skipping any versions. Should this become the case, this
124      * method MUST be updated.
125      *
126      * @param context The current context
127      * @param currentVersion The current version of the permission database
128      */
129     private suspend fun onUpgradeLocked(context: Context, currentVersion: Int): Int {
130         var sdkUpgradedFromP = false
131         var isNewUser = false
132 
133         if (currentVersion <= -1) {
134             sdkUpgradedFromP = true
135         } else if (currentVersion == 0) {
136             isNewUser = true
137         }
138 
139         val needBackgroundAppPermGroups = sdkUpgradedFromP && currentVersion <= 6
140         val needAccessMediaAppPermGroups = !isNewUser && currentVersion <= 7
141         val needGrantedExternalStorage = currentVersion <= 9 && SdkLevel.isAtLeastT()
142         val needBodySensorsAppPermGroups =
143             DeviceUtils.isWear(context) && currentVersion <= 9 && SdkLevel.isAtLeastT()
144         val needGrantedReadMediaVisual = currentVersion <= 10 && SdkLevel.isAtLeastU()
145         val isDeviceUpgrading = context.packageManager.isDeviceUpgrading
146 
147         // All data needed by this method.
148         //
149         // All data is loaded once and then not updated.
150         val upgradeDataProvider =
151             object : SmartUpdateMediatorLiveData<UpgradeData>() {
152                 /** Provides all preinstalled packages in the system */
153                 private val preinstalledPkgInfoProvider =
154                     PreinstalledUserPackageInfosLiveData[myUserHandle()]
155 
156                 /** Provides all platform runtime permission infos */
157                 private val platformRuntimePermissionInfoProviders =
158                     mutableListOf<LightPermInfoLiveData>()
159 
160                 /** {@link #platformRuntimePermissionInfoProvider} that already provided a result */
161                 private val platformRuntimePermissionInfoProvidersDone =
162                     mutableSetOf<LightPermInfoLiveData>()
163 
164                 /** Provides all packages in the system */
165                 private val pkgInfoProvider = UserPackageInfosLiveData[myUserHandle()]
166 
167                 /** Provides all {@link LightAppPermGroup} this upgrade needs */
168                 private var permGroupProviders: MutableSet<LightAppPermGroupLiveData>? = null
169 
170                 /** {@link #permGroupProviders} that already provided a result */
171                 private val permGroupProvidersDone = mutableSetOf<LightAppPermGroupLiveData>()
172 
173                 init {
174                     // First step: Load packages + perm infos
175                     addSource(pkgInfoProvider) { pkgInfos ->
176                         if (pkgInfos != null) {
177                             removeSource(pkgInfoProvider)
178 
179                             addSource(preinstalledPkgInfoProvider) { preinstalledPkgInfos ->
180                                 if (preinstalledPkgInfos != null) {
181                                     removeSource(preinstalledPkgInfoProvider)
182 
183                                     update()
184                                 }
185                             }
186                         }
187                     }
188 
189                     for (platformRuntimePermission in getRuntimePlatformPermissionNames()) {
190                         val permProvider = LightPermInfoLiveData[platformRuntimePermission]
191                         platformRuntimePermissionInfoProviders.add(permProvider)
192 
193                         addSource(permProvider) { permInfo ->
194                             if (permInfo != null) {
195                                 platformRuntimePermissionInfoProvidersDone.add(permProvider)
196                                 removeSource(permProvider)
197 
198                                 update()
199                             }
200                         }
201                     }
202                 }
203 
204                 override fun onUpdate() {
205                     if (permGroupProviders == null && pkgInfoProvider.value != null) {
206                         // Second step: Trigger load of app-perm-groups
207 
208                         permGroupProviders = mutableSetOf()
209 
210                         // Only load app-perm-groups needed for this upgrade
211                         if (
212                             needBackgroundAppPermGroups ||
213                                 needAccessMediaAppPermGroups ||
214                                 needGrantedExternalStorage ||
215                                 needGrantedReadMediaVisual ||
216                                 needBodySensorsAppPermGroups
217                         ) {
218                             for ((pkgName, _, requestedPerms, requestedPermFlags) in
219                                 pkgInfoProvider.value!!) {
220                                 var requestsAccessMediaLocation = false
221                                 var hasGrantedExternalStorage = false
222                                 var hasGrantedReadMediaVisual = false
223 
224                                 for ((perm, flags) in requestedPerms.zip(requestedPermFlags)) {
225                                     if (
226                                         needBackgroundAppPermGroups &&
227                                             perm == permission.ACCESS_BACKGROUND_LOCATION
228                                     ) {
229                                         permGroupProviders!!.add(
230                                             LightAppPermGroupLiveData[
231                                                 pkgName, permission_group.LOCATION, myUserHandle()]
232                                         )
233                                     }
234 
235                                     if (
236                                         needAccessMediaAppPermGroups ||
237                                             needGrantedExternalStorage ||
238                                             needGrantedReadMediaVisual
239                                     ) {
240                                         if (
241                                             needAccessMediaAppPermGroups &&
242                                                 perm == permission.ACCESS_MEDIA_LOCATION
243                                         ) {
244                                             requestsAccessMediaLocation = true
245                                         }
246 
247                                         val isGranted =
248                                             flags and PackageInfo.REQUESTED_PERMISSION_GRANTED != 0
249                                         if (perm == permission.READ_EXTERNAL_STORAGE && isGranted) {
250                                             hasGrantedExternalStorage = true
251                                         }
252                                         if (
253                                             PermissionMapping.getGroupOfPlatformPermission(perm) ==
254                                                 permission_group.READ_MEDIA_VISUAL && isGranted
255                                         ) {
256                                             hasGrantedReadMediaVisual = true
257                                         }
258                                     }
259 
260                                     if (
261                                         needBodySensorsAppPermGroups &&
262                                             perm == permission.BODY_SENSORS_BACKGROUND
263                                     ) {
264                                         permGroupProviders!!.add(
265                                             LightAppPermGroupLiveData[
266                                                 pkgName, permission_group.SENSORS, myUserHandle()]
267                                         )
268                                     }
269                                 }
270 
271                                 val accessMediaLocationPermGroup =
272                                     if (SdkLevel.isAtLeastT()) permission_group.READ_MEDIA_VISUAL
273                                     else permission_group.STORAGE
274 
275                                 if (hasGrantedExternalStorage) {
276                                     if (needGrantedExternalStorage) {
277                                         permGroupProviders!!.add(
278                                             LightAppPermGroupLiveData[
279                                                 pkgName, permission_group.STORAGE, myUserHandle()]
280                                         )
281                                         if (SdkLevel.isAtLeastT()) {
282                                             permGroupProviders!!.add(
283                                                 LightAppPermGroupLiveData[
284                                                     pkgName,
285                                                     permission_group.READ_MEDIA_VISUAL,
286                                                     myUserHandle()]
287                                             )
288                                             permGroupProviders!!.add(
289                                                 LightAppPermGroupLiveData[
290                                                     pkgName,
291                                                     permission_group.READ_MEDIA_AURAL,
292                                                     myUserHandle()]
293                                             )
294                                         }
295                                     } else if (requestsAccessMediaLocation) {
296                                         permGroupProviders!!.add(
297                                             LightAppPermGroupLiveData[
298                                                 pkgName,
299                                                 accessMediaLocationPermGroup,
300                                                 myUserHandle()]
301                                         )
302                                     }
303                                 }
304                                 if (hasGrantedReadMediaVisual && needGrantedReadMediaVisual) {
305                                     permGroupProviders!!.add(
306                                         LightAppPermGroupLiveData[
307                                             pkgName,
308                                             permission_group.READ_MEDIA_VISUAL,
309                                             myUserHandle()]
310                                     )
311                                 }
312                             }
313                         }
314 
315                         // Wait until groups are loaded and then trigger third step
316                         for (permGroupProvider in permGroupProviders!!) {
317                             addSource(permGroupProvider) { group ->
318                                 if (group != null) {
319                                     permGroupProvidersDone.add(permGroupProvider)
320                                     removeSource(permGroupProvider)
321 
322                                     update()
323                                 }
324                             }
325                         }
326 
327                         // If no group need to be loaded, directly switch to third step
328                         if (permGroupProviders!!.isEmpty()) {
329                             update()
330                         }
331                     } else if (
332                         permGroupProviders != null &&
333                             permGroupProvidersDone.size == permGroupProviders!!.size &&
334                             preinstalledPkgInfoProvider.value != null &&
335                             platformRuntimePermissionInfoProviders.size ==
336                                 platformRuntimePermissionInfoProvidersDone.size
337                     ) {
338                         // Third step: All packages, perm infos and perm groups are loaded, set
339                         // value
340 
341                         val bgGroups = mutableListOf<LightAppPermGroup>()
342                         val storageGroups = mutableListOf<LightAppPermGroup>()
343                         val bgSensorsGroups = mutableListOf<LightAppPermGroup>()
344 
345                         for (group in permGroupProviders!!.mapNotNull { it.value }) {
346                             when (group.permGroupName) {
347                                 permission_group.LOCATION -> {
348                                     bgGroups.add(group)
349                                 }
350                                 permission_group.STORAGE -> {
351                                     storageGroups.add(group)
352                                 }
353                                 permission_group.READ_MEDIA_AURAL -> {
354                                     storageGroups.add(group)
355                                 }
356                                 permission_group.READ_MEDIA_VISUAL -> {
357                                     storageGroups.add(group)
358                                 }
359                                 permission_group.SENSORS -> {
360                                     bgSensorsGroups.add(group)
361                                 }
362                             }
363                         }
364 
365                         val restrictedPermissions = mutableSetOf<String>()
366                         for (permInfoLiveDt in platformRuntimePermissionInfoProviders) {
367                             val permInfo = permInfoLiveDt.value!!
368 
369                             if (
370                                 permInfo.flags and
371                                     (PermissionInfo.FLAG_HARD_RESTRICTED or
372                                         PermissionInfo.FLAG_SOFT_RESTRICTED) == 0
373                             ) {
374                                 continue
375                             }
376 
377                             restrictedPermissions.add(permInfo.name)
378                         }
379 
380                         value =
381                             UpgradeData(
382                                 preinstalledPkgInfoProvider.value!!,
383                                 restrictedPermissions,
384                                 pkgInfoProvider.value!!,
385                                 bgGroups,
386                                 storageGroups,
387                                 bgSensorsGroups
388                             )
389                     }
390                 }
391             }
392 
393         // Trigger loading of data and wait until data is loaded
394         val upgradeData = upgradeDataProvider.getInitializedValue(forceUpdate = true)!!
395 
396         // Only exempt permissions that are in the OTA. Apps that are updated via OTAs are never
397         // installed. Hence their permission are never exempted. This code replaces that by
398         // always exempting them. For non-OTA updates the installer should do the exemption.
399         // If a restricted permission can't be exempted by the installer then it should be filtered
400         // out here.
401         val preinstalledAppExemptions =
402             getExemptions(upgradeData.restrictedPermissions, upgradeData.preinstalledPkgs)
403 
404         val (newVersion, upgradeExemptions, grants) =
405             onUpgradeLockedDataLoaded(
406                 currentVersion,
407                 upgradeData.pkgs,
408                 upgradeData.restrictedPermissions,
409                 upgradeData.bgGroups,
410                 upgradeData.storageGroups,
411                 upgradeData.bgSensorsGroups,
412                 isDeviceUpgrading
413             )
414 
415         // Do not run in parallel. Measurements have shown that this is slower than sequential
416         for (exemption in (preinstalledAppExemptions union upgradeExemptions)) {
417             exemption.applyToPlatform(context)
418         }
419 
420         for (grant in grants) {
421             grant.applyToPlatform(context)
422         }
423 
424         return newVersion
425     }
426 
427     private fun onUpgradeLockedDataLoaded(
428         currVersion: Int,
429         pkgs: List<LightPackageInfo>,
430         restrictedPermissions: Set<String>,
431         bgApps: List<LightAppPermGroup>,
432         storageAndMediaAppPermGroups: List<LightAppPermGroup>,
433         bgSensorsGroups: List<LightAppPermGroup>,
434         isDeviceUpgrading: Boolean
435     ): Triple<Int, List<RestrictionExemption>, List<Grant>> {
436         val exemptions = mutableListOf<RestrictionExemption>()
437         val grants = mutableListOf<Grant>()
438 
439         var currentVersion = currVersion
440         var sdkUpgradedFromP = false
441         var isNewUser = false
442         val bgAppsWithExemption = bgApps.map { it.packageName to it }.toMap().toMutableMap()
443 
444         if (currentVersion <= -1) {
445             Log.i(LOG_TAG, "Upgrading from Android P")
446 
447             sdkUpgradedFromP = true
448 
449             currentVersion = 0
450         } else {
451             // If the initial version is 0 the permission state was just created
452             if (currentVersion == 0) {
453                 isNewUser = true
454             }
455         }
456 
457         if (currentVersion == 0) {
458             Log.i(LOG_TAG, "Grandfathering SMS and CallLog permissions")
459 
460             val permissions =
461                 restrictedPermissions intersect
462                     (getPlatformPermissionNamesOfGroup(permission_group.SMS) +
463                         getPlatformPermissionNamesOfGroup(permission_group.CALL_LOG))
464 
465             exemptions.addAll(getExemptions(permissions, pkgs))
466 
467             currentVersion = 1
468         }
469 
470         if (currentVersion == 1) {
471             // moved to step 4->5 as it has to be after the grandfathering of loc bg perms
472             currentVersion = 2
473         }
474 
475         if (currentVersion == 2) {
476             // moved to step 5->6 to clean up broken permission state during dogfooding
477             currentVersion = 3
478         }
479 
480         if (currentVersion == 3) {
481             Log.i(LOG_TAG, "Grandfathering location background permissions")
482 
483             val bgLocExemptions = getExemptions(setOf(permission.ACCESS_BACKGROUND_LOCATION), pkgs)
484 
485             // Adjust bgApps as if the exemption was applied
486             for ((pkgName, _) in bgLocExemptions) {
487                 val bgApp = bgAppsWithExemption[pkgName] ?: continue
488                 val perm = bgApp.allPermissions[permission.ACCESS_BACKGROUND_LOCATION] ?: continue
489 
490                 val allPermissionsWithxemption = bgApp.allPermissions.toMutableMap()
491                 allPermissionsWithxemption[permission.ACCESS_BACKGROUND_LOCATION] =
492                     LightPermission(
493                         perm.pkgInfo,
494                         perm.permInfo,
495                         perm.isGranted,
496                         perm.flags or FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT,
497                         perm.foregroundPerms
498                     )
499 
500                 bgAppsWithExemption[pkgName] =
501                     LightAppPermGroup(
502                         bgApp.packageInfo,
503                         bgApp.permGroupInfo,
504                         allPermissionsWithxemption,
505                         bgApp.hasInstallToRuntimeSplit,
506                         bgApp.specialLocationGrant,
507                         bgApp.specialFixedStorageGrant,
508                     )
509             }
510 
511             exemptions.addAll(bgLocExemptions)
512 
513             currentVersion = 4
514         }
515 
516         if (currentVersion == 4) {
517             // moved to step 5->6 to clean up broken permission state during beta 4->5 upgrade
518             currentVersion = 5
519         }
520 
521         if (currentVersion == 5) {
522             Log.i(LOG_TAG, "Grandfathering Storage permissions")
523 
524             val permissions =
525                 restrictedPermissions intersect
526                     getPlatformPermissionNamesOfGroup(permission_group.STORAGE)
527 
528             // We don't want to allow modification of storage post install, so put it
529             // on the internal system exemptlist to prevent the installer changing it.
530             exemptions.addAll(getExemptions(permissions, pkgs))
531 
532             currentVersion = 6
533         }
534 
535         if (currentVersion == 6) {
536             if (sdkUpgradedFromP) {
537                 Log.i(LOG_TAG, "Expanding location permissions")
538                 for (appPermGroup in bgAppsWithExemption.values) {
539                     if (
540                         appPermGroup.foreground.isGranted &&
541                             appPermGroup.hasBackgroundGroup &&
542                             !appPermGroup.background.isUserSet &&
543                             !appPermGroup.background.isSystemFixed &&
544                             !appPermGroup.background.isPolicyFixed &&
545                             !appPermGroup.background.isUserFixed
546                     ) {
547                         grants.add(Grant(true, appPermGroup))
548                     }
549                 }
550             } else {
551                 Log.i(
552                     LOG_TAG,
553                     "Not expanding location permissions as this is not an upgrade " +
554                         "from Android P"
555                 )
556             }
557 
558             currentVersion = 7
559         }
560 
561         if (currentVersion == 7) {
562             if (!isNewUser) {
563                 Log.i(LOG_TAG, "Expanding read storage to access media location")
564 
565                 for (appPermGroup in storageAndMediaAppPermGroups) {
566                     val perm =
567                         appPermGroup.permissions[permission.ACCESS_MEDIA_LOCATION] ?: continue
568 
569                     if (
570                         !perm.isGranted &&
571                         !perm.isUserSet &&
572                         !perm.isOneTime &&
573                         !perm.isSystemFixed &&
574                         !perm.isPolicyFixed
575                     ) {
576                         grants.add(
577                             Grant(false, appPermGroup, listOf(permission.ACCESS_MEDIA_LOCATION))
578                         )
579                     }
580                 }
581             } else {
582                 Log.i(
583                     LOG_TAG,
584                     "Not expanding read storage to access media location as this is " + "a new user"
585                 )
586             }
587 
588             currentVersion = 8
589         }
590 
591         if (currentVersion == 8) {
592             // Removed
593 
594             currentVersion = 9
595         }
596 
597         if (currentVersion == 9 && SdkLevel.isAtLeastT()) {
598             if (isNewUser) {
599                 Log.i(
600                     LOG_TAG,
601                     "Not migrating STORAGE and BODY_SENSORS permissions as this is a new user"
602                 )
603             } else if (!isDeviceUpgrading) {
604                 Log.i(
605                     LOG_TAG,
606                     "Not migrating STORAGE and BODY_SENSORS permissions as" +
607                         " this device is not performing an upgrade"
608                 )
609             } else {
610                 Log.i(LOG_TAG, "Migrating STORAGE permissions to READ_MEDIA permissions")
611 
612                 // Upon upgrading to platform 33, for all targetSdk>=33 apps, do the following:
613                 // If STORAGE is granted, and the user has not set READ_MEDIA_AURAL or
614                 // READ_MEDIA_VISUAL, grant READ_MEDIA_AURAL and READ_MEDIA_VISUAL
615                 val grantedStorageAppPermGroups =
616                     storageAndMediaAppPermGroups.filter {
617                         it.packageInfo.targetSdkVersion >= Build.VERSION_CODES.TIRAMISU &&
618                             it.permGroupInfo.name == permission_group.STORAGE &&
619                             it.isGranted &&
620                             it.isUserSet
621                     }
622                 for (storageAppPermGroup in grantedStorageAppPermGroups) {
623                     val pkgName = storageAppPermGroup.packageInfo.packageName
624                     val auralAppPermGroup =
625                         storageAndMediaAppPermGroups.firstOrNull {
626                             it.packageInfo.packageName == pkgName &&
627                                 it.permGroupInfo.name == permission_group.READ_MEDIA_AURAL &&
628                                 !it.isUserSet &&
629                                 !it.isOneTime &&
630                                 !it.isUserFixed
631                         }
632                     val visualAppPermGroup =
633                         storageAndMediaAppPermGroups.firstOrNull {
634                             it.packageInfo.packageName == pkgName &&
635                                 it.permGroupInfo.name == permission_group.READ_MEDIA_VISUAL &&
636                                 !it.permissions
637                                     .filter { it.key != permission.ACCESS_MEDIA_LOCATION }
638                                     .any { it.value.isUserSet || it.value.isOneTime ||
639                                         it.value.isUserFixed }
640                         }
641 
642                     if (auralAppPermGroup != null) {
643                         grants.add(Grant(false, auralAppPermGroup))
644                     }
645                     if (visualAppPermGroup != null) {
646                         grants.add(Grant(false, visualAppPermGroup))
647                     }
648                 }
649 
650                 // Granting body sensors background permission to apps that had the pre-split body
651                 // sensors permission granted.
652                 Log.i(LOG_TAG, "Grandfathering body sensors background permissions")
653 
654                 for (bgSensorsGroup in bgSensorsGroups) {
655                     val perm =
656                         bgSensorsGroup.allPermissions[permission.BODY_SENSORS_BACKGROUND]
657                             ?: continue
658                     if (perm.flags and FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT != 0) {
659                         continue
660                     }
661 
662                     // Exempt the background permission to allow setting from users.
663                     val pkgName = bgSensorsGroup.packageName
664                     exemptions.add(
665                         RestrictionExemption(
666                             pkgName,
667                             permission.BODY_SENSORS_BACKGROUND,
668                             FLAG_PERMISSION_WHITELIST_UPGRADE
669                         )
670                     )
671 
672                     val allPermissionsWithExemption = bgSensorsGroup.allPermissions.toMutableMap()
673                     allPermissionsWithExemption[permission.BODY_SENSORS_BACKGROUND] =
674                         LightPermission(
675                             perm.pkgInfo,
676                             perm.permInfo,
677                             perm.isGranted,
678                             perm.flags or FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT,
679                             perm.foregroundPerms
680                         )
681                     val group =
682                         LightAppPermGroup(
683                             bgSensorsGroup.packageInfo,
684                             bgSensorsGroup.permGroupInfo,
685                             allPermissionsWithExemption,
686                             bgSensorsGroup.hasInstallToRuntimeSplit,
687                             bgSensorsGroup.specialLocationGrant,
688                             bgSensorsGroup.specialFixedStorageGrant,
689                         )
690 
691                     // Grant the background permission only if foreground permission is granted.
692                     if (group.foreground.isGranted) {
693                         if (DEBUG) {
694                             Log.i(LOG_TAG, "$pkgName Granted body sensors background permissions")
695                         }
696                         grants.add(Grant(isBackground = true, group = group))
697                     }
698                 }
699             }
700             currentVersion = 10
701         }
702 
703         if (currentVersion == 10 && SdkLevel.isAtLeastU()) {
704             // On U, if the app is granted READ_MEDIA_VISUAL, expand the grant to
705             // READ_MEDIA_VISUAL_USER_SELECTED
706             if (isDeviceUpgrading && !isNewUser) {
707                 Log.i(
708                     LOG_TAG,
709                     "Grandfathering READ_MEDIA_VISUAL_USER_SELECTED to apps already " +
710                         "granted visual permissions"
711                 )
712                 val visualAppPermGroups =
713                     storageAndMediaAppPermGroups.filter {
714                         it.packageInfo.targetSdkVersion >= Build.VERSION_CODES.TIRAMISU &&
715                             it.permGroupInfo.name == permission_group.READ_MEDIA_VISUAL &&
716                             it.isGranted &&
717                             it.isUserSet
718                     }
719                 visualAppPermGroups.forEach { grants.add(Grant(false, it)) }
720             }
721             currentVersion = 11
722         }
723 
724         // XXX: Add new upgrade steps above this point.
725 
726         return Triple(currentVersion, exemptions, grants)
727     }
728 
729     /** All data needed by {@link #onUpgradeLocked} */
730     private data class UpgradeData(
731         /** Preinstalled packages */
732         val preinstalledPkgs: List<LightPackageInfo>,
733         /** Restricted permissions */
734         val restrictedPermissions: Set<String>,
735         /** Currently installed packages */
736         val pkgs: List<LightPackageInfo>,
737         /**
738          * Background Location groups that need to be inspected by
739          * {@link #onUpgradeLockedDataLoaded}
740          */
741         val bgGroups: List<LightAppPermGroup>,
742         /** Storage groups that need to be inspected by {@link #onUpgradeLockedDataLoaded} */
743         val storageGroups: List<LightAppPermGroup>,
744         /**
745          * Background Sensors groups that need to be inspected by {@link #onUpgradeLockedDataLoaded}
746          */
747         val bgSensorsGroups: List<LightAppPermGroup>,
748     )
749 
750     /** A restricted permission of an app that should be exempted */
751     private data class RestrictionExemption(
752         /** Name of package to exempt */
753         val pkgName: String,
754         /** Name of permissions to exempt */
755         val permission: String,
756         /** Name of permissions to exempt */
757         val flags: Int = FLAG_PERMISSION_WHITELIST_UPGRADE
758     ) {
759         /**
760          * Exempt the permission by updating the platform state.
761          *
762          * @param context context to use when calling the platform
763          */
764         fun applyToPlatform(context: Context) {
765             context.packageManager.addWhitelistedRestrictedPermission(pkgName, permission, flags)
766         }
767     }
768 
769     /** A permission group of an app that should get granted */
770     private data class Grant(
771         /** Should the grant be for the foreground or background permissions */
772         private val isBackground: Boolean,
773         /** Group to be granted */
774         private val group: LightAppPermGroup,
775         /** Which of the permissions in the group should be granted */
776         private val permissions: List<String> = group.permissions.keys.toList()
777     ) {
778         /**
779          * Grant the permission by updating the platform state.
780          *
781          * @param context context to use when calling the platform
782          */
783         fun applyToPlatform(context: Context) {
784             if (isBackground) {
785                 val newGroup =
786                     grantBackgroundRuntimePermissions(context.application, group, permissions)
787 
788                 logRuntimePermissionUpgradeResult(
789                     newGroup,
790                     permissions intersect newGroup.backgroundPermNames
791                 )
792             } else {
793                 val newGroup =
794                     grantForegroundRuntimePermissions(context.application, group, permissions)
795 
796                 logRuntimePermissionUpgradeResult(
797                     newGroup,
798                     permissions intersect newGroup.foregroundPermNames
799                 )
800             }
801         }
802 
803         /**
804          * Log to the platform that permissions were granted due to an update
805          *
806          * @param permissionGroup The group that was granted
807          * @param filterPermissions Out of the group which permissions were granted
808          */
809         private fun logRuntimePermissionUpgradeResult(
810             permissionGroup: LightAppPermGroup,
811             filterPermissions: Iterable<String>
812         ) {
813             val uid = permissionGroup.packageInfo.uid
814             val packageName = permissionGroup.packageName
815             for (permName in filterPermissions) {
816                 val permission = permissionGroup.permissions[permName] ?: continue
817                 PermissionControllerStatsLog.write(
818                     RUNTIME_PERMISSIONS_UPGRADE_RESULT,
819                     permission.name,
820                     uid,
821                     packageName
822                 )
823                 Log.i(
824                     LOG_TAG,
825                     "Runtime permission upgrade logged for permissionName=" +
826                         permission.name +
827                         " uid=" +
828                         uid +
829                         " packageName=" +
830                         packageName
831                 )
832             }
833         }
834     }
835 } /* do nothing - hide constructor */
836