1 /* 2 * Copyright (C) 2015 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.packageinstaller.permission.ui; 18 19 import static android.content.pm.PackageManager.PERMISSION_DENIED; 20 import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; 21 22 import static com.android.packageinstaller.PermissionControllerStatsLog.GRANT_PERMISSIONS_ACTIVITY_BUTTON_ACTIONS; 23 import static com.android.packageinstaller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_DENIED; 24 import static com.android.packageinstaller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_GRANTED; 25 import static com.android.packageinstaller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED; 26 import static com.android.packageinstaller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_POLICY_FIXED; 27 import static com.android.packageinstaller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_RESTRICTED_PERMISSION; 28 import static com.android.packageinstaller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_USER_FIXED; 29 import static com.android.packageinstaller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED; 30 import static com.android.packageinstaller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_WITH_PREJUDICE; 31 import static com.android.packageinstaller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED; 32 import static com.android.packageinstaller.permission.ui.GrantPermissionsViewHandler.DENIED; 33 import static com.android.packageinstaller.permission.ui.GrantPermissionsViewHandler.DENIED_DO_NOT_ASK_AGAIN; 34 import static com.android.packageinstaller.permission.ui.GrantPermissionsViewHandler.GRANTED_ALWAYS; 35 import static com.android.packageinstaller.permission.ui.GrantPermissionsViewHandler.GRANTED_FOREGROUND_ONLY; 36 import static com.android.packageinstaller.permission.utils.Utils.getRequestMessage; 37 38 import android.app.Activity; 39 import android.app.KeyguardManager; 40 import android.app.admin.DevicePolicyManager; 41 import android.content.Intent; 42 import android.content.pm.PackageInfo; 43 import android.content.pm.PackageManager; 44 import android.content.pm.PackageManager.NameNotFoundException; 45 import android.content.res.Resources; 46 import android.graphics.drawable.Icon; 47 import android.os.Build; 48 import android.os.Bundle; 49 import android.os.UserHandle; 50 import android.permission.PermissionManager; 51 import android.text.Html; 52 import android.text.Spanned; 53 import android.util.ArrayMap; 54 import android.util.Log; 55 import android.util.Pair; 56 import android.view.KeyEvent; 57 import android.view.MotionEvent; 58 import android.view.View; 59 import android.view.Window; 60 import android.view.WindowManager; 61 62 import androidx.annotation.NonNull; 63 import androidx.annotation.Nullable; 64 65 import com.android.packageinstaller.DeviceUtils; 66 import com.android.packageinstaller.PermissionControllerStatsLog; 67 import com.android.packageinstaller.permission.model.AppPermissionGroup; 68 import com.android.packageinstaller.permission.model.AppPermissions; 69 import com.android.packageinstaller.permission.model.Permission; 70 import com.android.packageinstaller.permission.ui.auto.GrantPermissionsAutoViewHandler; 71 import com.android.packageinstaller.permission.utils.ArrayUtils; 72 import com.android.packageinstaller.permission.utils.PackageRemovalMonitor; 73 import com.android.packageinstaller.permission.utils.SafetyNetLogger; 74 import com.android.permissioncontroller.R; 75 76 import java.util.ArrayList; 77 import java.util.List; 78 import java.util.Random; 79 80 public class GrantPermissionsActivity extends Activity 81 implements GrantPermissionsViewHandler.ResultListener { 82 83 private static final String LOG_TAG = "GrantPermissionsActivity"; 84 85 private static final String KEY_REQUEST_ID = GrantPermissionsActivity.class.getName() 86 + "_REQUEST_ID"; 87 88 public static int NUM_BUTTONS = 5; 89 public static int LABEL_ALLOW_BUTTON = 0; 90 public static int LABEL_ALLOW_ALWAYS_BUTTON = 1; 91 public static int LABEL_ALLOW_FOREGROUND_BUTTON = 2; 92 public static int LABEL_DENY_BUTTON = 3; 93 public static int LABEL_DENY_AND_DONT_ASK_AGAIN_BUTTON = 4; 94 95 /** Unique Id of a request */ 96 private long mRequestId; 97 98 private String[] mRequestedPermissions; 99 private CharSequence[] mButtonLabels; 100 101 private ArrayMap<Pair<String, Boolean>, GroupState> mRequestGrantPermissionGroups = 102 new ArrayMap<>(); 103 104 private GrantPermissionsViewHandler mViewHandler; 105 private AppPermissions mAppPermissions; 106 107 boolean mResultSet; 108 109 /** 110 * Listens for changes to the permission of the app the permissions are currently getting 111 * granted to. {@code null} when unregistered. 112 */ 113 private @Nullable PackageManager.OnPermissionsChangedListener mPermissionChangeListener; 114 115 /** 116 * Listens for changes to the app the permissions are currently getting granted to. {@code null} 117 * when unregistered. 118 */ 119 private @Nullable PackageRemovalMonitor mPackageRemovalMonitor; 120 121 /** Package that requested the permission grant */ 122 private String mCallingPackage; 123 /** uid of {@link #mCallingPackage} */ 124 private int mCallingUid; 125 getPermissionPolicy()126 private int getPermissionPolicy() { 127 DevicePolicyManager devicePolicyManager = getSystemService(DevicePolicyManager.class); 128 return devicePolicyManager.getPermissionPolicy(null); 129 } 130 131 /** 132 * Try to add a single permission that is requested to be granted. 133 * 134 * <p>This does <u>not</u> expand the permissions into the {@link #computeAffectedPermissions 135 * affected permissions}. 136 * 137 * @param group The group the permission belongs to (might be a background permission group) 138 * @param permName The name of the permission to add 139 * @param isFirstInstance Is this the first time the groupStates get created 140 */ addRequestedPermissions(AppPermissionGroup group, String permName, boolean isFirstInstance)141 private void addRequestedPermissions(AppPermissionGroup group, String permName, 142 boolean isFirstInstance) { 143 if (!group.isGrantingAllowed()) { 144 reportRequestResult(permName, 145 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED); 146 // Skip showing groups that we know cannot be granted. 147 return; 148 } 149 150 Permission permission = group.getPermission(permName); 151 152 // If the permission is restricted it does not show in the UI and 153 // is not added to the group at all, so check that first. 154 if (permission == null && ArrayUtils.contains( 155 mAppPermissions.getPackageInfo().requestedPermissions, permName)) { 156 reportRequestResult(permName, 157 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_RESTRICTED_PERMISSION); 158 return; 159 // We allow the user to choose only non-fixed permissions. A permission 160 // is fixed either by device policy or the user denying with prejudice. 161 } else if (group.isUserFixed()) { 162 reportRequestResult(permName, 163 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_USER_FIXED); 164 return; 165 } else if (group.isPolicyFixed() && !group.areRuntimePermissionsGranted() 166 || permission.isPolicyFixed()) { 167 reportRequestResult(permName, 168 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_POLICY_FIXED); 169 return; 170 } 171 172 Pair<String, Boolean> groupKey = new Pair<>(group.getName(), 173 group.isBackgroundGroup()); 174 175 GroupState state = mRequestGrantPermissionGroups.get(groupKey); 176 if (state == null) { 177 state = new GroupState(group); 178 mRequestGrantPermissionGroups.put(groupKey, state); 179 } 180 state.affectedPermissions = ArrayUtils.appendString( 181 state.affectedPermissions, permName); 182 183 boolean skipGroup = false; 184 switch (getPermissionPolicy()) { 185 case DevicePolicyManager.PERMISSION_POLICY_AUTO_GRANT: { 186 final String[] filterPermissions = new String[]{permName}; 187 group.grantRuntimePermissions(false, filterPermissions); 188 group.setPolicyFixed(filterPermissions); 189 state.mState = GroupState.STATE_ALLOWED; 190 skipGroup = true; 191 192 reportRequestResult(permName, 193 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_GRANTED); 194 } break; 195 196 case DevicePolicyManager.PERMISSION_POLICY_AUTO_DENY: { 197 final String[] filterPermissions = new String[]{permName}; 198 group.setPolicyFixed(filterPermissions); 199 state.mState = GroupState.STATE_DENIED; 200 skipGroup = true; 201 202 reportRequestResult(permName, 203 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_DENIED); 204 } break; 205 206 default: { 207 if (group.areRuntimePermissionsGranted()) { 208 group.grantRuntimePermissions(false, new String[]{permName}); 209 state.mState = GroupState.STATE_ALLOWED; 210 skipGroup = true; 211 212 reportRequestResult(permName, 213 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_GRANTED); 214 } 215 } break; 216 } 217 218 if (skipGroup && isFirstInstance) { 219 // Only allow to skip groups when this is the first time the dialog was created. 220 // Otherwise the number of groups changes between instances of the dialog. 221 state.mState = GroupState.STATE_SKIPPED; 222 } 223 } 224 225 /** 226 * Report the result of a grant of a permission. 227 * 228 * @param permission The permission that was granted or denied 229 * @param result The permission grant result 230 */ reportRequestResult(@onNull String permission, int result)231 private void reportRequestResult(@NonNull String permission, int result) { 232 boolean isImplicit = !ArrayUtils.contains(mRequestedPermissions, permission); 233 234 Log.v(LOG_TAG, 235 "Permission grant result requestId=" + mRequestId + " callingUid=" + mCallingUid 236 + " callingPackage=" + mCallingPackage + " permission=" + permission 237 + " isImplicit=" + isImplicit + " result=" + result); 238 239 PermissionControllerStatsLog.write( 240 PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED, mRequestId, 241 mCallingUid, mCallingPackage, permission, isImplicit, result); 242 } 243 244 /** 245 * Report the result of a grant of a permission. 246 * 247 * @param permissions The permissions that were granted or denied 248 * @param result The permission grant result 249 */ reportRequestResult(@onNull String[] permissions, int result)250 private void reportRequestResult(@NonNull String[] permissions, int result) { 251 for (String permission : permissions) { 252 reportRequestResult(permission, result); 253 } 254 } 255 256 @Override onCreate(Bundle icicle)257 public void onCreate(Bundle icicle) { 258 super.onCreate(icicle); 259 260 if (icicle == null) { 261 mRequestId = new Random().nextLong(); 262 } else { 263 mRequestId = icicle.getLong(KEY_REQUEST_ID); 264 } 265 266 getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); 267 268 // Cache this as this can only read on onCreate, not later. 269 mCallingPackage = getCallingPackage(); 270 271 setFinishOnTouchOutside(false); 272 273 setTitle(R.string.permission_request_title); 274 275 mRequestedPermissions = getIntent().getStringArrayExtra( 276 PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES); 277 if (mRequestedPermissions == null) { 278 mRequestedPermissions = new String[0]; 279 } 280 281 final int requestedPermCount = mRequestedPermissions.length; 282 283 if (requestedPermCount == 0) { 284 setResultAndFinish(); 285 return; 286 } 287 288 PackageInfo callingPackageInfo = getCallingPackageInfo(); 289 290 if (callingPackageInfo == null || callingPackageInfo.requestedPermissions == null 291 || callingPackageInfo.requestedPermissions.length <= 0) { 292 setResultAndFinish(); 293 return; 294 } 295 296 // Don't allow legacy apps to request runtime permissions. 297 if (callingPackageInfo.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) { 298 // Returning empty arrays means a cancellation. 299 mRequestedPermissions = new String[0]; 300 setResultAndFinish(); 301 return; 302 } 303 304 mCallingUid = callingPackageInfo.applicationInfo.uid; 305 306 UserHandle userHandle = UserHandle.getUserHandleForUid(mCallingUid); 307 308 if (DeviceUtils.isTelevision(this)) { 309 mViewHandler = new com.android.packageinstaller.permission.ui.television 310 .GrantPermissionsViewHandlerImpl(this, 311 mCallingPackage).setResultListener(this); 312 } else if (DeviceUtils.isWear(this)) { 313 mViewHandler = new GrantPermissionsWatchViewHandler(this).setResultListener(this); 314 } else if (DeviceUtils.isAuto(this)) { 315 mViewHandler = new GrantPermissionsAutoViewHandler(this, mCallingPackage, userHandle) 316 .setResultListener(this); 317 } else { 318 mViewHandler = new com.android.packageinstaller.permission.ui.handheld 319 .GrantPermissionsViewHandlerImpl(this, mCallingPackage, userHandle) 320 .setResultListener(this); 321 } 322 323 mAppPermissions = new AppPermissions(this, callingPackageInfo, false, 324 new Runnable() { 325 @Override 326 public void run() { 327 setResultAndFinish(); 328 } 329 }); 330 331 for (String requestedPermission : mRequestedPermissions) { 332 if (requestedPermission == null) { 333 continue; 334 } 335 336 ArrayList<String> affectedPermissions = 337 computeAffectedPermissions(requestedPermission); 338 339 int numAffectedPermissions = affectedPermissions.size(); 340 for (int i = 0; i < numAffectedPermissions; i++) { 341 AppPermissionGroup group = 342 mAppPermissions.getGroupForPermission(affectedPermissions.get(i)); 343 if (group == null) { 344 reportRequestResult(affectedPermissions.get(i), 345 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED); 346 347 continue; 348 } 349 350 addRequestedPermissions(group, affectedPermissions.get(i), icicle == null); 351 } 352 } 353 354 int numGroupStates = mRequestGrantPermissionGroups.size(); 355 for (int groupStateNum = 0; groupStateNum < numGroupStates; groupStateNum++) { 356 GroupState groupState = mRequestGrantPermissionGroups.valueAt(groupStateNum); 357 AppPermissionGroup group = groupState.mGroup; 358 359 // Restore permission group state after lifecycle events 360 if (icicle != null) { 361 groupState.mState = icicle.getInt( 362 getInstanceStateKey(mRequestGrantPermissionGroups.keyAt(groupStateNum)), 363 groupState.mState); 364 } 365 366 // Do not attempt to grant background access if foreground access is not either already 367 // granted or requested 368 if (group.isBackgroundGroup()) { 369 // Check if a foreground permission is already granted 370 boolean foregroundGroupAlreadyGranted = mAppPermissions.getPermissionGroup( 371 group.getName()).areRuntimePermissionsGranted(); 372 boolean hasForegroundRequest = (getForegroundGroupState(group.getName()) != null); 373 374 if (!foregroundGroupAlreadyGranted && !hasForegroundRequest) { 375 // The background permission cannot be granted at this time 376 int numPermissions = groupState.affectedPermissions.length; 377 for (int permissionNum = 0; permissionNum < numPermissions; permissionNum++) { 378 Log.w(LOG_TAG, 379 "Cannot grant " + groupState.affectedPermissions[permissionNum] 380 + " as the matching foreground permission is not already " 381 + "granted."); 382 } 383 384 groupState.mState = GroupState.STATE_SKIPPED; 385 386 reportRequestResult(groupState.affectedPermissions, 387 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED); 388 } 389 } 390 } 391 392 setContentView(mViewHandler.createView()); 393 394 Window window = getWindow(); 395 WindowManager.LayoutParams layoutParams = window.getAttributes(); 396 mViewHandler.updateWindowAttributes(layoutParams); 397 window.setAttributes(layoutParams); 398 399 // Restore UI state after lifecycle events. This has to be before 400 // showNextPermissionGroupGrantRequest is called. showNextPermissionGroupGrantRequest might 401 // update the UI and the UI behaves differently for updates and initial creations. 402 if (icicle != null) { 403 mViewHandler.loadInstanceState(icicle); 404 } 405 406 if (!showNextPermissionGroupGrantRequest()) { 407 setResultAndFinish(); 408 } 409 } 410 411 /** 412 * Update the {@link #mRequestedPermissions} if the system reports them as granted. 413 * 414 * <p>This also updates the {@link #mAppPermissions} state and switches to the next group grant 415 * request if the current group becomes granted. 416 */ updateIfPermissionsWereGranted()417 private void updateIfPermissionsWereGranted() { 418 PackageManager pm = getPackageManager(); 419 420 boolean mightShowNextGroup = true; 421 int numGroupStates = mRequestGrantPermissionGroups.size(); 422 for (int i = 0; i < numGroupStates; i++) { 423 GroupState groupState = mRequestGrantPermissionGroups.valueAt(i); 424 425 if (groupState == null || groupState.mState != GroupState.STATE_UNKNOWN) { 426 // Group has already been approved / denied via the UI by the user 427 continue; 428 } 429 430 boolean allAffectedPermissionsOfThisGroupAreGranted = true; 431 432 if (groupState.affectedPermissions == null) { 433 // It is not clear which permissions belong to this group, hence never skip this 434 // view 435 allAffectedPermissionsOfThisGroupAreGranted = false; 436 } else { 437 for (int permNum = 0; permNum < groupState.affectedPermissions.length; 438 permNum++) { 439 if (pm.checkPermission(groupState.affectedPermissions[permNum], mCallingPackage) 440 == PERMISSION_DENIED) { 441 allAffectedPermissionsOfThisGroupAreGranted = false; 442 break; 443 } 444 } 445 } 446 447 if (allAffectedPermissionsOfThisGroupAreGranted) { 448 groupState.mState = GroupState.STATE_ALLOWED; 449 450 if (mightShowNextGroup) { 451 // The UI currently displays the first group with 452 // mState == STATE_UNKNOWN. So we are switching to next group until we 453 // could not allow a group that was still unknown 454 if (!showNextPermissionGroupGrantRequest()) { 455 setResultAndFinish(); 456 } 457 } 458 } else { 459 mightShowNextGroup = false; 460 } 461 } 462 } 463 464 @Override onStart()465 protected void onStart() { 466 super.onStart(); 467 468 try { 469 mPermissionChangeListener = new PermissionChangeListener(); 470 } catch (NameNotFoundException e) { 471 setResultAndFinish(); 472 return; 473 } 474 PackageManager pm = getPackageManager(); 475 pm.addOnPermissionsChangeListener(mPermissionChangeListener); 476 477 // get notified when the package is removed 478 mPackageRemovalMonitor = new PackageRemovalMonitor(this, mCallingPackage) { 479 @Override 480 public void onPackageRemoved() { 481 Log.w(LOG_TAG, mCallingPackage + " was uninstalled"); 482 483 finish(); 484 } 485 }; 486 mPackageRemovalMonitor.register(); 487 488 // check if the package was removed while this activity was not started 489 try { 490 pm.getPackageInfo(mCallingPackage, 0); 491 } catch (NameNotFoundException e) { 492 Log.w(LOG_TAG, mCallingPackage + " was uninstalled while this activity was stopped", e); 493 finish(); 494 } 495 496 updateIfPermissionsWereGranted(); 497 } 498 499 @Override onStop()500 protected void onStop() { 501 super.onStop(); 502 503 if (mPackageRemovalMonitor != null) { 504 mPackageRemovalMonitor.unregister(); 505 mPackageRemovalMonitor = null; 506 } 507 508 if (mPermissionChangeListener != null) { 509 getPackageManager().removeOnPermissionsChangeListener(mPermissionChangeListener); 510 mPermissionChangeListener = null; 511 } 512 } 513 514 @Override dispatchTouchEvent(MotionEvent ev)515 public boolean dispatchTouchEvent(MotionEvent ev) { 516 View rootView = getWindow().getDecorView(); 517 if (rootView.getTop() != 0) { 518 // We are animating the top view, need to compensate for that in motion events. 519 ev.setLocation(ev.getX(), ev.getY() - rootView.getTop()); 520 } 521 return super.dispatchTouchEvent(ev); 522 } 523 524 /** 525 * Compose a key that stores the GroupState.mState in the instance state. 526 * 527 * @param requestGrantPermissionGroupsKey The key of the permission group 528 * 529 * @return A unique key to be used in the instance state 530 */ getInstanceStateKey( Pair<String, Boolean> requestGrantPermissionGroupsKey)531 private static String getInstanceStateKey( 532 Pair<String, Boolean> requestGrantPermissionGroupsKey) { 533 return GrantPermissionsActivity.class.getName() + "_" 534 + requestGrantPermissionGroupsKey.first + "_" 535 + requestGrantPermissionGroupsKey.second; 536 } 537 538 @Override onSaveInstanceState(Bundle outState)539 protected void onSaveInstanceState(Bundle outState) { 540 super.onSaveInstanceState(outState); 541 542 mViewHandler.saveInstanceState(outState); 543 544 outState.putLong(KEY_REQUEST_ID, mRequestId); 545 546 int numGroups = mRequestGrantPermissionGroups.size(); 547 for (int i = 0; i < numGroups; i++) { 548 int state = mRequestGrantPermissionGroups.valueAt(i).mState; 549 550 if (state != GroupState.STATE_UNKNOWN) { 551 outState.putInt(getInstanceStateKey(mRequestGrantPermissionGroups.keyAt(i)), state); 552 } 553 } 554 } 555 556 /** 557 * @return the background group state for the permission group with the {@code name} 558 */ getBackgroundGroupState(String name)559 private GroupState getBackgroundGroupState(String name) { 560 return mRequestGrantPermissionGroups.get(new Pair<>(name, true)); 561 } 562 563 /** 564 * @return the foreground group state for the permission group with the {@code name} 565 */ getForegroundGroupState(String name)566 private GroupState getForegroundGroupState(String name) { 567 return mRequestGrantPermissionGroups.get(new Pair<>(name, false)); 568 } 569 shouldShowRequestForGroupState(GroupState groupState)570 private boolean shouldShowRequestForGroupState(GroupState groupState) { 571 if (groupState.mState == GroupState.STATE_SKIPPED) { 572 return false; 573 } 574 575 GroupState foregroundGroup = getForegroundGroupState(groupState.mGroup.getName()); 576 if (groupState.mGroup.isBackgroundGroup() 577 && (foregroundGroup != null && shouldShowRequestForGroupState(foregroundGroup))) { 578 // If an app requests both foreground and background permissions of the same group, 579 // we only show one request 580 return false; 581 } 582 583 return true; 584 } 585 showNextPermissionGroupGrantRequest()586 private boolean showNextPermissionGroupGrantRequest() { 587 int numGroupStates = mRequestGrantPermissionGroups.size(); 588 int numGrantRequests = 0; 589 for (int i = 0; i < numGroupStates; i++) { 590 if (shouldShowRequestForGroupState(mRequestGrantPermissionGroups.valueAt(i))) { 591 numGrantRequests++; 592 } 593 } 594 595 int currentIndex = 0; 596 for (GroupState groupState : mRequestGrantPermissionGroups.values()) { 597 if (!shouldShowRequestForGroupState(groupState)) { 598 continue; 599 } 600 601 if (groupState.mState == GroupState.STATE_UNKNOWN) { 602 GroupState foregroundGroupState; 603 GroupState backgroundGroupState; 604 if (groupState.mGroup.isBackgroundGroup()) { 605 backgroundGroupState = groupState; 606 foregroundGroupState = getForegroundGroupState(groupState.mGroup.getName()); 607 } else { 608 foregroundGroupState = groupState; 609 backgroundGroupState = getBackgroundGroupState(groupState.mGroup.getName()); 610 } 611 612 CharSequence appLabel = mAppPermissions.getAppLabel(); 613 614 Icon icon; 615 try { 616 icon = Icon.createWithResource(groupState.mGroup.getIconPkg(), 617 groupState.mGroup.getIconResId()); 618 } catch (Resources.NotFoundException e) { 619 Log.e(LOG_TAG, "Cannot load icon for group" + groupState.mGroup.getName(), e); 620 icon = null; 621 } 622 623 // If no background permissions are granted yet, we need to ask for background 624 // permissions 625 boolean needBackgroundPermission = false; 626 boolean isBackgroundPermissionUserSet = false; 627 if (backgroundGroupState != null) { 628 if (!backgroundGroupState.mGroup.areRuntimePermissionsGranted()) { 629 needBackgroundPermission = true; 630 isBackgroundPermissionUserSet = backgroundGroupState.mGroup.isUserSet(); 631 } 632 } 633 634 // If no foreground permissions are granted yet, we need to ask for foreground 635 // permissions 636 boolean needForegroundPermission = false; 637 boolean isForegroundPermissionUserSet = false; 638 if (foregroundGroupState != null) { 639 if (!foregroundGroupState.mGroup.areRuntimePermissionsGranted()) { 640 needForegroundPermission = true; 641 isForegroundPermissionUserSet = foregroundGroupState.mGroup.isUserSet(); 642 } 643 } 644 645 // The button doesn't show when its label is null 646 mButtonLabels = new CharSequence[NUM_BUTTONS]; 647 mButtonLabels[LABEL_ALLOW_BUTTON] = getString(R.string.grant_dialog_button_allow); 648 mButtonLabels[LABEL_ALLOW_ALWAYS_BUTTON] = null; 649 mButtonLabels[LABEL_ALLOW_FOREGROUND_BUTTON] = null; 650 mButtonLabels[LABEL_DENY_BUTTON] = getString(R.string.grant_dialog_button_deny); 651 if (isForegroundPermissionUserSet || isBackgroundPermissionUserSet) { 652 mButtonLabels[LABEL_DENY_AND_DONT_ASK_AGAIN_BUTTON] = 653 getString(R.string.grant_dialog_button_deny_and_dont_ask_again); 654 } else { 655 mButtonLabels[LABEL_DENY_AND_DONT_ASK_AGAIN_BUTTON] = null; 656 } 657 658 int messageId; 659 int detailMessageId = 0; 660 if (needForegroundPermission) { 661 messageId = groupState.mGroup.getRequest(); 662 663 if (groupState.mGroup.hasPermissionWithBackgroundMode()) { 664 mButtonLabels[LABEL_ALLOW_BUTTON] = null; 665 mButtonLabels[LABEL_ALLOW_FOREGROUND_BUTTON] = 666 getString(R.string.grant_dialog_button_allow_foreground); 667 if (needBackgroundPermission) { 668 mButtonLabels[LABEL_ALLOW_ALWAYS_BUTTON] = 669 getString(R.string.grant_dialog_button_allow_always); 670 if (isForegroundPermissionUserSet || isBackgroundPermissionUserSet) { 671 mButtonLabels[LABEL_DENY_BUTTON] = null; 672 } 673 } 674 } else { 675 detailMessageId = groupState.mGroup.getRequestDetail(); 676 } 677 } else { 678 if (needBackgroundPermission) { 679 messageId = groupState.mGroup.getBackgroundRequest(); 680 detailMessageId = groupState.mGroup.getBackgroundRequestDetail(); 681 mButtonLabels[LABEL_ALLOW_BUTTON] = 682 getString(R.string.grant_dialog_button_allow_background); 683 mButtonLabels[LABEL_DENY_BUTTON] = 684 getString(R.string.grant_dialog_button_deny_background); 685 mButtonLabels[LABEL_DENY_AND_DONT_ASK_AGAIN_BUTTON] = 686 getString(R.string 687 .grant_dialog_button_deny_background_and_dont_ask_again); 688 } else { 689 // Not reached as the permissions should be auto-granted 690 return false; 691 } 692 } 693 694 CharSequence message = getRequestMessage(appLabel, groupState.mGroup, this, 695 messageId); 696 697 Spanned detailMessage = null; 698 if (detailMessageId != 0) { 699 try { 700 detailMessage = Html.fromHtml( 701 getPackageManager().getResourcesForApplication( 702 groupState.mGroup.getDeclaringPackage()).getString( 703 detailMessageId), 0); 704 } catch (NameNotFoundException ignored) { 705 } 706 } 707 708 // Set the permission message as the title so it can be announced. 709 setTitle(message); 710 711 mViewHandler.updateUi(groupState.mGroup.getName(), numGrantRequests, currentIndex, 712 icon, message, detailMessage, mButtonLabels); 713 714 return true; 715 } 716 717 if (groupState.mState != GroupState.STATE_SKIPPED) { 718 currentIndex++; 719 } 720 } 721 722 return false; 723 } 724 725 @Override onPermissionGrantResult(String name, @GrantPermissionsViewHandler.Result int result)726 public void onPermissionGrantResult(String name, 727 @GrantPermissionsViewHandler.Result int result) { 728 logGrantPermissionActivityButtons(name, result); 729 GroupState foregroundGroupState = getForegroundGroupState(name); 730 GroupState backgroundGroupState = getBackgroundGroupState(name); 731 732 if (result == GRANTED_ALWAYS || result == GRANTED_FOREGROUND_ONLY 733 || result == DENIED_DO_NOT_ASK_AGAIN) { 734 KeyguardManager kgm = getSystemService(KeyguardManager.class); 735 736 if (kgm.isDeviceLocked()) { 737 kgm.requestDismissKeyguard(this, new KeyguardManager.KeyguardDismissCallback() { 738 @Override 739 public void onDismissError() { 740 Log.e(LOG_TAG, "Cannot dismiss keyguard perm=" + name + " result=" 741 + result); 742 } 743 744 @Override 745 public void onDismissCancelled() { 746 // do nothing (i.e. stay at the current permission group) 747 } 748 749 @Override 750 public void onDismissSucceeded() { 751 // Now the keyguard is dismissed, hence the device is not locked 752 // anymore 753 onPermissionGrantResult(name, result); 754 } 755 }); 756 757 return; 758 } 759 } 760 761 switch (result) { 762 case GRANTED_ALWAYS : 763 if (foregroundGroupState != null) { 764 onPermissionGrantResultSingleState(foregroundGroupState, true, false); 765 } 766 if (backgroundGroupState != null) { 767 onPermissionGrantResultSingleState(backgroundGroupState, true, false); 768 } 769 break; 770 case GRANTED_FOREGROUND_ONLY : 771 if (foregroundGroupState != null) { 772 onPermissionGrantResultSingleState(foregroundGroupState, true, false); 773 } 774 if (backgroundGroupState != null) { 775 onPermissionGrantResultSingleState(backgroundGroupState, false, false); 776 } 777 break; 778 case DENIED : 779 if (foregroundGroupState != null) { 780 onPermissionGrantResultSingleState(foregroundGroupState, false, false); 781 } 782 if (backgroundGroupState != null) { 783 onPermissionGrantResultSingleState(backgroundGroupState, false, false); 784 } 785 break; 786 case DENIED_DO_NOT_ASK_AGAIN : 787 if (foregroundGroupState != null) { 788 onPermissionGrantResultSingleState(foregroundGroupState, false, true); 789 } 790 if (backgroundGroupState != null) { 791 onPermissionGrantResultSingleState(backgroundGroupState, false, true); 792 } 793 break; 794 } 795 796 if (!showNextPermissionGroupGrantRequest()) { 797 setResultAndFinish(); 798 } 799 } 800 801 /** 802 * Grants or revoked the affected permissions for a single {@link groupState}. 803 * 804 * @param groupState The group state with the permissions to grant/revoke 805 * @param granted {@code true} if the permissions should be granted, {@code false} if they 806 * should be revoked 807 * @param doNotAskAgain if the permissions should be revoked should be app be allowed to ask 808 * again for the same permissions? 809 */ onPermissionGrantResultSingleState(GroupState groupState, boolean granted, boolean doNotAskAgain)810 private void onPermissionGrantResultSingleState(GroupState groupState, boolean granted, 811 boolean doNotAskAgain) { 812 if (groupState != null && groupState.mGroup != null 813 && groupState.mState == GroupState.STATE_UNKNOWN) { 814 if (granted) { 815 groupState.mGroup.grantRuntimePermissions(doNotAskAgain, 816 groupState.affectedPermissions); 817 groupState.mState = GroupState.STATE_ALLOWED; 818 819 reportRequestResult(groupState.affectedPermissions, 820 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED); 821 } else { 822 groupState.mGroup.revokeRuntimePermissions(doNotAskAgain, 823 groupState.affectedPermissions); 824 groupState.mState = GroupState.STATE_DENIED; 825 826 reportRequestResult(groupState.affectedPermissions, doNotAskAgain 827 ? 828 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_WITH_PREJUDICE 829 : PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED); 830 } 831 } 832 } 833 834 @Override onKeyDown(int keyCode, KeyEvent event)835 public boolean onKeyDown(int keyCode, KeyEvent event) { 836 // We do not allow backing out. 837 return keyCode == KeyEvent.KEYCODE_BACK; 838 } 839 840 @Override onKeyUp(int keyCode, KeyEvent event)841 public boolean onKeyUp(int keyCode, KeyEvent event) { 842 // We do not allow backing out. 843 return keyCode == KeyEvent.KEYCODE_BACK; 844 } 845 846 @Override finish()847 public void finish() { 848 setResultIfNeeded(RESULT_CANCELED); 849 super.finish(); 850 } 851 getCallingPackageInfo()852 private PackageInfo getCallingPackageInfo() { 853 try { 854 return getPackageManager().getPackageInfo(mCallingPackage, 855 PackageManager.GET_PERMISSIONS); 856 } catch (NameNotFoundException e) { 857 Log.i(LOG_TAG, "No package: " + mCallingPackage, e); 858 return null; 859 } 860 } 861 setResultIfNeeded(int resultCode)862 private void setResultIfNeeded(int resultCode) { 863 if (!mResultSet) { 864 mResultSet = true; 865 logRequestedPermissionGroups(); 866 Intent result = new Intent(PackageManager.ACTION_REQUEST_PERMISSIONS); 867 result.putExtra(PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES, mRequestedPermissions); 868 869 PackageManager pm = getPackageManager(); 870 int numRequestedPermissions = mRequestedPermissions.length; 871 int[] grantResults = new int[numRequestedPermissions]; 872 for (int i = 0; i < numRequestedPermissions; i++) { 873 grantResults[i] = pm.checkPermission(mRequestedPermissions[i], mCallingPackage); 874 } 875 876 result.putExtra(PackageManager.EXTRA_REQUEST_PERMISSIONS_RESULTS, grantResults); 877 setResult(resultCode, result); 878 } 879 } 880 setResultAndFinish()881 private void setResultAndFinish() { 882 setResultIfNeeded(RESULT_OK); 883 finish(); 884 } 885 logRequestedPermissionGroups()886 private void logRequestedPermissionGroups() { 887 if (mRequestGrantPermissionGroups.isEmpty()) { 888 return; 889 } 890 891 final int groupCount = mRequestGrantPermissionGroups.size(); 892 List<AppPermissionGroup> groups = new ArrayList<>(groupCount); 893 for (GroupState groupState : mRequestGrantPermissionGroups.values()) { 894 groups.add(groupState.mGroup); 895 } 896 897 SafetyNetLogger.logPermissionsRequested(mAppPermissions.getPackageInfo(), groups); 898 } 899 900 /** 901 * Get the actually requested permissions when a permission is requested. 902 * 903 * <p>>In some cases requesting to grant a single permission requires the system to grant 904 * additional permissions. E.g. before N-MR1 a single permission of a group caused the whole 905 * group to be granted. Another case are permissions that are split into two. For apps that 906 * target an SDK before the split, this method automatically adds the split off permission. 907 * 908 * @param permission The requested permission 909 * 910 * @return The actually requested permissions 911 */ computeAffectedPermissions(String permission)912 private ArrayList<String> computeAffectedPermissions(String permission) { 913 int requestingAppTargetSDK = 914 mAppPermissions.getPackageInfo().applicationInfo.targetSdkVersion; 915 916 // If a permission is split, all permissions the original permission is split into are 917 // affected 918 ArrayList<String> extendedBySplitPerms = new ArrayList<>(); 919 extendedBySplitPerms.add(permission); 920 921 List<PermissionManager.SplitPermissionInfo> splitPerms = getSystemService( 922 PermissionManager.class).getSplitPermissions(); 923 int numSplitPerms = splitPerms.size(); 924 for (int i = 0; i < numSplitPerms; i++) { 925 PermissionManager.SplitPermissionInfo splitPerm = splitPerms.get(i); 926 927 if (requestingAppTargetSDK < splitPerm.getTargetSdk() 928 && permission.equals(splitPerm.getSplitPermission())) { 929 extendedBySplitPerms.addAll(splitPerm.getNewPermissions()); 930 } 931 } 932 933 // For <= N_MR1 apps all permissions of the groups of the requested permissions are affected 934 if (requestingAppTargetSDK <= Build.VERSION_CODES.N_MR1) { 935 ArrayList<String> extendedBySplitPermsAndGroup = new ArrayList<>(); 936 937 int numExtendedBySplitPerms = extendedBySplitPerms.size(); 938 for (int splitPermNum = 0; splitPermNum < numExtendedBySplitPerms; splitPermNum++) { 939 AppPermissionGroup group = mAppPermissions.getGroupForPermission( 940 extendedBySplitPerms.get(splitPermNum)); 941 942 if (group == null) { 943 continue; 944 } 945 946 ArrayList<Permission> permissionsInGroup = group.getPermissions(); 947 int numPermissionsInGroup = permissionsInGroup.size(); 948 for (int permNum = 0; permNum < numPermissionsInGroup; permNum++) { 949 extendedBySplitPermsAndGroup.add(permissionsInGroup.get(permNum).getName()); 950 } 951 } 952 953 return extendedBySplitPermsAndGroup; 954 } else { 955 return extendedBySplitPerms; 956 } 957 } 958 logGrantPermissionActivityButtons(String permissionGroupName, int grantResult)959 private void logGrantPermissionActivityButtons(String permissionGroupName, int grantResult) { 960 int clickedButton = 0; 961 int presentedButtons = getButtonState(); 962 switch (grantResult) { 963 case GRANTED_ALWAYS: 964 if ((presentedButtons & (1 << LABEL_ALLOW_BUTTON)) != 0) { 965 clickedButton = 1 << LABEL_ALLOW_BUTTON; 966 } else { 967 clickedButton = 1 << LABEL_ALLOW_ALWAYS_BUTTON; 968 } 969 break; 970 case GRANTED_FOREGROUND_ONLY: 971 clickedButton = 1 << LABEL_ALLOW_FOREGROUND_BUTTON; 972 break; 973 case DENIED: 974 clickedButton = 1 << LABEL_DENY_BUTTON; 975 break; 976 case DENIED_DO_NOT_ASK_AGAIN: 977 clickedButton = 1 << LABEL_DENY_AND_DONT_ASK_AGAIN_BUTTON; 978 break; 979 default: 980 break; 981 } 982 983 PermissionControllerStatsLog.write(GRANT_PERMISSIONS_ACTIVITY_BUTTON_ACTIONS, 984 permissionGroupName, mCallingUid, mCallingPackage, presentedButtons, 985 clickedButton); 986 Log.v(LOG_TAG, "Logged buttons presented and clicked permissionGroupName=" 987 + permissionGroupName + " uid=" + mCallingUid + " package=" + mCallingPackage 988 + " presentedButtons=" + presentedButtons + " clickedButton=" + clickedButton); 989 } 990 getButtonState()991 private int getButtonState() { 992 if (mButtonLabels == null) { 993 return 0; 994 } 995 int buttonState = 0; 996 for (int i = NUM_BUTTONS - 1; i >= 0; i--) { 997 buttonState *= 2; 998 if (mButtonLabels[i] != null) { 999 buttonState++; 1000 } 1001 } 1002 return buttonState; 1003 } 1004 1005 private static final class GroupState { 1006 static final int STATE_UNKNOWN = 0; 1007 static final int STATE_ALLOWED = 1; 1008 static final int STATE_DENIED = 2; 1009 static final int STATE_SKIPPED = 3; 1010 1011 final AppPermissionGroup mGroup; 1012 int mState = STATE_UNKNOWN; 1013 1014 /** Permissions of this group that need to be granted, null == no permissions of group */ 1015 String[] affectedPermissions; 1016 GroupState(AppPermissionGroup group)1017 GroupState(AppPermissionGroup group) { 1018 mGroup = group; 1019 } 1020 } 1021 1022 private class PermissionChangeListener implements PackageManager.OnPermissionsChangedListener { 1023 final int mCallingPackageUid; 1024 PermissionChangeListener()1025 PermissionChangeListener() throws NameNotFoundException { 1026 mCallingPackageUid = getPackageManager().getPackageUid(mCallingPackage, 0); 1027 } 1028 1029 @Override onPermissionsChanged(int uid)1030 public void onPermissionsChanged(int uid) { 1031 if (uid == mCallingPackageUid) { 1032 updateIfPermissionsWereGranted(); 1033 } 1034 } 1035 } 1036 } 1037