• 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.PermissionControllerStatsLog
32 import com.android.permissioncontroller.PermissionControllerStatsLog.RUNTIME_PERMISSIONS_UPGRADE_RESULT
33 import com.android.permissioncontroller.permission.data.LightAppPermGroupLiveData
34 import com.android.permissioncontroller.permission.data.LightPermInfoLiveData
35 import com.android.permissioncontroller.permission.data.PreinstalledUserPackageInfosLiveData
36 import com.android.permissioncontroller.permission.data.SmartUpdateMediatorLiveData
37 import com.android.permissioncontroller.permission.data.UserPackageInfosLiveData
38 import com.android.permissioncontroller.permission.data.get
39 import com.android.permissioncontroller.permission.model.livedatatypes.LightAppPermGroup
40 import com.android.permissioncontroller.permission.model.livedatatypes.LightPackageInfo
41 import com.android.permissioncontroller.permission.model.livedatatypes.LightPermission
42 import com.android.permissioncontroller.permission.utils.IPC
43 import com.android.permissioncontroller.permission.utils.KotlinUtils.grantBackgroundRuntimePermissions
44 import com.android.permissioncontroller.permission.utils.KotlinUtils.grantForegroundRuntimePermissions
45 import com.android.permissioncontroller.permission.utils.PermissionMapping.getPlatformPermissionNamesOfGroup
46 import com.android.permissioncontroller.permission.utils.PermissionMapping.getRuntimePlatformPermissionNames
47 import com.android.permissioncontroller.permission.utils.application
48 import kotlinx.coroutines.GlobalScope
49 import kotlinx.coroutines.launch
50 
51 /**
52  * This class handles upgrading the runtime permissions database
53  */
54 internal object RuntimePermissionsUpgradeController {
55     private val LOG_TAG = RuntimePermissionsUpgradeController::class.java.simpleName
56 
57     // The latest version of the runtime permissions database
58     private val LATEST_VERSION = if (SdkLevel.isAtLeastT()) {
59         10
60     } else {
61         9
62     }
63 
64     fun upgradeIfNeeded(context: Context, onComplete: Runnable) {
65         val permissionManager = context.getSystemService(PermissionManager::class.java)
66         val storedVersion = permissionManager!!.runtimePermissionsVersion
67         val currentVersion = minOf(storedVersion, LATEST_VERSION)
68 
69         GlobalScope.launch(IPC) {
70             val upgradedVersion = onUpgradeLocked(context, currentVersion)
71             if (upgradedVersion != LATEST_VERSION) {
72                 Log.wtf("PermissionControllerService", "warning: upgrading permission database" +
73                     " to version $LATEST_VERSION left it at $currentVersion instead; this is " +
74                     "probably a bug. Did you update LATEST_VERSION?", Throwable())
75                 throw RuntimeException("db upgrade error")
76             }
77 
78             if (storedVersion != upgradedVersion) {
79                 permissionManager.runtimePermissionsVersion = LATEST_VERSION
80             }
81             onComplete.run()
82         }
83     }
84 
85     /**
86      * Create exemptions for select restricted permissions of select apps.
87      *
88      * @param permissionInfos permissions to exempt
89      * @param pkgs packages to exempt
90      *
91      * @return the exemptions to apply
92      */
93     private fun getExemptions(
94         permissions: Set<String>,
95         pkgs: List<LightPackageInfo>,
96         flags: Int = FLAG_PERMISSION_WHITELIST_UPGRADE
97     ): List<RestrictionExemption> {
98         val exemptions = mutableListOf<RestrictionExemption>()
99 
100         for (pkg in pkgs) {
101             for (permission in permissions intersect pkg.requestedPermissions) {
102                 exemptions.add(RestrictionExemption(pkg.packageName, permission, flags))
103             }
104         }
105 
106         return exemptions
107     }
108 
109     /**
110      * You must perform all necessary mutations to bring the runtime permissions
111      * database from the old to the new version. When you add a new upgrade step
112      * you *must* update LATEST_VERSION.
113      *
114      * <p> NOTE: Relies upon the fact that the system will attempt to upgrade every version after
115      * currentVersion in order, without skipping any versions. Should this become the case, this
116      * method MUST be updated.
117      *
118      * @param context The current context
119      * @param currentVersion The current version of the permission database
120      */
121     private suspend fun onUpgradeLocked(
122         context: Context,
123         currentVersion: Int
124     ): Int {
125         var sdkUpgradedFromP = false
126         var isNewUser = false
127 
128         if (currentVersion <= -1) {
129             sdkUpgradedFromP = true
130         } else if (currentVersion == 0) {
131             isNewUser = true
132         }
133 
134         val needBackgroundAppPermGroups = sdkUpgradedFromP && currentVersion <= 6
135         val needAccessMediaAppPermGroups = !isNewUser && currentVersion <= 7
136         val needGrantedExternalStorage = currentVersion <= 9 && SdkLevel.isAtLeastT()
137         val isDeviceUpgrading = context.packageManager.isDeviceUpgrading
138 
139         // All data needed by this method.
140         //
141         // All data is loaded once and then not updated.
142         val upgradeDataProvider = object : SmartUpdateMediatorLiveData<UpgradeData>() {
143             /** Provides all preinstalled packages in the system */
144             private val preinstalledPkgInfoProvider =
145                     PreinstalledUserPackageInfosLiveData[myUserHandle()]
146 
147             /** Provides all platform runtime permission infos */
148             private val platformRuntimePermissionInfoProviders =
149                     mutableListOf<LightPermInfoLiveData>()
150 
151             /** {@link #platformRuntimePermissionInfoProvider} that already provided a result */
152             private val platformRuntimePermissionInfoProvidersDone =
153                     mutableSetOf<LightPermInfoLiveData>()
154 
155             /** Provides all packages in the system */
156             private val pkgInfoProvider = UserPackageInfosLiveData[myUserHandle()]
157 
158             /** Provides all {@link LightAppPermGroup} this upgrade needs */
159             private var permGroupProviders: MutableList<LightAppPermGroupLiveData>? = null
160 
161             /** {@link #permGroupProviders} that already provided a result */
162             private val permGroupProvidersDone = mutableSetOf<LightAppPermGroupLiveData>()
163 
164             init {
165                 // First step: Load packages + perm infos
166                 // TODO ntmyren: remove once b/154796729 is fixed
167                 Log.i("RuntimePermissions", "observing UserPackageInfoLiveData for " +
168                     "${myUserHandle().identifier} in RuntimePermissionsUpgradeController")
169                 addSource(pkgInfoProvider) { pkgInfos ->
170                     if (pkgInfos != null) {
171                         removeSource(pkgInfoProvider)
172 
173                         // TODO ntmyren: remove once b/154796729 is fixed
174                         Log.i("RuntimePermissions", "observing " +
175                             "PreinstalledUserPackageInfoLiveData for ${myUserHandle().identifier}" +
176                             " in RuntimePermissionsUpgradeController")
177                         addSource(preinstalledPkgInfoProvider) { preinstalledPkgInfos ->
178                             if (preinstalledPkgInfos != null) {
179                                 removeSource(preinstalledPkgInfoProvider)
180 
181                                 update()
182                             }
183                         }
184                     }
185                 }
186 
187                 for (platformRuntimePermission in getRuntimePlatformPermissionNames()) {
188                     val permProvider = LightPermInfoLiveData[platformRuntimePermission]
189                     platformRuntimePermissionInfoProviders.add(permProvider)
190 
191                     addSource(permProvider) { permInfo ->
192                         if (permInfo != null) {
193                             platformRuntimePermissionInfoProvidersDone.add(permProvider)
194                             removeSource(permProvider)
195 
196                             update()
197                         }
198                     }
199                 }
200             }
201 
202             override fun onUpdate() {
203                 if (permGroupProviders == null && pkgInfoProvider.value != null) {
204                     // Second step: Trigger load of app-perm-groups
205 
206                     permGroupProviders = mutableListOf()
207 
208                     // Only load app-perm-groups needed for this upgrade
209                     if (needBackgroundAppPermGroups || needAccessMediaAppPermGroups ||
210                         needGrantedExternalStorage) {
211                         for ((pkgName, _, requestedPerms, requestedPermFlags) in
212                                 pkgInfoProvider.value!!) {
213                             var requestsAccessMediaLocation = false
214                             var hasGrantedExternalStorage = false
215 
216                             for ((perm, flags) in requestedPerms.zip(requestedPermFlags)) {
217                                 if (needBackgroundAppPermGroups &&
218                                         perm == permission.ACCESS_BACKGROUND_LOCATION) {
219                                     permGroupProviders!!.add(LightAppPermGroupLiveData[pkgName,
220                                             permission_group.LOCATION, myUserHandle()])
221                                 }
222 
223                                 if (needAccessMediaAppPermGroups || needGrantedExternalStorage) {
224                                     if (needAccessMediaAppPermGroups &&
225                                         perm == permission.ACCESS_MEDIA_LOCATION) {
226                                         requestsAccessMediaLocation = true
227                                     }
228 
229                                     if (perm == permission.READ_EXTERNAL_STORAGE &&
230                                             flags and PackageInfo.REQUESTED_PERMISSION_GRANTED
231                                             != 0) {
232                                         hasGrantedExternalStorage = true
233                                     }
234                                 }
235                             }
236 
237                             val accessMediaLocationPermGroup =
238                                 if (SdkLevel.isAtLeastT())
239                                     permission_group.READ_MEDIA_VISUAL
240                                 else
241                                     permission_group.STORAGE
242 
243                             if (hasGrantedExternalStorage) {
244                                 if (needGrantedExternalStorage) {
245                                     permGroupProviders!!.add(LightAppPermGroupLiveData[pkgName,
246                                             permission_group.STORAGE, myUserHandle()])
247                                     if (SdkLevel.isAtLeastT()) {
248                                         permGroupProviders!!.add(LightAppPermGroupLiveData[pkgName,
249                                                 permission_group.READ_MEDIA_VISUAL, myUserHandle()])
250                                         permGroupProviders!!.add(LightAppPermGroupLiveData[pkgName,
251                                                 permission_group.READ_MEDIA_AURAL, myUserHandle()])
252                                     }
253                                 } else if (requestsAccessMediaLocation) {
254                                     permGroupProviders!!.add(LightAppPermGroupLiveData[pkgName,
255                                             accessMediaLocationPermGroup, myUserHandle()])
256                                 }
257                             }
258                         }
259                     }
260 
261                     // Wait until groups are loaded and then trigger third step
262                     for (permGroupProvider in permGroupProviders!!) {
263                         addSource(permGroupProvider) { group ->
264                             if (group != null) {
265                                 permGroupProvidersDone.add(permGroupProvider)
266                                 removeSource(permGroupProvider)
267 
268                                 update()
269                             }
270                         }
271                     }
272 
273                     // If no group need to be loaded, directly switch to third step
274                     if (permGroupProviders!!.isEmpty()) {
275                         update()
276                     }
277                 } else if (permGroupProviders != null &&
278                         permGroupProvidersDone.size == permGroupProviders!!.size &&
279                         preinstalledPkgInfoProvider.value != null &&
280                         platformRuntimePermissionInfoProviders.size
281                         == platformRuntimePermissionInfoProvidersDone.size) {
282                     // Third step: All packages, perm infos and perm groups are loaded, set value
283 
284                     val bgGroups = mutableListOf<LightAppPermGroup>()
285                     val storageGroups = mutableListOf<LightAppPermGroup>()
286 
287                     for (group in permGroupProviders!!.mapNotNull { it.value }) {
288                         when (group.permGroupName) {
289                             permission_group.LOCATION -> {
290                                 bgGroups.add(group)
291                             }
292                             permission_group.STORAGE -> {
293                                 storageGroups.add(group)
294                             }
295                             permission_group.READ_MEDIA_AURAL -> {
296                                 storageGroups.add(group)
297                             }
298                             permission_group.READ_MEDIA_VISUAL -> {
299                                 storageGroups.add(group)
300                             }
301                         }
302                     }
303 
304                     val restrictedPermissions = mutableSetOf<String>()
305                     for (permInfoLiveDt in platformRuntimePermissionInfoProviders) {
306                         val permInfo = permInfoLiveDt.value!!
307 
308                         if (permInfo.flags and (PermissionInfo.FLAG_HARD_RESTRICTED or
309                                         PermissionInfo.FLAG_SOFT_RESTRICTED) == 0) {
310                             continue
311                         }
312 
313                         restrictedPermissions.add(permInfo.name)
314                     }
315 
316                     value = UpgradeData(preinstalledPkgInfoProvider.value!!, restrictedPermissions,
317                             pkgInfoProvider.value!!, bgGroups, storageGroups)
318                 }
319             }
320         }
321 
322         // Trigger loading of data and wait until data is loaded
323         val upgradeData = upgradeDataProvider.getInitializedValue(forceUpdate = true)
324 
325         // Only exempt permissions that are in the OTA. Apps that are updated via OTAs are never
326         // installed. Hence their permission are never exempted. This code replaces that by
327         // always exempting them. For non-OTA updates the installer should do the exemption.
328         // If a restricted permission can't be exempted by the installer then it should be filtered
329         // out here.
330         val preinstalledAppExemptions = getExemptions(
331                 upgradeData.restrictedPermissions,
332                 upgradeData.preinstalledPkgs)
333 
334         val (newVersion, upgradeExemptions, grants) = onUpgradeLockedDataLoaded(currentVersion,
335                 upgradeData.pkgs, upgradeData.restrictedPermissions,
336                 upgradeData.bgGroups, upgradeData.storageGroups,
337                 isDeviceUpgrading)
338 
339         // Do not run in parallel. Measurements have shown that this is slower than sequential
340         for (exemption in (preinstalledAppExemptions union upgradeExemptions)) {
341             exemption.applyToPlatform(context)
342         }
343 
344         for (grant in grants) {
345             grant.applyToPlatform(context)
346         }
347 
348         return newVersion
349     }
350 
351     private fun onUpgradeLockedDataLoaded(
352         currVersion: Int,
353         pkgs: List<LightPackageInfo>,
354         restrictedPermissions: Set<String>,
355         bgApps: List<LightAppPermGroup>,
356         storageAndMediaAppPermGroups: List<LightAppPermGroup>,
357         isDeviceUpgrading: Boolean
358     ): Triple<Int, List<RestrictionExemption>, List<Grant>> {
359         val exemptions = mutableListOf<RestrictionExemption>()
360         val grants = mutableListOf<Grant>()
361 
362         var currentVersion = currVersion
363         var sdkUpgradedFromP = false
364         var isNewUser = false
365         val bgAppsWithExemption = bgApps.map { it.packageName to it }.toMap().toMutableMap()
366 
367         if (currentVersion <= -1) {
368             Log.i(LOG_TAG, "Upgrading from Android P")
369 
370             sdkUpgradedFromP = true
371 
372             currentVersion = 0
373         } else {
374             // If the initial version is 0 the permission state was just created
375             if (currentVersion == 0) {
376                 isNewUser = true
377             }
378         }
379 
380         if (currentVersion == 0) {
381             Log.i(LOG_TAG, "Grandfathering SMS and CallLog permissions")
382 
383             val permissions = restrictedPermissions intersect
384                     (getPlatformPermissionNamesOfGroup(permission_group.SMS) +
385                     getPlatformPermissionNamesOfGroup(permission_group.CALL_LOG))
386 
387             exemptions.addAll(getExemptions(permissions, pkgs))
388 
389             currentVersion = 1
390         }
391 
392         if (currentVersion == 1) {
393             // moved to step 4->5 as it has to be after the grandfathering of loc bg perms
394             currentVersion = 2
395         }
396 
397         if (currentVersion == 2) {
398             // moved to step 5->6 to clean up broken permission state during dogfooding
399             currentVersion = 3
400         }
401 
402         if (currentVersion == 3) {
403             Log.i(LOG_TAG, "Grandfathering location background permissions")
404 
405             val bgLocExemptions = getExemptions(setOf(permission.ACCESS_BACKGROUND_LOCATION),
406                     pkgs)
407 
408             // Adjust bgApps as if the exemption was applied
409             for ((pkgName, _) in bgLocExemptions) {
410                 val bgApp = bgAppsWithExemption[pkgName] ?: continue
411                 val perm = bgApp.allPermissions[permission.ACCESS_BACKGROUND_LOCATION] ?: continue
412 
413                 val allPermissionsWithxemption = bgApp.allPermissions.toMutableMap()
414                 allPermissionsWithxemption[permission.ACCESS_BACKGROUND_LOCATION] =
415                         LightPermission(perm.pkgInfo, perm.permInfo, perm.isGrantedIncludingAppOp,
416                         perm.flags or FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT,
417                         perm.foregroundPerms)
418 
419                 bgAppsWithExemption[pkgName] = LightAppPermGroup(bgApp.packageInfo,
420                         bgApp.permGroupInfo, allPermissionsWithxemption,
421                         bgApp.hasInstallToRuntimeSplit, bgApp.specialLocationGrant)
422             }
423 
424             exemptions.addAll(bgLocExemptions)
425 
426             currentVersion = 4
427         }
428 
429         if (currentVersion == 4) {
430             // moved to step 5->6 to clean up broken permission state during beta 4->5 upgrade
431             currentVersion = 5
432         }
433 
434         if (currentVersion == 5) {
435             Log.i(LOG_TAG, "Grandfathering Storage permissions")
436 
437             val permissions = restrictedPermissions intersect
438                     getPlatformPermissionNamesOfGroup(permission_group.STORAGE)
439 
440             // We don't want to allow modification of storage post install, so put it
441             // on the internal system exemptlist to prevent the installer changing it.
442             exemptions.addAll(getExemptions(permissions, pkgs))
443 
444             currentVersion = 6
445         }
446 
447         if (currentVersion == 6) {
448             if (sdkUpgradedFromP) {
449                 Log.i(LOG_TAG, "Expanding location permissions")
450                 for (appPermGroup in bgAppsWithExemption.values) {
451                     if (appPermGroup.foreground.isGranted &&
452                         appPermGroup.hasBackgroundGroup &&
453                         !appPermGroup.background.isUserSet &&
454                         !appPermGroup.background.isSystemFixed &&
455                         !appPermGroup.background.isPolicyFixed &&
456                         !appPermGroup.background.isUserFixed) {
457                         grants.add(Grant(true, appPermGroup))
458                     }
459                 }
460             } else {
461                 Log.i(LOG_TAG, "Not expanding location permissions as this is not an upgrade " +
462                     "from Android P")
463             }
464 
465             currentVersion = 7
466         }
467 
468         if (currentVersion == 7) {
469             if (!isNewUser) {
470                 Log.i(LOG_TAG, "Expanding read storage to access media location")
471 
472                 for (appPermGroup in storageAndMediaAppPermGroups) {
473                     val perm = appPermGroup.permissions[permission.ACCESS_MEDIA_LOCATION]
474                             ?: continue
475 
476                     if (!perm.isUserSet && !perm.isSystemFixed && !perm.isPolicyFixed &&
477                             !perm.isGrantedIncludingAppOp) {
478                         grants.add(Grant(false, appPermGroup,
479                                 listOf(permission.ACCESS_MEDIA_LOCATION)))
480                     }
481                 }
482             } else {
483                 Log.i(LOG_TAG, "Not expanding read storage to access media location as this is " +
484                         "a new user")
485             }
486 
487             currentVersion = 8
488         }
489 
490         if (currentVersion == 8) {
491             // Removed
492 
493             currentVersion = 9
494         }
495 
496         if (currentVersion == 9 && SdkLevel.isAtLeastT()) {
497             if (isNewUser) {
498                 Log.i(LOG_TAG, "Not migrating STORAGE permissions to READ_MEDIA permissions as" +
499                     " this is a new user")
500             } else if (!isDeviceUpgrading) {
501                 Log.i(LOG_TAG, "Not migrating STORAGE permissions to READ_MEDIA permissions as" +
502                     " this device is not performing an upgrade")
503             } else {
504                 Log.i(LOG_TAG, "Migrating STORAGE permissions to READ_MEDIA permissions")
505 
506                 // Upon upgrading to platform 33, for all targetSdk>=33 apps, do the following:
507                 // If STORAGE is granted, and the user has not set READ_MEDIA_AURAL or
508                 // READ_MEDIA_VISUAL, grant READ_MEDIA_AURAL and READ_MEDIA_VISUAL
509                 val storageAppPermGroups = storageAndMediaAppPermGroups.filter {
510                     it.packageInfo.targetSdkVersion >= Build.VERSION_CODES.TIRAMISU &&
511                         it.permGroupInfo.name == permission_group.STORAGE &&
512                         it.isGranted && it.isUserSet
513                 }
514                 for (storageAppPermGroup in storageAppPermGroups) {
515                     val pkgName = storageAppPermGroup.packageInfo.packageName
516                     val auralAppPermGroup = storageAndMediaAppPermGroups.firstOrNull {
517                         it.packageInfo.packageName == pkgName &&
518                             it.permGroupInfo.name == permission_group.READ_MEDIA_AURAL &&
519                             !it.isUserSet && !it.isUserFixed
520                     }
521                     val visualAppPermGroup = storageAndMediaAppPermGroups.firstOrNull {
522                         it.packageInfo.packageName == pkgName &&
523                             it.permGroupInfo.name == permission_group.READ_MEDIA_VISUAL &&
524                             !it.permissions.filter { it.key != permission.ACCESS_MEDIA_LOCATION }
525                                 .any { it.value.isUserSet || it.value.isUserFixed }
526                     }
527 
528                     if (auralAppPermGroup != null) {
529                         grants.add(Grant(false, auralAppPermGroup))
530                     }
531                     if (visualAppPermGroup != null) {
532                         grants.add(Grant(false, visualAppPermGroup))
533                     }
534                 }
535             }
536             currentVersion = 10
537         }
538 
539         // XXX: Add new upgrade steps above this point.
540 
541         return Triple(currentVersion, exemptions, grants)
542     }
543 
544     /**
545      * All data needed by {@link #onUpgradeLocked}
546      */
547     private data class UpgradeData(
548         /** Preinstalled packages */
549         val preinstalledPkgs: List<LightPackageInfo>,
550         /** Restricted permissions */
551         val restrictedPermissions: Set<String>,
552         /** Currently installed packages */
553         val pkgs: List<LightPackageInfo>,
554         /**
555          * Background Location groups that need to be inspected by
556          * {@link #onUpgradeLockedDataLoaded}
557          */
558         val bgGroups: List<LightAppPermGroup>,
559         /**
560          * Storage groups that need to be inspected by {@link #onUpgradeLockedDataLoaded}
561          */
562         val storageGroups: List<LightAppPermGroup>,
563     )
564 
565     /**
566      * A restricted permission of an app that should be exempted
567      */
568     private data class RestrictionExemption(
569         /** Name of package to exempt */
570         val pkgName: String,
571         /** Name of permissions to exempt */
572         val permission: String,
573         /** Name of permissions to exempt */
574         val flags: Int = FLAG_PERMISSION_WHITELIST_UPGRADE
575     ) {
576         /**
577          * Exempt the permission by updating the platform state.
578          *
579          * @param context context to use when calling the platform
580          */
581         fun applyToPlatform(context: Context) {
582             context.packageManager.addWhitelistedRestrictedPermission(pkgName, permission, flags)
583         }
584     }
585 
586     /**
587      * A permission group of an app that should get granted
588      */
589     private data class Grant(
590         /** Should the grant be for the foreground or background permissions */
591         private val isBackground: Boolean,
592         /** Group to be granted */
593         private val group: LightAppPermGroup,
594         /** Which of th permissions in the group should be granted */
595         private val permissions: List<String> = group.permissions.keys.toList()
596     ) {
597         /**
598          * Grant the permission by updating the platform state.
599          *
600          * @param context context to use when calling the platform
601          */
602         fun applyToPlatform(context: Context) {
603             if (isBackground) {
604                 val newGroup = grantBackgroundRuntimePermissions(context.application, group,
605                         permissions)
606 
607                 logRuntimePermissionUpgradeResult(newGroup,
608                         permissions intersect newGroup.backgroundPermNames)
609             } else {
610                 val newGroup = grantForegroundRuntimePermissions(context.application, group,
611                         permissions)
612 
613                 logRuntimePermissionUpgradeResult(newGroup,
614                         permissions intersect newGroup.foregroundPermNames)
615             }
616         }
617 
618         /**
619          * Log to the platform that permissions were granted due to an update
620          *
621          * @param permissionGroup The group that was granted
622          * @param filterPermissions Out of the group which permissions were granted
623          */
624         private fun logRuntimePermissionUpgradeResult(
625             permissionGroup: LightAppPermGroup,
626             filterPermissions: Iterable<String>
627         ) {
628             val uid = permissionGroup.packageInfo.uid
629             val packageName = permissionGroup.packageName
630             for (permName in filterPermissions) {
631                 val permission = permissionGroup.permissions[permName] ?: continue
632                 PermissionControllerStatsLog.write(RUNTIME_PERMISSIONS_UPGRADE_RESULT,
633                         permission.name, uid, packageName)
634                 Log.v(LOG_TAG, "Runtime permission upgrade logged for permissionName=" +
635                         permission.name + " uid=" + uid + " packageName=" + packageName)
636             }
637         }
638     }
639 } /* do nothing - hide constructor */
640