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.isGrantedIncludingAppOp,
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                     )
508             }
509 
510             exemptions.addAll(bgLocExemptions)
511 
512             currentVersion = 4
513         }
514 
515         if (currentVersion == 4) {
516             // moved to step 5->6 to clean up broken permission state during beta 4->5 upgrade
517             currentVersion = 5
518         }
519 
520         if (currentVersion == 5) {
521             Log.i(LOG_TAG, "Grandfathering Storage permissions")
522 
523             val permissions =
524                 restrictedPermissions intersect
525                     getPlatformPermissionNamesOfGroup(permission_group.STORAGE)
526 
527             // We don't want to allow modification of storage post install, so put it
528             // on the internal system exemptlist to prevent the installer changing it.
529             exemptions.addAll(getExemptions(permissions, pkgs))
530 
531             currentVersion = 6
532         }
533 
534         if (currentVersion == 6) {
535             if (sdkUpgradedFromP) {
536                 Log.i(LOG_TAG, "Expanding location permissions")
537                 for (appPermGroup in bgAppsWithExemption.values) {
538                     if (
539                         appPermGroup.foreground.isGranted &&
540                             appPermGroup.hasBackgroundGroup &&
541                             !appPermGroup.background.isUserSet &&
542                             !appPermGroup.background.isSystemFixed &&
543                             !appPermGroup.background.isPolicyFixed &&
544                             !appPermGroup.background.isUserFixed
545                     ) {
546                         grants.add(Grant(true, appPermGroup))
547                     }
548                 }
549             } else {
550                 Log.i(
551                     LOG_TAG,
552                     "Not expanding location permissions as this is not an upgrade " +
553                         "from Android P"
554                 )
555             }
556 
557             currentVersion = 7
558         }
559 
560         if (currentVersion == 7) {
561             if (!isNewUser) {
562                 Log.i(LOG_TAG, "Expanding read storage to access media location")
563 
564                 for (appPermGroup in storageAndMediaAppPermGroups) {
565                     val perm =
566                         appPermGroup.permissions[permission.ACCESS_MEDIA_LOCATION] ?: continue
567 
568                     if (
569                         !perm.isUserSet &&
570                             !perm.isSystemFixed &&
571                             !perm.isPolicyFixed &&
572                             !perm.isGrantedIncludingAppOp
573                     ) {
574                         grants.add(
575                             Grant(false, appPermGroup, listOf(permission.ACCESS_MEDIA_LOCATION))
576                         )
577                     }
578                 }
579             } else {
580                 Log.i(
581                     LOG_TAG,
582                     "Not expanding read storage to access media location as this is " + "a new user"
583                 )
584             }
585 
586             currentVersion = 8
587         }
588 
589         if (currentVersion == 8) {
590             // Removed
591 
592             currentVersion = 9
593         }
594 
595         if (currentVersion == 9 && SdkLevel.isAtLeastT()) {
596             if (isNewUser) {
597                 Log.i(
598                     LOG_TAG,
599                     "Not migrating STORAGE and BODY_SENSORS permissions as this is a new user"
600                 )
601             } else if (!isDeviceUpgrading) {
602                 Log.i(
603                     LOG_TAG,
604                     "Not migrating STORAGE and BODY_SENSORS permissions as" +
605                         " this device is not performing an upgrade"
606                 )
607             } else {
608                 Log.i(LOG_TAG, "Migrating STORAGE permissions to READ_MEDIA permissions")
609 
610                 // Upon upgrading to platform 33, for all targetSdk>=33 apps, do the following:
611                 // If STORAGE is granted, and the user has not set READ_MEDIA_AURAL or
612                 // READ_MEDIA_VISUAL, grant READ_MEDIA_AURAL and READ_MEDIA_VISUAL
613                 val storageAppPermGroups =
614                     storageAndMediaAppPermGroups.filter {
615                         it.packageInfo.targetSdkVersion >= Build.VERSION_CODES.TIRAMISU &&
616                             it.permGroupInfo.name == permission_group.STORAGE &&
617                             it.isGranted &&
618                             it.isUserSet
619                     }
620                 for (storageAppPermGroup in storageAppPermGroups) {
621                     val pkgName = storageAppPermGroup.packageInfo.packageName
622                     val auralAppPermGroup =
623                         storageAndMediaAppPermGroups.firstOrNull {
624                             it.packageInfo.packageName == pkgName &&
625                                 it.permGroupInfo.name == permission_group.READ_MEDIA_AURAL &&
626                                 !it.isUserSet &&
627                                 !it.isUserFixed
628                         }
629                     val visualAppPermGroup =
630                         storageAndMediaAppPermGroups.firstOrNull {
631                             it.packageInfo.packageName == pkgName &&
632                                 it.permGroupInfo.name == permission_group.READ_MEDIA_VISUAL &&
633                                 !it.permissions
634                                     .filter { it.key != permission.ACCESS_MEDIA_LOCATION }
635                                     .any { it.value.isUserSet || it.value.isUserFixed }
636                         }
637 
638                     if (auralAppPermGroup != null) {
639                         grants.add(Grant(false, auralAppPermGroup))
640                     }
641                     if (visualAppPermGroup != null) {
642                         grants.add(Grant(false, visualAppPermGroup))
643                     }
644                 }
645 
646                 // Granting body sensors background permission to apps that had the pre-split body
647                 // sensors permission granted.
648                 Log.i(LOG_TAG, "Grandfathering body sensors background permissions")
649 
650                 for (bgSensorsGroup in bgSensorsGroups) {
651                     val perm =
652                         bgSensorsGroup.allPermissions[permission.BODY_SENSORS_BACKGROUND]
653                             ?: continue
654                     if (perm.flags and FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT != 0) {
655                         continue
656                     }
657 
658                     // Exempt the background permission to allow setting from users.
659                     val pkgName = bgSensorsGroup.packageName
660                     exemptions.add(
661                         RestrictionExemption(
662                             pkgName,
663                             permission.BODY_SENSORS_BACKGROUND,
664                             FLAG_PERMISSION_WHITELIST_UPGRADE
665                         )
666                     )
667 
668                     val allPermissionsWithExemption = bgSensorsGroup.allPermissions.toMutableMap()
669                     allPermissionsWithExemption[permission.BODY_SENSORS_BACKGROUND] =
670                         LightPermission(
671                             perm.pkgInfo,
672                             perm.permInfo,
673                             perm.isGrantedIncludingAppOp,
674                             perm.flags or FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT,
675                             perm.foregroundPerms
676                         )
677                     val group =
678                         LightAppPermGroup(
679                             bgSensorsGroup.packageInfo,
680                             bgSensorsGroup.permGroupInfo,
681                             allPermissionsWithExemption,
682                             bgSensorsGroup.hasInstallToRuntimeSplit,
683                             bgSensorsGroup.specialLocationGrant
684                         )
685 
686                     // Grant the background permission only if foreground permission is granted.
687                     if (group.foreground.isGranted) {
688                         if (DEBUG) {
689                             Log.i(LOG_TAG, "$pkgName Granted body sensors background permissions")
690                         }
691                         grants.add(Grant(isBackground = true, group = group))
692                     }
693                 }
694             }
695             currentVersion = 10
696         }
697 
698         if (currentVersion == 10 && SdkLevel.isAtLeastU()) {
699             // On U, if the app is granted READ_MEDIA_VISUAL, expand the grant to
700             // READ_MEDIA_VISUAL_USER_SELECTED
701             if (isDeviceUpgrading && !isNewUser) {
702                 Log.i(
703                     LOG_TAG,
704                     "Grandfathering READ_MEDIA_VISUAL_USER_SELECTED to apps already " +
705                         "granted visual permissions"
706                 )
707                 val visualAppPermGroups =
708                     storageAndMediaAppPermGroups.filter {
709                         it.packageInfo.targetSdkVersion >= Build.VERSION_CODES.TIRAMISU &&
710                             it.permGroupInfo.name == permission_group.READ_MEDIA_VISUAL &&
711                             it.isGranted &&
712                             it.isUserSet
713                     }
714                 visualAppPermGroups.forEach { grants.add(Grant(false, it)) }
715             }
716             currentVersion = 11
717         }
718 
719         // XXX: Add new upgrade steps above this point.
720 
721         return Triple(currentVersion, exemptions, grants)
722     }
723 
724     /** All data needed by {@link #onUpgradeLocked} */
725     private data class UpgradeData(
726         /** Preinstalled packages */
727         val preinstalledPkgs: List<LightPackageInfo>,
728         /** Restricted permissions */
729         val restrictedPermissions: Set<String>,
730         /** Currently installed packages */
731         val pkgs: List<LightPackageInfo>,
732         /**
733          * Background Location groups that need to be inspected by
734          * {@link #onUpgradeLockedDataLoaded}
735          */
736         val bgGroups: List<LightAppPermGroup>,
737         /** Storage groups that need to be inspected by {@link #onUpgradeLockedDataLoaded} */
738         val storageGroups: List<LightAppPermGroup>,
739         /**
740          * Background Sensors groups that need to be inspected by {@link #onUpgradeLockedDataLoaded}
741          */
742         val bgSensorsGroups: List<LightAppPermGroup>,
743     )
744 
745     /** A restricted permission of an app that should be exempted */
746     private data class RestrictionExemption(
747         /** Name of package to exempt */
748         val pkgName: String,
749         /** Name of permissions to exempt */
750         val permission: String,
751         /** Name of permissions to exempt */
752         val flags: Int = FLAG_PERMISSION_WHITELIST_UPGRADE
753     ) {
754         /**
755          * Exempt the permission by updating the platform state.
756          *
757          * @param context context to use when calling the platform
758          */
759         fun applyToPlatform(context: Context) {
760             context.packageManager.addWhitelistedRestrictedPermission(pkgName, permission, flags)
761         }
762     }
763 
764     /** A permission group of an app that should get granted */
765     private data class Grant(
766         /** Should the grant be for the foreground or background permissions */
767         private val isBackground: Boolean,
768         /** Group to be granted */
769         private val group: LightAppPermGroup,
770         /** Which of the permissions in the group should be granted */
771         private val permissions: List<String> = group.permissions.keys.toList()
772     ) {
773         /**
774          * Grant the permission by updating the platform state.
775          *
776          * @param context context to use when calling the platform
777          */
778         fun applyToPlatform(context: Context) {
779             if (isBackground) {
780                 val newGroup =
781                     grantBackgroundRuntimePermissions(context.application, group, permissions)
782 
783                 logRuntimePermissionUpgradeResult(
784                     newGroup,
785                     permissions intersect newGroup.backgroundPermNames
786                 )
787             } else {
788                 val newGroup =
789                     grantForegroundRuntimePermissions(context.application, group, permissions)
790 
791                 logRuntimePermissionUpgradeResult(
792                     newGroup,
793                     permissions intersect newGroup.foregroundPermNames
794                 )
795             }
796         }
797 
798         /**
799          * Log to the platform that permissions were granted due to an update
800          *
801          * @param permissionGroup The group that was granted
802          * @param filterPermissions Out of the group which permissions were granted
803          */
804         private fun logRuntimePermissionUpgradeResult(
805             permissionGroup: LightAppPermGroup,
806             filterPermissions: Iterable<String>
807         ) {
808             val uid = permissionGroup.packageInfo.uid
809             val packageName = permissionGroup.packageName
810             for (permName in filterPermissions) {
811                 val permission = permissionGroup.permissions[permName] ?: continue
812                 PermissionControllerStatsLog.write(
813                     RUNTIME_PERMISSIONS_UPGRADE_RESULT,
814                     permission.name,
815                     uid,
816                     packageName
817                 )
818                 Log.i(
819                     LOG_TAG,
820                     "Runtime permission upgrade logged for permissionName=" +
821                         permission.name +
822                         " uid=" +
823                         uid +
824                         " packageName=" +
825                         packageName
826                 )
827             }
828         }
829     }
830 } /* do nothing - hide constructor */
831