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