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