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