1 /* 2 * 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 = 9 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 exemptions for select restricted permissions of select apps. 81 * 82 * @param permissionInfos permissions to exempt 83 * @param pkgs packages to exempt 84 * 85 * @return the exemptions to apply 86 */ 87 private fun getExemptions( 88 permissions: Set<String>, 89 pkgs: List<LightPackageInfo>, 90 flags: Int = FLAG_PERMISSION_WHITELIST_UPGRADE 91 ): List<RestrictionExemption> { 92 val exemptions = mutableListOf<RestrictionExemption>() 93 94 for (pkg in pkgs) { 95 for (permission in permissions intersect pkg.requestedPermissions) { 96 exemptions.add(RestrictionExemption(pkg.packageName, permission, flags)) 97 } 98 } 99 100 return exemptions 101 } 102 103 /** 104 * You must perform all necessary mutations to bring the runtime permissions 105 * database from the old to the new version. When you add a new upgrade step 106 * you *must* update LATEST_VERSION. 107 * 108 * <p> NOTE: Relies upon the fact that the system will attempt to upgrade every version after 109 * currentVersion in order, without skipping any versions. Should this become the case, this 110 * method MUST be updated. 111 * 112 * @param context The current context 113 * @param currentVersion The current version of the permission database 114 */ 115 private suspend fun onUpgradeLocked( 116 context: Context, 117 currentVersion: Int 118 ): Int { 119 var sdkUpgradedFromP = false 120 var isNewUser = false 121 122 if (currentVersion <= -1) { 123 sdkUpgradedFromP = true 124 } else if (currentVersion == 0) { 125 isNewUser = true 126 } 127 128 val needBackgroundAppPermGroups = sdkUpgradedFromP && currentVersion <= 6 129 val needAccessMediaAppPermGroups = !isNewUser && currentVersion <= 7 130 131 // All data needed by this method. 132 // 133 // All data is loaded once and then not updated. 134 val upgradeDataProvider = object : SmartUpdateMediatorLiveData<UpgradeData>() { 135 /** Provides all preinstalled packages in the system */ 136 private val preinstalledPkgInfoProvider = 137 PreinstalledUserPackageInfosLiveData[myUserHandle()] 138 139 /** Provides all platform runtime permission infos */ 140 private val platformRuntimePermissionInfoProviders = 141 mutableListOf<LightPermInfoLiveData>() 142 143 /** {@link #platformRuntimePermissionInfoProvider} that already provided a result */ 144 private val platformRuntimePermissionInfoProvidersDone = 145 mutableSetOf<LightPermInfoLiveData>() 146 147 /** Provides all packages in the system */ 148 private val pkgInfoProvider = UserPackageInfosLiveData[myUserHandle()] 149 150 /** Provides all {@link LightAppPermGroup} this upgrade needs */ 151 private var permGroupProviders: MutableList<LightAppPermGroupLiveData>? = null 152 153 /** {@link #permGroupProviders} that already provided a result */ 154 private val permGroupProvidersDone = mutableSetOf<LightAppPermGroupLiveData>() 155 156 init { 157 // First step: Load packages + perm infos 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 update() 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 update() 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 update() 242 } 243 } 244 } 245 246 // If no group need to be loaded, directly switch to third step 247 if (permGroupProviders!!.isEmpty()) { 248 update() 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 val bgMicGroups = mutableListOf<LightAppPermGroup>() 260 261 for (group in permGroupProviders!!.mapNotNull { it.value }) { 262 when (group.permGroupName) { 263 permission_group.LOCATION -> { 264 bgGroups.add(group) 265 } 266 permission_group.STORAGE -> { 267 storageGroups.add(group) 268 } 269 permission_group.MICROPHONE -> { 270 bgMicGroups.add(group) 271 } 272 } 273 } 274 275 val restrictedPermissions = mutableSetOf<String>() 276 for (permInfoLiveDt in platformRuntimePermissionInfoProviders) { 277 val permInfo = permInfoLiveDt.value!! 278 279 if (permInfo.flags and (PermissionInfo.FLAG_HARD_RESTRICTED or 280 PermissionInfo.FLAG_SOFT_RESTRICTED) == 0) { 281 continue 282 } 283 284 restrictedPermissions.add(permInfo.name) 285 } 286 287 value = UpgradeData(preinstalledPkgInfoProvider.value!!, restrictedPermissions, 288 pkgInfoProvider.value!!, bgGroups, storageGroups, bgMicGroups) 289 } 290 } 291 } 292 293 // Trigger loading of data and wait until data is loaded 294 val upgradeData = upgradeDataProvider.getInitializedValue(forceUpdate = true) 295 296 // Only exempt permissions that are in the OTA. Apps that are updated via OTAs are never 297 // installed. Hence their permission are never exempted. This code replaces that by 298 // always exempting them. For non-OTA updates the installer should do the exemption. 299 // If a restricted permission can't be exempted by the installer then it should be filtered 300 // out here. 301 val preinstalledAppExemptions = getExemptions( 302 upgradeData.restrictedPermissions, 303 upgradeData.preinstalledPkgs) 304 305 val (newVersion, upgradeExemptions, grants) = onUpgradeLockedDataLoaded(currentVersion, 306 upgradeData.pkgs, upgradeData.restrictedPermissions, 307 upgradeData.bgGroups, upgradeData.storageGroups, upgradeData.bgMicGroups) 308 309 // Do not run in parallel. Measurements have shown that this is slower than sequential 310 for (exemption in (preinstalledAppExemptions union upgradeExemptions)) { 311 exemption.applyToPlatform(context) 312 } 313 314 for (grant in grants) { 315 grant.applyToPlatform(context) 316 } 317 318 return newVersion 319 } 320 321 private fun onUpgradeLockedDataLoaded( 322 currVersion: Int, 323 pkgs: List<LightPackageInfo>, 324 restrictedPermissions: Set<String>, 325 bgApps: List<LightAppPermGroup>, 326 accessMediaApps: List<LightAppPermGroup>, 327 bgMicApps: List<LightAppPermGroup> 328 ): Triple<Int, List<RestrictionExemption>, List<Grant>> { 329 val exemptions = mutableListOf<RestrictionExemption>() 330 val grants = mutableListOf<Grant>() 331 332 var currentVersion = currVersion 333 var sdkUpgradedFromP = false 334 var isNewUser = false 335 val bgAppsWithExemption = bgApps.map { it.packageName to it }.toMap().toMutableMap() 336 337 if (currentVersion <= -1) { 338 Log.i(LOG_TAG, "Upgrading from Android P") 339 340 sdkUpgradedFromP = true 341 342 currentVersion = 0 343 } else { 344 // If the initial version is 0 the permission state was just created 345 if (currentVersion == 0) { 346 isNewUser = true 347 } 348 } 349 350 if (currentVersion == 0) { 351 Log.i(LOG_TAG, "Grandfathering SMS and CallLog permissions") 352 353 val permissions = restrictedPermissions intersect 354 (getPlatformPermissionNamesOfGroup(permission_group.SMS) + 355 getPlatformPermissionNamesOfGroup(permission_group.CALL_LOG)) 356 357 exemptions.addAll(getExemptions(permissions, pkgs)) 358 359 currentVersion = 1 360 } 361 362 if (currentVersion == 1) { 363 // moved to step 4->5 as it has to be after the grandfathering of loc bg perms 364 currentVersion = 2 365 } 366 367 if (currentVersion == 2) { 368 // moved to step 5->6 to clean up broken permission state during dogfooding 369 currentVersion = 3 370 } 371 372 if (currentVersion == 3) { 373 Log.i(LOG_TAG, "Grandfathering location background permissions") 374 375 val bgLocExemptions = getExemptions(setOf(permission.ACCESS_BACKGROUND_LOCATION), 376 pkgs) 377 378 // Adjust bgApps as if the exemption was applied 379 for ((pkgName, _) in bgLocExemptions) { 380 val bgApp = bgAppsWithExemption[pkgName] ?: continue 381 val perm = bgApp.allPermissions[permission.ACCESS_BACKGROUND_LOCATION] ?: continue 382 383 val allPermissionsWithxemption = bgApp.allPermissions.toMutableMap() 384 allPermissionsWithxemption[permission.ACCESS_BACKGROUND_LOCATION] = 385 LightPermission(perm.pkgInfo, perm.permInfo, perm.isGrantedIncludingAppOp, 386 perm.flags or FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT, 387 perm.foregroundPerms) 388 389 bgAppsWithExemption[pkgName] = LightAppPermGroup(bgApp.packageInfo, 390 bgApp.permGroupInfo, allPermissionsWithxemption, 391 bgApp.hasInstallToRuntimeSplit, bgApp.specialLocationGrant) 392 } 393 394 exemptions.addAll(bgLocExemptions) 395 396 currentVersion = 4 397 } 398 399 if (currentVersion == 4) { 400 // moved to step 5->6 to clean up broken permission state during beta 4->5 upgrade 401 currentVersion = 5 402 } 403 404 if (currentVersion == 5) { 405 Log.i(LOG_TAG, "Grandfathering Storage permissions") 406 407 val permissions = restrictedPermissions intersect 408 getPlatformPermissionNamesOfGroup(permission_group.STORAGE) 409 410 // We don't want to allow modification of storage post install, so put it 411 // on the internal system exemptlist to prevent the installer changing it. 412 exemptions.addAll(getExemptions(permissions, pkgs)) 413 414 currentVersion = 6 415 } 416 417 if (currentVersion == 6) { 418 if (sdkUpgradedFromP) { 419 Log.i(LOG_TAG, "Expanding location permissions") 420 for (appPermGroup in bgAppsWithExemption.values) { 421 if (appPermGroup.foreground.isGranted && 422 appPermGroup.hasBackgroundGroup && 423 !appPermGroup.background.isUserSet && 424 !appPermGroup.background.isSystemFixed && 425 !appPermGroup.background.isPolicyFixed && 426 !appPermGroup.background.isUserFixed) { 427 grants.add(Grant(true, appPermGroup)) 428 } 429 } 430 } else { 431 Log.i(LOG_TAG, "Not expanding location permissions as this is not an upgrade " + 432 "from Android P") 433 } 434 435 currentVersion = 7 436 } 437 438 if (currentVersion == 7) { 439 if (!isNewUser) { 440 Log.i(LOG_TAG, "Expanding read storage to access media location") 441 442 for (appPermGroup in accessMediaApps) { 443 val perm = appPermGroup.permissions[permission.ACCESS_MEDIA_LOCATION] 444 ?: continue 445 446 if (!perm.isUserSet && !perm.isSystemFixed && !perm.isPolicyFixed && 447 !perm.isGrantedIncludingAppOp) { 448 grants.add(Grant(false, appPermGroup, 449 listOf(permission.ACCESS_MEDIA_LOCATION))) 450 } 451 } 452 } else { 453 Log.i(LOG_TAG, "Not expanding read storage to access media location as this is " + 454 "a new user") 455 } 456 457 currentVersion = 8 458 } 459 460 if (currentVersion == 8) { 461 // Removed 462 463 currentVersion = 9 464 } 465 466 // XXX: Add new upgrade steps above this point. 467 468 return Triple(currentVersion, exemptions, grants) 469 } 470 471 /** 472 * All data needed by {@link #onUpgradeLocked} 473 */ 474 private data class UpgradeData( 475 /** Preinstalled packages */ 476 val preinstalledPkgs: List<LightPackageInfo>, 477 /** Restricted permissions */ 478 val restrictedPermissions: Set<String>, 479 /** Currently installed packages */ 480 val pkgs: List<LightPackageInfo>, 481 /** 482 * Background Location groups that need to be inspected by 483 * {@link #onUpgradeLockedDataLoaded} 484 */ 485 val bgGroups: List<LightAppPermGroup>, 486 /** 487 * Storage groups that need to be inspected by {@link #onUpgradeLockedDataLoaded} 488 */ 489 val storageGroups: List<LightAppPermGroup>, 490 /** 491 * Background Microphone groups that need to be inspected by 492 * {@link #onUpgradeLockedDataLoaded} 493 */ 494 val bgMicGroups: List<LightAppPermGroup> 495 ) 496 497 /** 498 * A restricted permission of an app that should be exempted 499 */ 500 private data class RestrictionExemption( 501 /** Name of package to exempt */ 502 val pkgName: String, 503 /** Name of permissions to exempt */ 504 val permission: String, 505 /** Name of permissions to exempt */ 506 val flags: Int = FLAG_PERMISSION_WHITELIST_UPGRADE 507 ) { 508 /** 509 * Exempt the permission by updating the platform state. 510 * 511 * @param context context to use when calling the platform 512 */ 513 fun applyToPlatform(context: Context) { 514 context.packageManager.addWhitelistedRestrictedPermission(pkgName, permission, flags) 515 } 516 } 517 518 /** 519 * A permission group of an app that should get granted 520 */ 521 private data class Grant( 522 /** Should the grant be for the foreground or background permissions */ 523 private val isBackground: Boolean, 524 /** Group to be granted */ 525 private val group: LightAppPermGroup, 526 /** Which of th permissions in the group should be granted */ 527 private val permissions: List<String> = group.permissions.keys.toList() 528 ) { 529 /** 530 * Grant the permission by updating the platform state. 531 * 532 * @param context context to use when calling the platform 533 */ 534 fun applyToPlatform(context: Context) { 535 if (isBackground) { 536 val newGroup = grantBackgroundRuntimePermissions(context.application, group, 537 permissions) 538 539 logRuntimePermissionUpgradeResult(newGroup, 540 permissions intersect newGroup.backgroundPermNames) 541 } else { 542 val newGroup = grantForegroundRuntimePermissions(context.application, group, 543 permissions) 544 545 logRuntimePermissionUpgradeResult(newGroup, 546 permissions intersect newGroup.foregroundPermNames) 547 } 548 } 549 550 /** 551 * Log to the platform that permissions were granted due to an update 552 * 553 * @param permissionGroup The group that was granted 554 * @param filterPermissions Out of the group which permissions were granted 555 */ 556 private fun logRuntimePermissionUpgradeResult( 557 permissionGroup: LightAppPermGroup, 558 filterPermissions: Iterable<String> 559 ) { 560 val uid = permissionGroup.packageInfo.uid 561 val packageName = permissionGroup.packageName 562 for (permName in filterPermissions) { 563 val permission = permissionGroup.permissions[permName] ?: continue 564 PermissionControllerStatsLog.write(RUNTIME_PERMISSIONS_UPGRADE_RESULT, 565 permission.name, uid, packageName) 566 Log.v(LOG_TAG, "Runtime permission upgrade logged for permissionName=" + 567 permission.name + " uid=" + uid + " packageName=" + packageName) 568 } 569 } 570 } 571 } /* do nothing - hide constructor */ 572