1 /* <lambda>null2 * Copyright (C) 2020 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.ui.model 18 19 import android.Manifest 20 import android.Manifest.permission.ACCESS_COARSE_LOCATION 21 import android.Manifest.permission.ACCESS_FINE_LOCATION 22 import android.Manifest.permission_group.LOCATION 23 import android.app.Activity 24 import android.app.Application 25 import android.app.admin.DevicePolicyManager 26 import android.content.Intent 27 import android.content.pm.PackageManager 28 import android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED 29 import android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED 30 import android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET 31 import android.os.Build 32 import android.os.Bundle 33 import android.os.Process 34 import android.permission.PermissionManager 35 import android.util.Log 36 import androidx.core.util.Consumer 37 import androidx.lifecycle.ViewModel 38 import androidx.lifecycle.ViewModelProvider 39 import com.android.permissioncontroller.Constants 40 import com.android.permissioncontroller.PermissionControllerStatsLog 41 import com.android.permissioncontroller.PermissionControllerStatsLog.GRANT_PERMISSIONS_ACTIVITY_BUTTON_ACTIONS 42 import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_DENIED 43 import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_GRANTED 44 import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED 45 import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_POLICY_FIXED 46 import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_RESTRICTED_PERMISSION 47 import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_USER_FIXED 48 import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED 49 import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_IN_SETTINGS 50 import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_WITH_PREJUDICE 51 import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_WITH_PREJUDICE_IN_SETTINGS 52 import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED 53 import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED_IN_SETTINGS 54 import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED_ONE_TIME 55 import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_IGNORED 56 import com.android.permissioncontroller.permission.data.LightAppPermGroupLiveData 57 import com.android.permissioncontroller.permission.data.LightPackageInfoLiveData 58 import com.android.permissioncontroller.permission.data.PackagePermissionsLiveData 59 import com.android.permissioncontroller.permission.data.SmartUpdateMediatorLiveData 60 import com.android.permissioncontroller.permission.data.get 61 import com.android.permissioncontroller.permission.model.livedatatypes.LightAppPermGroup 62 import com.android.permissioncontroller.permission.model.livedatatypes.LightPackageInfo 63 import com.android.permissioncontroller.permission.model.livedatatypes.LightPermGroupInfo 64 import com.android.permissioncontroller.permission.ui.AutoGrantPermissionsNotifier 65 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity 66 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_BUTTON 67 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_FOREGROUND_BUTTON 68 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_ONE_TIME_BUTTON 69 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.COARSE_RADIO_BUTTON 70 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DENY_AND_DONT_ASK_AGAIN_BUTTON 71 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DENY_BUTTON 72 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DIALOG_WITH_BOTH_LOCATIONS 73 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DIALOG_WITH_COARSE_LOCATION_ONLY 74 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DIALOG_WITH_FINE_LOCATION_ONLY 75 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.FINE_RADIO_BUTTON 76 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.LINK_TO_SETTINGS 77 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.LOCATION_ACCURACY_LAYOUT 78 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NEXT_BUTTON 79 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NEXT_LOCATION_DIALOG 80 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON 81 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NO_UPGRADE_BUTTON 82 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NO_UPGRADE_OT_AND_DONT_ASK_AGAIN_BUTTON 83 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NO_UPGRADE_OT_BUTTON 84 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.PERMISSION_TO_BIT_SHIFT 85 import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler 86 import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.DENIED 87 import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.DENIED_DO_NOT_ASK_AGAIN 88 import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.GRANTED_ALWAYS 89 import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.GRANTED_FOREGROUND_ONLY 90 import com.android.permissioncontroller.permission.ui.handheld.dashboard.getDefaultPrecision 91 import com.android.permissioncontroller.permission.ui.handheld.dashboard.isLocationAccuracyEnabled 92 import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity 93 import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_RESULT_PERMISSION_INTERACTED 94 import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_RESULT_PERMISSION_RESULT 95 import com.android.permissioncontroller.permission.utils.AdminRestrictedPermissionsUtils 96 import com.android.permissioncontroller.permission.utils.KotlinUtils 97 import com.android.permissioncontroller.permission.utils.SafetyNetLogger 98 import com.android.permissioncontroller.permission.utils.Utils 99 import kotlinx.coroutines.Dispatchers.Main 100 import kotlinx.coroutines.GlobalScope 101 import kotlinx.coroutines.launch 102 103 /** 104 * ViewModel for the GrantPermissionsActivity. Tracks all permission groups that are affected by 105 * the permissions requested by the user, and generates a RequestInfo object for each group, if 106 * action is needed. It will not return any data if one of the requests is malformed. 107 * 108 * @param app: The current application 109 * @param packageName: The packageName permissions are being requested for 110 * @param requestedPermissions: The list of permissions requested 111 * @param sessionId: A long to identify this session 112 * @param storedState: Previous state, if this activity was stopped and is being recreated 113 */ 114 class GrantPermissionsViewModel( 115 private val app: Application, 116 private val packageName: String, 117 private val requestedPermissions: List<String>, 118 private val sessionId: Long, 119 private val storedState: Bundle? 120 ) : ViewModel() { 121 private val LOG_TAG = GrantPermissionsViewModel::class.java.simpleName 122 private val user = Process.myUserHandle() 123 private val packageInfoLiveData = LightPackageInfoLiveData[packageName, user] 124 private val dpm = app.getSystemService(DevicePolicyManager::class.java)!! 125 private val permissionPolicy = dpm.getPermissionPolicy(null) 126 private val permGroupsToSkip = mutableListOf<String>() 127 private var groupStates = mutableMapOf<Pair<String, Boolean>, GroupState>() 128 private var isFirstTimeRequestingFineAndCoarse: Boolean = false 129 130 private var autoGrantNotifier: AutoGrantPermissionsNotifier? = null 131 private fun getAutoGrantNotifier(): AutoGrantPermissionsNotifier { 132 autoGrantNotifier = AutoGrantPermissionsNotifier(app, packageInfo.toPackageInfo(app)!!) 133 return autoGrantNotifier!! 134 } 135 136 private lateinit var packageInfo: LightPackageInfo 137 138 // All permissions that could possibly be affected by the provided requested permissions, before 139 // filtering system fixed, auto grant, etc. 140 private var unfilteredAffectedPermissions = requestedPermissions 141 142 /** 143 * A class which represents a correctly requested permission group, and the buttons and messages 144 * which should be shown with it. 145 */ 146 data class RequestInfo( 147 val groupInfo: LightPermGroupInfo, 148 val buttonVisibilities: List<Boolean> = List(NEXT_BUTTON) { false }, 149 val locationVisibilities: List<Boolean> = List(NEXT_LOCATION_DIALOG) { false }, 150 val message: RequestMessage = RequestMessage.FG_MESSAGE, 151 val detailMessage: RequestMessage = RequestMessage.NO_MESSAGE, 152 val sendToSettingsImmediately: Boolean = false 153 ) { 154 val groupName = groupInfo.name 155 } 156 157 var activityResultCallback: Consumer<Intent>? = null 158 159 /** 160 * A LiveData which holds a list of the currently pending RequestInfos 161 */ 162 val requestInfosLiveData = object : 163 SmartUpdateMediatorLiveData<List<RequestInfo>>() { 164 private val LOG_TAG = GrantPermissionsViewModel::class.java.simpleName 165 private val packagePermissionsLiveData = PackagePermissionsLiveData[packageName, user] 166 private val appPermGroupLiveDatas = mutableMapOf<String, LightAppPermGroupLiveData>() 167 168 init { 169 GlobalScope.launch(Main.immediate) { 170 val groups = packagePermissionsLiveData.getInitializedValue() 171 if (groups == null || groups.isEmpty()) { 172 Log.e(LOG_TAG, "Package $packageName not found") 173 value = null 174 return@launch 175 } 176 packageInfo = packageInfoLiveData.getInitializedValue() 177 178 if (packageInfo.requestedPermissions.isEmpty() || 179 packageInfo.targetSdkVersion < Build.VERSION_CODES.M) { 180 Log.e(LOG_TAG, "Package $packageName has no requested permissions, or " + 181 "is a pre-M app") 182 value = null 183 return@launch 184 } 185 186 val allAffectedPermissions = requestedPermissions.toMutableSet() 187 for (requestedPerm in requestedPermissions) { 188 allAffectedPermissions.addAll(computeAffectedPermissions(requestedPerm, groups)) 189 } 190 unfilteredAffectedPermissions = allAffectedPermissions.toList() 191 192 getAppPermGroups(groups.toMutableMap().apply { 193 remove(PackagePermissionsLiveData.NON_RUNTIME_NORMAL_PERMS) 194 }) 195 } 196 } 197 198 private fun getAppPermGroups(groups: Map<String, List<String>>) { 199 200 val requestedGroups = groups.filter { (_, perms) -> 201 perms.any { it in unfilteredAffectedPermissions } 202 } 203 204 if (requestedGroups.isEmpty()) { 205 Log.e(LOG_TAG, "None of " + 206 "$unfilteredAffectedPermissions in $groups") 207 value = null 208 return 209 } 210 211 val getLiveDataFun = { groupName: String -> 212 LightAppPermGroupLiveData[packageName, groupName, user] 213 } 214 setSourcesToDifference(requestedGroups.keys, appPermGroupLiveDatas, getLiveDataFun) 215 } 216 217 override fun onUpdate() { 218 if (appPermGroupLiveDatas.any { it.value.isStale }) { 219 return 220 } 221 var newGroups = false 222 for ((groupName, groupLiveData) in appPermGroupLiveDatas) { 223 val appPermGroup = groupLiveData.value 224 if (appPermGroup == null || groupName in permGroupsToSkip) { 225 if (appPermGroup == null) { 226 Log.e(LOG_TAG, "Group $packageName $groupName invalid") 227 } 228 groupStates[groupName to true]?.state = STATE_SKIPPED 229 groupStates[groupName to false]?.state = STATE_SKIPPED 230 continue 231 } 232 233 packageInfo = appPermGroup.packageInfo 234 235 val states = groupStates.filter { it.key.first == groupName } 236 if (states.isNotEmpty()) { 237 // some requests might have been granted, check for that 238 for ((key, state) in states) { 239 val allAffectedGranted = state.affectedPermissions.all { perm -> 240 appPermGroup.permissions[perm]?.isGrantedIncludingAppOp == true 241 } 242 if (allAffectedGranted) { 243 groupStates[key]!!.state = STATE_ALLOWED 244 } 245 } 246 } else { 247 newGroups = true 248 } 249 } 250 251 if (newGroups) { 252 groupStates = getRequiredGroupStates( 253 appPermGroupLiveDatas.mapNotNull { it.value.value }) 254 } 255 getRequestInfosFromGroupStates() 256 } 257 258 private fun getRequestInfosFromGroupStates() { 259 val requestInfos = mutableListOf<RequestInfo>() 260 for ((key, groupState) in groupStates) { 261 val groupInfo = groupState.group.permGroupInfo 262 val (groupName, isBackground) = key 263 if (groupState.state != STATE_UNKNOWN) { 264 continue 265 } 266 267 val fgState = groupStates[groupName to false] 268 val bgState = groupStates[groupName to true] 269 var needFgPermissions = false 270 var needBgPermissions = false 271 var isFgUserSet = false 272 var isBgUserSet = false 273 274 if (fgState?.group != null) { 275 val fgGroup = fgState.group 276 for (perm in fgState.affectedPermissions) { 277 if (fgGroup.permissions[perm]?.isGrantedIncludingAppOp == false) { 278 // If any of the requested permissions is not granted, 279 // needFgPermissions = true 280 needFgPermissions = true 281 // If any of the requested permission's UserSet is true and the 282 // permission is not granted, isFgUserSet = true. 283 if (fgGroup.permissions[perm]?.isUserSet == true) { 284 isFgUserSet = true 285 } 286 } 287 } 288 } 289 290 if (bgState?.group?.background?.isGranted == false) { 291 needBgPermissions = true 292 isBgUserSet = bgState.group.background.isUserSet 293 } 294 295 val buttonVisibilities = MutableList(NEXT_BUTTON) { false } 296 buttonVisibilities[ALLOW_BUTTON] = true 297 buttonVisibilities[DENY_BUTTON] = true 298 buttonVisibilities[ALLOW_ONE_TIME_BUTTON] = 299 Utils.supportsOneTimeGrant(groupName) 300 var message = RequestMessage.FG_MESSAGE 301 // Whether or not to use the foreground, background, or no detail message. 302 // null == 303 var detailMessage = RequestMessage.NO_MESSAGE 304 305 if (groupState.group.packageInfo.targetSdkVersion >= Build.VERSION_CODES.R) { 306 if (isBackground || groupState.group.hasPermWithBackgroundMode) { 307 if (needFgPermissions) { 308 if (needBgPermissions) { 309 if (groupState.group.permGroupName 310 .equals(Manifest.permission_group.CAMERA) || 311 groupState.group.permGroupName 312 .equals(Manifest.permission_group.MICROPHONE)) { 313 if (groupState.group.packageInfo.targetSdkVersion >= 314 Build.VERSION_CODES.S) { 315 Log.e(LOG_TAG, 316 "For S apps, background permissions must be " + 317 "requested after foreground permissions are" + 318 " already granted") 319 value = null 320 return 321 } else { 322 // Case: sdk < S, BG&FG mic/camera permission requested 323 buttonVisibilities[ALLOW_BUTTON] = false 324 buttonVisibilities[ALLOW_FOREGROUND_BUTTON] = true 325 buttonVisibilities[DENY_BUTTON] = !isFgUserSet 326 buttonVisibilities[DENY_AND_DONT_ASK_AGAIN_BUTTON] = 327 isFgUserSet 328 if (needBgPermissions) { 329 // Case: sdk < R, BG/FG permission requesting both 330 message = RequestMessage.BG_MESSAGE 331 detailMessage = RequestMessage.BG_MESSAGE 332 } 333 } 334 } else { 335 // Shouldn't be reached as background must be requested as a 336 // singleton 337 Log.e(LOG_TAG, "For R+ apps, background permissions must be " + 338 "requested after foreground permissions are already" + 339 " granted") 340 value = null 341 return 342 } 343 } else { 344 buttonVisibilities[ALLOW_BUTTON] = false 345 buttonVisibilities[ALLOW_FOREGROUND_BUTTON] = true 346 buttonVisibilities[DENY_BUTTON] = !isFgUserSet 347 buttonVisibilities[DENY_AND_DONT_ASK_AGAIN_BUTTON] = isFgUserSet 348 } 349 } else if (needBgPermissions) { 350 // Case: sdk >= R, BG/FG permission requesting BG only 351 requestInfos.add(RequestInfo( 352 groupInfo, sendToSettingsImmediately = true)) 353 continue 354 } else { 355 // Not reached as the permissions should be auto-granted 356 value = null 357 return 358 } 359 } else { 360 // Case: sdk >= R, Requesting normal permission 361 buttonVisibilities[DENY_BUTTON] = !isFgUserSet 362 buttonVisibilities[DENY_AND_DONT_ASK_AGAIN_BUTTON] = isFgUserSet 363 } 364 } else { 365 if (isBackground || groupState.group.hasPermWithBackgroundMode) { 366 if (needFgPermissions) { 367 // Case: sdk < R, BG/FG permission requesting both or FG only 368 buttonVisibilities[ALLOW_BUTTON] = false 369 buttonVisibilities[ALLOW_FOREGROUND_BUTTON] = true 370 buttonVisibilities[DENY_BUTTON] = !isFgUserSet 371 buttonVisibilities[DENY_AND_DONT_ASK_AGAIN_BUTTON] = isFgUserSet 372 if (needBgPermissions) { 373 // Case: sdk < R, BG/FG permission requesting both 374 message = RequestMessage.BG_MESSAGE 375 detailMessage = RequestMessage.BG_MESSAGE 376 } 377 } else if (needBgPermissions) { 378 // Case: sdk < R, BG/FG permission requesting BG only 379 if (!groupState.group.foreground.isGranted) { 380 Log.e(LOG_TAG, "Background permissions can't be requested " + 381 "solely before foreground permissions are granted.") 382 value = null 383 return 384 } 385 message = RequestMessage.UPGRADE_MESSAGE 386 detailMessage = RequestMessage.UPGRADE_MESSAGE 387 buttonVisibilities[ALLOW_BUTTON] = false 388 buttonVisibilities[DENY_BUTTON] = false 389 buttonVisibilities[ALLOW_ONE_TIME_BUTTON] = false 390 if (groupState.group.isOneTime) { 391 buttonVisibilities[NO_UPGRADE_OT_BUTTON] = !isBgUserSet 392 buttonVisibilities[NO_UPGRADE_OT_AND_DONT_ASK_AGAIN_BUTTON] = 393 isBgUserSet 394 } else { 395 buttonVisibilities[NO_UPGRADE_BUTTON] = !isBgUserSet 396 buttonVisibilities[NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON] = 397 isBgUserSet 398 } 399 } else { 400 // Not reached as the permissions should be auto-granted 401 value = null 402 return 403 } 404 } else { 405 // If no permissions needed, do nothing 406 if (!needFgPermissions && !needBgPermissions) { 407 value = null 408 return 409 } 410 // Case: sdk < R, Requesting normal permission 411 buttonVisibilities[DENY_BUTTON] = !isFgUserSet 412 buttonVisibilities[DENY_AND_DONT_ASK_AGAIN_BUTTON] = isFgUserSet 413 } 414 } 415 buttonVisibilities[LINK_TO_SETTINGS] = 416 detailMessage != RequestMessage.NO_MESSAGE 417 418 // Show location permission dialogs based on location permissions 419 val locationVisibilities = MutableList(NEXT_LOCATION_DIALOG) { false } 420 if (groupState.group.permGroupName == LOCATION && isLocationAccuracyEnabled()) { 421 if (needFgPermissions) { 422 locationVisibilities[LOCATION_ACCURACY_LAYOUT] = true 423 if (fgState != null && 424 fgState.affectedPermissions.contains(ACCESS_FINE_LOCATION)) { 425 val coarseLocationPerm = 426 groupState.group.allPermissions[ACCESS_COARSE_LOCATION] 427 if (coarseLocationPerm?.isGrantedIncludingAppOp == true) { 428 // Upgrade flow 429 locationVisibilities[DIALOG_WITH_FINE_LOCATION_ONLY] = true 430 message = RequestMessage.FG_FINE_LOCATION_MESSAGE 431 // If COARSE was granted one time, hide 'While in use' button 432 if (coarseLocationPerm.isOneTime) { 433 buttonVisibilities[ALLOW_FOREGROUND_BUTTON] = false 434 } 435 } else { 436 if (coarseLocationPerm?.isOneTime == false && 437 !coarseLocationPerm.isUserSet && 438 !coarseLocationPerm.isUserFixed) { 439 isFirstTimeRequestingFineAndCoarse = true 440 } 441 // Normal flow with both Coarse and Fine locations 442 locationVisibilities[DIALOG_WITH_BOTH_LOCATIONS] = true 443 // Steps to decide location accuracy default state 444 // 1. If none of the FINE and COARSE isSelectedLocationAccuracy 445 // flags is set, then use default precision from device config. 446 // 2. Otherwise set to whichever isSelectedLocationAccuracy is true. 447 val fineLocationPerm = 448 groupState.group.allPermissions[ACCESS_FINE_LOCATION] 449 if (coarseLocationPerm?.isSelectedLocationAccuracy == false && 450 fineLocationPerm?.isSelectedLocationAccuracy == false) { 451 if (getDefaultPrecision()) { 452 locationVisibilities[FINE_RADIO_BUTTON] = true 453 } else { 454 locationVisibilities[COARSE_RADIO_BUTTON] = true 455 } 456 } else if (coarseLocationPerm?.isSelectedLocationAccuracy == true) { 457 locationVisibilities[COARSE_RADIO_BUTTON] = true 458 } else { 459 locationVisibilities[FINE_RADIO_BUTTON] = true 460 } 461 } 462 } else if (fgState != null && fgState.affectedPermissions 463 .contains(ACCESS_COARSE_LOCATION)) { 464 // Request Coarse only 465 locationVisibilities[DIALOG_WITH_COARSE_LOCATION_ONLY] = true 466 message = RequestMessage.FG_COARSE_LOCATION_MESSAGE 467 } 468 } 469 } 470 471 requestInfos.add(RequestInfo( 472 groupInfo, 473 buttonVisibilities, 474 locationVisibilities, 475 message, 476 detailMessage)) 477 } 478 requestInfos.sortWith(Comparator { rhs, lhs -> 479 val rhsHasOneTime = rhs.buttonVisibilities[ALLOW_ONE_TIME_BUTTON] 480 val lhsHasOneTime = lhs.buttonVisibilities[ALLOW_ONE_TIME_BUTTON] 481 if (rhsHasOneTime && !lhsHasOneTime) { 482 -1 483 } else if (!rhsHasOneTime && lhsHasOneTime) { 484 1 485 } else { 486 rhs.groupName.compareTo(lhs.groupName) 487 } 488 }) 489 490 value = if (requestInfos.any { it.sendToSettingsImmediately } && 491 requestInfos.size > 1) { 492 Log.e(LOG_TAG, "For R+ apps, background permissions must be requested " + 493 "individually") 494 null 495 } else { 496 requestInfos 497 } 498 } 499 } 500 501 /** 502 * Converts a list of LightAppPermGroups into a list of GroupStates 503 */ 504 private fun getRequiredGroupStates( 505 groups: List<LightAppPermGroup> 506 ): MutableMap<Pair<String, Boolean>, GroupState> { 507 val groupStates = mutableMapOf<Pair<String, Boolean>, GroupState>() 508 val filteredPermissions = unfilteredAffectedPermissions.filter { perm -> 509 val group = getGroupWithPerm(perm, groups) 510 group != null && isPermissionGrantableAndNotFixed(perm, group) 511 } 512 for (perm in filteredPermissions) { 513 val group = getGroupWithPerm(perm, groups)!! 514 515 val isBackground = perm in group.backgroundPermNames 516 val groupStateInfo = groupStates.getOrPut(group.permGroupName to isBackground) { 517 GroupState(group, isBackground) 518 } 519 520 var currGroupState = groupStateInfo.state 521 if (storedState != null && currGroupState != STATE_UNKNOWN) { 522 currGroupState = storedState.getInt(getInstanceStateKey(group.permGroupName, 523 isBackground), STATE_UNKNOWN) 524 } 525 526 val otherGroupPermissions = filteredPermissions.filter { it in group.permissions } 527 val groupStateOfPerm = getGroupState(perm, group, otherGroupPermissions) 528 if (groupStateOfPerm != STATE_UNKNOWN) { 529 currGroupState = groupStateOfPerm 530 } 531 532 if (group.permGroupName in permGroupsToSkip) { 533 currGroupState = STATE_SKIPPED 534 } 535 536 if (currGroupState != STATE_UNKNOWN) { 537 groupStateInfo.state = currGroupState 538 } 539 // If we saved state, load it 540 groupStateInfo.affectedPermissions.add(perm) 541 } 542 return groupStates 543 } 544 545 /** 546 * Get the actually requested permissions when a permission is requested. 547 * 548 * >In some cases requesting to grant a single permission requires the system to grant 549 * additional permissions. E.g. before N-MR1 a single permission of a group caused the whole 550 * group to be granted. Another case are permissions that are split into two. For apps that 551 * target an SDK before the split, this method automatically adds the split off permission. 552 * 553 * @param perm The requested permission 554 * 555 * @return The actually requested permissions 556 */ 557 private fun computeAffectedPermissions( 558 perm: String, 559 appPermissions: Map<String, List<String>> 560 ): List<String> { 561 val requestingAppTargetSDK = packageInfo.targetSdkVersion 562 563 // If a permission is split, all permissions the original permission is split into are 564 // affected 565 val extendedBySplitPerms = mutableListOf(perm) 566 567 val splitPerms = app.getSystemService(PermissionManager::class.java)!!.splitPermissions 568 for (splitPerm in splitPerms) { 569 570 if (requestingAppTargetSDK < splitPerm.targetSdk && perm == splitPerm.splitPermission) { 571 extendedBySplitPerms.addAll(splitPerm.newPermissions) 572 } 573 } 574 575 // For <= N_MR1 apps all permissions of the groups of the requested permissions are affected 576 if (requestingAppTargetSDK <= Build.VERSION_CODES.N_MR1) { 577 val extendedBySplitPermsAndGroup = mutableListOf<String>() 578 579 for (splitPerm in extendedBySplitPerms) { 580 val groups = appPermissions.filter { splitPerm in it.value } 581 if (groups.isEmpty()) { 582 continue 583 } 584 585 val permissionsInGroup = groups.values.first() 586 for (permissionInGroup in permissionsInGroup) { 587 extendedBySplitPermsAndGroup.add(permissionInGroup) 588 } 589 } 590 591 return extendedBySplitPermsAndGroup 592 } else { 593 return extendedBySplitPerms 594 } 595 } 596 597 private fun isPermissionGrantableAndNotFixed(perm: String, group: LightAppPermGroup): Boolean { 598 599 // If the permission is restricted it does not show in the UI and 600 // is not added to the group at all, so check that first. 601 if (perm in group.packageInfo.requestedPermissions && perm !in group.permissions) { 602 reportRequestResult(perm, 603 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_RESTRICTED_PERMISSION) 604 return false 605 } 606 607 val subGroup = if (perm in group.backgroundPermNames) { 608 group.background 609 } else { 610 group.foreground 611 } 612 613 val lightPermission = group.permissions[perm] ?: return false 614 615 if (!subGroup.isGrantable) { 616 reportRequestResult(perm, PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED) 617 // Skip showing groups that we know cannot be granted. 618 return false 619 } else if (subGroup.isUserFixed) { 620 reportRequestResult(perm, 621 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_USER_FIXED) 622 return false 623 } else if (subGroup.isPolicyFixed && !subGroup.isGranted || lightPermission.isPolicyFixed) { 624 reportRequestResult(perm, 625 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_POLICY_FIXED) 626 return false 627 } 628 629 return true 630 } 631 632 private fun getGroupState( 633 perm: String, 634 group: LightAppPermGroup, 635 groupRequestedPermissions: List<String> 636 ): Int { 637 val policyState = getStateFromPolicy(perm, group) 638 if (policyState != STATE_UNKNOWN) { 639 return policyState 640 } 641 642 val isBackground = perm in group.backgroundPermNames 643 644 val hasForegroundRequest = groupRequestedPermissions.any { 645 it !in group.backgroundPermNames 646 } 647 648 // Do not attempt to grant background access if foreground access is not either already 649 // granted or requested 650 if (isBackground && !group.foreground.isGranted && !hasForegroundRequest) { 651 Log.w(LOG_TAG, "Cannot grant $perm as the matching foreground permission is not " + 652 "already granted.") 653 val affectedPermissions = groupRequestedPermissions.filter { 654 it in group.backgroundPermNames 655 } 656 reportRequestResult(affectedPermissions, 657 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED) 658 return STATE_SKIPPED 659 } 660 661 if (isBackground && group.background.isGranted || 662 !isBackground && group.foreground.isGranted) { 663 // If FINE location is not granted, do not grant it automatically when COARSE 664 // location is already granted. 665 if (group.permGroupName == LOCATION && 666 group.allPermissions[ACCESS_FINE_LOCATION]?.isGrantedIncludingAppOp 667 == false) { 668 return STATE_UNKNOWN 669 } 670 671 if (group.permissions[perm]?.isGrantedIncludingAppOp == false) { 672 if (isBackground) { 673 KotlinUtils.grantBackgroundRuntimePermissions(app, group, listOf(perm)) 674 } else { 675 KotlinUtils.grantForegroundRuntimePermissions(app, group, listOf(perm)) 676 } 677 KotlinUtils.setGroupFlags(app, group, FLAG_PERMISSION_USER_SET to false, 678 FLAG_PERMISSION_USER_FIXED to false, filterPermissions = listOf(perm)) 679 reportRequestResult(perm, 680 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_GRANTED) 681 } 682 683 return if (storedState == null) { 684 STATE_SKIPPED 685 } else { 686 STATE_ALLOWED 687 } 688 } 689 return STATE_UNKNOWN 690 } 691 692 private fun getStateFromPolicy(perm: String, group: LightAppPermGroup): Int { 693 val isBackground = perm in group.backgroundPermNames 694 var skipGroup = false 695 var state = STATE_UNKNOWN 696 when (permissionPolicy) { 697 DevicePolicyManager.PERMISSION_POLICY_AUTO_GRANT -> { 698 if (AdminRestrictedPermissionsUtils.mayAdminGrantPermission( 699 app, perm, user.identifier)) { 700 if (isBackground) { 701 KotlinUtils.grantBackgroundRuntimePermissions(app, group, listOf(perm)) 702 } else { 703 KotlinUtils.grantForegroundRuntimePermissions(app, group, listOf(perm)) 704 } 705 KotlinUtils.setGroupFlags(app, group, FLAG_PERMISSION_POLICY_FIXED to true, 706 FLAG_PERMISSION_USER_SET to false, FLAG_PERMISSION_USER_FIXED to false, 707 filterPermissions = listOf(perm)) 708 state = STATE_ALLOWED 709 skipGroup = true 710 711 getAutoGrantNotifier().onPermissionAutoGranted(perm) 712 reportRequestResult(perm, 713 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_GRANTED) 714 } 715 } 716 717 DevicePolicyManager.PERMISSION_POLICY_AUTO_DENY -> { 718 if (group.permissions[perm]?.isPolicyFixed == false) { 719 KotlinUtils.setGroupFlags(app, group, FLAG_PERMISSION_POLICY_FIXED to true, 720 FLAG_PERMISSION_USER_SET to false, FLAG_PERMISSION_USER_FIXED to false, 721 filterPermissions = listOf(perm)) 722 } 723 state = STATE_DENIED 724 skipGroup = true 725 726 reportRequestResult(perm, 727 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_DENIED) 728 } 729 } 730 if (skipGroup && storedState == null) { 731 return STATE_SKIPPED 732 } 733 return state 734 } 735 736 /** 737 * Upon the user clicking a button, grant permissions, if applicable. 738 * 739 * @param groupName The name of the permission group which was changed 740 * @param affectedForegroundPermissions The name of the foreground permission which was changed 741 * @param result The choice the user made regarding the group. 742 */ 743 fun onPermissionGrantResult( 744 groupName: String?, 745 affectedForegroundPermissions: List<String>?, 746 result: Int 747 ) { 748 if (groupName == null) { 749 return 750 } 751 val foregroundGroupState = groupStates[groupName to false] 752 val backgroundGroupState = groupStates[groupName to true] 753 when (result) { 754 GrantPermissionsViewHandler.CANCELED -> { 755 if (foregroundGroupState != null) { 756 reportRequestResult(foregroundGroupState.affectedPermissions, 757 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_IGNORED) 758 } 759 if (backgroundGroupState != null) { 760 reportRequestResult(backgroundGroupState.affectedPermissions, 761 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_IGNORED) 762 } 763 return 764 } 765 GRANTED_ALWAYS -> { 766 if (foregroundGroupState != null) { 767 onPermissionGrantResultSingleState(foregroundGroupState, 768 affectedForegroundPermissions, granted = true, isOneTime = false, 769 doNotAskAgain = false) 770 } 771 if (backgroundGroupState != null) { 772 onPermissionGrantResultSingleState(backgroundGroupState, 773 affectedForegroundPermissions, granted = true, isOneTime = false, 774 doNotAskAgain = false) 775 } 776 } 777 GRANTED_FOREGROUND_ONLY -> { 778 if (foregroundGroupState != null) { 779 onPermissionGrantResultSingleState(foregroundGroupState, 780 affectedForegroundPermissions, granted = true, isOneTime = false, 781 doNotAskAgain = false) 782 } 783 if (backgroundGroupState != null) { 784 onPermissionGrantResultSingleState(backgroundGroupState, 785 affectedForegroundPermissions, granted = false, isOneTime = false, 786 doNotAskAgain = false) 787 } 788 } 789 GrantPermissionsViewHandler.GRANTED_ONE_TIME -> { 790 if (foregroundGroupState != null) { 791 onPermissionGrantResultSingleState(foregroundGroupState, 792 affectedForegroundPermissions, granted = true, isOneTime = true, 793 doNotAskAgain = false) 794 } 795 if (backgroundGroupState != null) { 796 onPermissionGrantResultSingleState(backgroundGroupState, 797 affectedForegroundPermissions, granted = false, isOneTime = true, 798 doNotAskAgain = false) 799 } 800 } 801 DENIED -> { 802 if (foregroundGroupState != null) { 803 onPermissionGrantResultSingleState(foregroundGroupState, 804 affectedForegroundPermissions, granted = false, isOneTime = false, 805 doNotAskAgain = false) 806 } 807 if (backgroundGroupState != null) { 808 onPermissionGrantResultSingleState(backgroundGroupState, 809 affectedForegroundPermissions, granted = false, isOneTime = false, 810 doNotAskAgain = false) 811 } 812 } 813 DENIED_DO_NOT_ASK_AGAIN -> { 814 if (foregroundGroupState != null) { 815 onPermissionGrantResultSingleState(foregroundGroupState, 816 affectedForegroundPermissions, granted = false, isOneTime = false, 817 doNotAskAgain = true) 818 } 819 if (backgroundGroupState != null) { 820 onPermissionGrantResultSingleState(backgroundGroupState, 821 affectedForegroundPermissions, granted = false, isOneTime = false, 822 doNotAskAgain = true) 823 } 824 } 825 } 826 } 827 828 private fun onPermissionGrantResultSingleState( 829 groupState: GroupState, 830 affectedForegroundPermissions: List<String>?, 831 granted: Boolean, 832 isOneTime: Boolean, 833 doNotAskAgain: Boolean 834 ) { 835 if (groupState.state != STATE_UNKNOWN) { 836 // We already dealt with this group, don't re-grant/re-revoke 837 return 838 } 839 val result: Int 840 if (granted) { 841 result = if (isOneTime) { 842 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED_ONE_TIME 843 } else { 844 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED 845 } 846 if (groupState.isBackground) { 847 KotlinUtils.grantBackgroundRuntimePermissions(app, groupState.group, 848 groupState.affectedPermissions) 849 } else { 850 if (affectedForegroundPermissions == null) { 851 KotlinUtils.grantForegroundRuntimePermissions(app, groupState.group, 852 groupState.affectedPermissions, isOneTime) 853 // This prevents weird flag state when app targetSDK switches from S+ to R- 854 if (groupState.affectedPermissions.contains(ACCESS_FINE_LOCATION)) { 855 KotlinUtils.setFlagsWhenLocationAccuracyChanged( 856 app, groupState.group, true) 857 } 858 } else { 859 val newGroup = KotlinUtils.grantForegroundRuntimePermissions(app, 860 groupState.group, affectedForegroundPermissions, isOneTime) 861 if (!isOneTime || newGroup.isOneTime) { 862 KotlinUtils.setFlagsWhenLocationAccuracyChanged(app, newGroup, 863 affectedForegroundPermissions.contains(ACCESS_FINE_LOCATION)) 864 } 865 } 866 } 867 groupState.state = STATE_ALLOWED 868 } else { 869 if (groupState.isBackground) { 870 KotlinUtils.revokeBackgroundRuntimePermissions(app, groupState.group, 871 userFixed = doNotAskAgain, filterPermissions = groupState.affectedPermissions) 872 } else { 873 if (affectedForegroundPermissions == null) { 874 KotlinUtils.revokeForegroundRuntimePermissions(app, groupState.group, 875 userFixed = doNotAskAgain, 876 filterPermissions = groupState.affectedPermissions, oneTime = isOneTime) 877 } else { 878 KotlinUtils.revokeForegroundRuntimePermissions(app, groupState.group, 879 userFixed = doNotAskAgain, 880 filterPermissions = affectedForegroundPermissions, oneTime = isOneTime) 881 } 882 } 883 result = if (doNotAskAgain) { 884 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_WITH_PREJUDICE 885 } else { 886 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED 887 } 888 groupState.state = STATE_DENIED 889 } 890 reportRequestResult(groupState.affectedPermissions, result) 891 // group state has changed, reload liveData 892 requestInfosLiveData.update() 893 } 894 895 private fun getGroupWithPerm( 896 perm: String, 897 groups: List<LightAppPermGroup> 898 ): LightAppPermGroup? { 899 val groupsWithPerm = groups.filter { perm in it.permissions } 900 if (groupsWithPerm.isEmpty()) { 901 return null 902 } 903 return groupsWithPerm.first() 904 } 905 906 /** 907 * An internal class which represents the state of a current AppPermissionGroup grant request. 908 */ 909 internal class GroupState( 910 internal val group: LightAppPermGroup, 911 internal val isBackground: Boolean, 912 internal val affectedPermissions: MutableList<String> = mutableListOf(), 913 internal var state: Int = STATE_UNKNOWN 914 ) { 915 override fun toString(): String { 916 val stateStr: String = when (state) { 917 STATE_UNKNOWN -> "unknown" 918 STATE_ALLOWED -> "granted" 919 STATE_DENIED -> "denied" 920 else -> "skipped" 921 } 922 return "${group.permGroupName} $isBackground $stateStr $affectedPermissions" 923 } 924 } 925 926 private fun reportRequestResult(permissions: List<String>, result: Int) { 927 for (perm in permissions) { 928 reportRequestResult(perm, result) 929 } 930 } 931 932 /** 933 * Report the result of a grant of a permission. 934 * 935 * @param permission The permission that was granted or denied 936 * @param result The permission grant result 937 */ 938 private fun reportRequestResult(permission: String, result: Int) { 939 val isImplicit = permission !in requestedPermissions 940 941 Log.v(LOG_TAG, "Permission grant result requestId=$sessionId " + 942 "callingUid=${packageInfo.uid} callingPackage=$packageName permission=$permission " + 943 "isImplicit=$isImplicit result=$result") 944 945 PermissionControllerStatsLog.write( 946 PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED, sessionId, 947 packageInfo.uid, packageName, permission, isImplicit, result) 948 } 949 950 /** 951 * Save the group states of the view model, to allow for state restoration after lifecycle 952 * events 953 * 954 * @param outState The bundle in which to store state 955 */ 956 fun saveInstanceState(outState: Bundle) { 957 for ((groupKey, groupState) in groupStates) { 958 val (groupName, isBackground) = groupKey 959 outState.putInt(getInstanceStateKey(groupName, isBackground), groupState.state) 960 } 961 } 962 963 /** 964 * Determine if the activity should return permission state to the caller 965 * 966 * @return Whether or not state should be returned. False only if the package is pre-M, true 967 * otherwise. 968 */ 969 fun shouldReturnPermissionState(): Boolean { 970 return if (packageInfoLiveData.value != null) { 971 packageInfoLiveData.value!!.targetSdkVersion >= Build.VERSION_CODES.M 972 } else { 973 // Should not be reached, as this method shouldn't be called before data is passed to 974 // the activity for the first time 975 try { 976 Utils.getUserContext(app, user).packageManager 977 .getApplicationInfo(packageName, 0).targetSdkVersion >= Build.VERSION_CODES.M 978 } catch (e: PackageManager.NameNotFoundException) { 979 true 980 } 981 } 982 } 983 984 /** 985 * Send the user directly to the AppPermissionFragment. Used for R+ apps. 986 * 987 * @param activity The current activity 988 * @param groupName The name of the permission group whose fragment should be opened 989 */ 990 fun sendDirectlyToSettings(activity: Activity, groupName: String) { 991 if (activityResultCallback == null) { 992 startAppPermissionFragment(activity, groupName) 993 activityResultCallback = Consumer { data -> 994 if (data?.getStringExtra(EXTRA_RESULT_PERMISSION_INTERACTED) == null) { 995 // User didn't interact, count against rate limit 996 val group = groupStates[groupName to false]?.group 997 ?: groupStates[groupName to true]?.group ?: return@Consumer 998 if (group.background.isUserSet) { 999 KotlinUtils.setGroupFlags(app, group, FLAG_PERMISSION_USER_FIXED to true, 1000 filterPermissions = group.backgroundPermNames) 1001 } else { 1002 KotlinUtils.setGroupFlags(app, group, FLAG_PERMISSION_USER_SET to true, 1003 filterPermissions = group.backgroundPermNames) 1004 } 1005 } 1006 1007 permGroupsToSkip.add(groupName) 1008 // Update our liveData now that there is a new skipped group 1009 requestInfosLiveData.update() 1010 } 1011 } 1012 } 1013 1014 /** 1015 * Send the user to the AppPermissionFragment from a link. Used for Q- apps 1016 * 1017 * @param activity The current activity 1018 * @param groupName The name of the permission group whose fragment should be opened 1019 */ 1020 fun sendToSettingsFromLink(activity: Activity, groupName: String) { 1021 startAppPermissionFragment(activity, groupName) 1022 activityResultCallback = Consumer { data -> 1023 val returnGroupName = data?.getStringExtra(EXTRA_RESULT_PERMISSION_INTERACTED) 1024 if (returnGroupName != null) { 1025 permGroupsToSkip.add(returnGroupName) 1026 val result = data.getIntExtra(EXTRA_RESULT_PERMISSION_RESULT, -1) 1027 logSettingsInteraction(returnGroupName, result) 1028 requestInfosLiveData.update() 1029 } 1030 } 1031 } 1032 1033 private fun startAppPermissionFragment(activity: Activity, groupName: String) { 1034 val intent = Intent(Intent.ACTION_MANAGE_APP_PERMISSION) 1035 .putExtra(Intent.EXTRA_PACKAGE_NAME, packageName) 1036 .putExtra(Intent.EXTRA_PERMISSION_GROUP_NAME, groupName) 1037 .putExtra(Intent.EXTRA_USER, user) 1038 .putExtra(ManagePermissionsActivity.EXTRA_CALLER_NAME, 1039 GrantPermissionsActivity::class.java.name) 1040 .putExtra(Constants.EXTRA_SESSION_ID, sessionId) 1041 .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) 1042 activity.startActivityForResult(intent, APP_PERMISSION_REQUEST_CODE) 1043 } 1044 1045 private fun getInstanceStateKey(groupName: String, isBackground: Boolean): String { 1046 return "${this::class.java.name}_${groupName}_$isBackground" 1047 } 1048 1049 private fun logSettingsInteraction(groupName: String, result: Int) { 1050 val foregroundGroupState = groupStates[groupName to false] 1051 val backgroundGroupState = groupStates[groupName to true] 1052 val deniedPrejudiceInSettings = 1053 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_WITH_PREJUDICE_IN_SETTINGS 1054 when (result) { 1055 GRANTED_ALWAYS -> { 1056 if (foregroundGroupState != null) { 1057 reportRequestResult(foregroundGroupState.affectedPermissions, 1058 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED_IN_SETTINGS) 1059 } 1060 if (backgroundGroupState != null) { 1061 reportRequestResult(backgroundGroupState.affectedPermissions, 1062 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED_IN_SETTINGS) 1063 } 1064 } 1065 GRANTED_FOREGROUND_ONLY -> { 1066 if (foregroundGroupState != null) { 1067 reportRequestResult(foregroundGroupState.affectedPermissions, 1068 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED_IN_SETTINGS) 1069 } 1070 if (backgroundGroupState != null) { 1071 reportRequestResult(backgroundGroupState.affectedPermissions, 1072 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_IN_SETTINGS) 1073 } 1074 } 1075 DENIED -> { 1076 if (foregroundGroupState != null) { 1077 reportRequestResult(foregroundGroupState.affectedPermissions, 1078 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_IN_SETTINGS) 1079 } 1080 if (backgroundGroupState != null) { 1081 reportRequestResult(backgroundGroupState.affectedPermissions, 1082 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_IN_SETTINGS) 1083 } 1084 } 1085 DENIED_DO_NOT_ASK_AGAIN -> { 1086 if (foregroundGroupState != null) { 1087 reportRequestResult(foregroundGroupState.affectedPermissions, 1088 deniedPrejudiceInSettings) 1089 } 1090 if (backgroundGroupState != null) { 1091 reportRequestResult(backgroundGroupState.affectedPermissions, 1092 deniedPrejudiceInSettings) 1093 } 1094 } 1095 } 1096 } 1097 1098 /** 1099 * Log all permission groups which were requested 1100 */ 1101 fun logRequestedPermissionGroups() { 1102 if (groupStates.isEmpty()) { 1103 return 1104 } 1105 val groups = groupStates.map { it.value.group } 1106 SafetyNetLogger.logPermissionsRequested(packageName, packageInfo.uid, groups) 1107 } 1108 1109 /** 1110 * Log information about the buttons which were shown and clicked by the user. 1111 * 1112 * @param groupName The name of the permission group which was interacted with 1113 * @param selectedPrecision Selected precision of the location permission - bit flags indicate 1114 * which locations were chosen 1115 * @param clickedButton The button that was clicked by the user 1116 * @param presentedButtons All buttons which were shown to the user 1117 */ 1118 fun logClickedButtons( 1119 groupName: String?, 1120 selectedPrecision: Int, 1121 clickedButton: Int, 1122 presentedButtons: Int 1123 ) { 1124 if (groupName == null) { 1125 return 1126 } 1127 var selectedLocations = 0 1128 // log permissions if it's 1) first time requesting both locations OR 2) upgrade flow 1129 if (isFirstTimeRequestingFineAndCoarse || 1130 selectedPrecision == 1131 1 shl PERMISSION_TO_BIT_SHIFT[ACCESS_FINE_LOCATION]!!) { 1132 selectedLocations = selectedPrecision 1133 } 1134 PermissionControllerStatsLog.write(GRANT_PERMISSIONS_ACTIVITY_BUTTON_ACTIONS, 1135 groupName, packageInfo.uid, packageName, presentedButtons, clickedButton, sessionId, 1136 packageInfo.targetSdkVersion, selectedLocations) 1137 Log.v(LOG_TAG, "Logged buttons presented and clicked permissionGroupName=" + 1138 "$groupName uid=${packageInfo.uid} selectedLocations=$selectedLocations " + 1139 "package=$packageName presentedButtons=$presentedButtons " + 1140 "clickedButton=$clickedButton sessionId=$sessionId " + 1141 "targetSdk=${packageInfo.targetSdkVersion}") 1142 } 1143 1144 /** 1145 * Use the autoGrantNotifier to notify of auto-granted permissions. 1146 */ 1147 fun autoGrantNotify() { 1148 autoGrantNotifier?.notifyOfAutoGrantPermissions(true) 1149 } 1150 1151 companion object { 1152 private const val APP_PERMISSION_REQUEST_CODE = 1 1153 private const val STATE_UNKNOWN = 0 1154 private const val STATE_ALLOWED = 1 1155 private const val STATE_DENIED = 2 1156 private const val STATE_SKIPPED = 3 1157 private const val STATE_ALREADY_ALLOWED = 4 1158 1159 /** 1160 * An enum that represents the type of message which should be shown- foreground, 1161 * background, upgrade, or no message. 1162 */ 1163 enum class RequestMessage(request: Int) { 1164 FG_MESSAGE(0), 1165 BG_MESSAGE(1), 1166 UPGRADE_MESSAGE(2), 1167 NO_MESSAGE(3), 1168 FG_FINE_LOCATION_MESSAGE(4), 1169 FG_COARSE_LOCATION_MESSAGE(5) 1170 } 1171 } 1172 } 1173 1174 /** 1175 * Factory for an AppPermissionViewModel 1176 * 1177 * @param app The current application 1178 * @param packageName The name of the package this ViewModel represents 1179 */ 1180 class GrantPermissionsViewModelFactory( 1181 private val app: Application, 1182 private val packageName: String, 1183 private val requestedPermissions: Array<String>, 1184 private val sessionId: Long, 1185 private val savedState: Bundle? 1186 ) : ViewModelProvider.Factory { createnull1187 override fun <T : ViewModel> create(modelClass: Class<T>): T { 1188 @Suppress("UNCHECKED_CAST") 1189 return GrantPermissionsViewModel(app, packageName, requestedPermissions.toList(), sessionId, 1190 savedState) as T 1191 } 1192 } 1193