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