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 SafetyNetLogger.logIfHasUndefinedPermissionGroup(getPackageManager(), mCallingPackage); 272 273 setFinishOnTouchOutside(false); 274 275 setTitle(R.string.permission_request_title); 276 277 mRequestedPermissions = getIntent().getStringArrayExtra( 278 PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES); 279 if (mRequestedPermissions == null) { 280 mRequestedPermissions = new String[0]; 281 } 282 283 final int requestedPermCount = mRequestedPermissions.length; 284 285 if (requestedPermCount == 0) { 286 setResultAndFinish(); 287 return; 288 } 289 290 PackageInfo callingPackageInfo = getCallingPackageInfo(); 291 292 if (callingPackageInfo == null || callingPackageInfo.requestedPermissions == null 293 || callingPackageInfo.requestedPermissions.length <= 0) { 294 setResultAndFinish(); 295 return; 296 } 297 298 // Don't allow legacy apps to request runtime permissions. 299 if (callingPackageInfo.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) { 300 // Returning empty arrays means a cancellation. 301 mRequestedPermissions = new String[0]; 302 setResultAndFinish(); 303 return; 304 } 305 306 mCallingUid = callingPackageInfo.applicationInfo.uid; 307 308 UserHandle userHandle = UserHandle.getUserHandleForUid(mCallingUid); 309 310 if (DeviceUtils.isTelevision(this)) { 311 mViewHandler = new com.android.packageinstaller.permission.ui.television 312 .GrantPermissionsViewHandlerImpl(this, 313 mCallingPackage).setResultListener(this); 314 } else if (DeviceUtils.isWear(this)) { 315 mViewHandler = new GrantPermissionsWatchViewHandler(this).setResultListener(this); 316 } else if (DeviceUtils.isAuto(this)) { 317 mViewHandler = new GrantPermissionsAutoViewHandler(this, mCallingPackage, userHandle) 318 .setResultListener(this); 319 } else { 320 mViewHandler = new com.android.packageinstaller.permission.ui.handheld 321 .GrantPermissionsViewHandlerImpl(this, mCallingPackage, userHandle) 322 .setResultListener(this); 323 } 324 325 mAppPermissions = new AppPermissions(this, callingPackageInfo, false, 326 new Runnable() { 327 @Override 328 public void run() { 329 setResultAndFinish(); 330 } 331 }); 332 333 for (String requestedPermission : mRequestedPermissions) { 334 if (requestedPermission == null) { 335 continue; 336 } 337 338 ArrayList<String> affectedPermissions = 339 computeAffectedPermissions(requestedPermission); 340 341 int numAffectedPermissions = affectedPermissions.size(); 342 for (int i = 0; i < numAffectedPermissions; i++) { 343 AppPermissionGroup group = 344 mAppPermissions.getGroupForPermission(affectedPermissions.get(i)); 345 if (group == null) { 346 reportRequestResult(affectedPermissions.get(i), 347 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED); 348 349 continue; 350 } 351 352 addRequestedPermissions(group, affectedPermissions.get(i), icicle == null); 353 } 354 } 355 356 int numGroupStates = mRequestGrantPermissionGroups.size(); 357 for (int groupStateNum = 0; groupStateNum < numGroupStates; groupStateNum++) { 358 GroupState groupState = mRequestGrantPermissionGroups.valueAt(groupStateNum); 359 AppPermissionGroup group = groupState.mGroup; 360 361 // Restore permission group state after lifecycle events 362 if (icicle != null) { 363 groupState.mState = icicle.getInt( 364 getInstanceStateKey(mRequestGrantPermissionGroups.keyAt(groupStateNum)), 365 groupState.mState); 366 } 367 368 // Do not attempt to grant background access if foreground access is not either already 369 // granted or requested 370 if (group.isBackgroundGroup()) { 371 // Check if a foreground permission is already granted 372 boolean foregroundGroupAlreadyGranted = mAppPermissions.getPermissionGroup( 373 group.getName()).areRuntimePermissionsGranted(); 374 boolean hasForegroundRequest = (getForegroundGroupState(group.getName()) != null); 375 376 if (!foregroundGroupAlreadyGranted && !hasForegroundRequest) { 377 // The background permission cannot be granted at this time 378 int numPermissions = groupState.affectedPermissions.length; 379 for (int permissionNum = 0; permissionNum < numPermissions; permissionNum++) { 380 Log.w(LOG_TAG, 381 "Cannot grant " + groupState.affectedPermissions[permissionNum] 382 + " as the matching foreground permission is not already " 383 + "granted."); 384 } 385 386 groupState.mState = GroupState.STATE_SKIPPED; 387 388 reportRequestResult(groupState.affectedPermissions, 389 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED); 390 } 391 } 392 } 393 394 setContentView(mViewHandler.createView()); 395 396 Window window = getWindow(); 397 WindowManager.LayoutParams layoutParams = window.getAttributes(); 398 mViewHandler.updateWindowAttributes(layoutParams); 399 window.setAttributes(layoutParams); 400 401 // Restore UI state after lifecycle events. This has to be before 402 // showNextPermissionGroupGrantRequest is called. showNextPermissionGroupGrantRequest might 403 // update the UI and the UI behaves differently for updates and initial creations. 404 if (icicle != null) { 405 mViewHandler.loadInstanceState(icicle); 406 } 407 408 if (!showNextPermissionGroupGrantRequest()) { 409 setResultAndFinish(); 410 } 411 } 412 413 /** 414 * Update the {@link #mRequestedPermissions} if the system reports them as granted. 415 * 416 * <p>This also updates the {@link #mAppPermissions} state and switches to the next group grant 417 * request if the current group becomes granted. 418 */ updateIfPermissionsWereGranted()419 private void updateIfPermissionsWereGranted() { 420 PackageManager pm = getPackageManager(); 421 422 boolean mightShowNextGroup = true; 423 int numGroupStates = mRequestGrantPermissionGroups.size(); 424 for (int i = 0; i < numGroupStates; i++) { 425 GroupState groupState = mRequestGrantPermissionGroups.valueAt(i); 426 427 if (groupState == null || groupState.mState != GroupState.STATE_UNKNOWN) { 428 // Group has already been approved / denied via the UI by the user 429 continue; 430 } 431 432 boolean allAffectedPermissionsOfThisGroupAreGranted = true; 433 434 if (groupState.affectedPermissions == null) { 435 // It is not clear which permissions belong to this group, hence never skip this 436 // view 437 allAffectedPermissionsOfThisGroupAreGranted = false; 438 } else { 439 for (int permNum = 0; permNum < groupState.affectedPermissions.length; 440 permNum++) { 441 if (pm.checkPermission(groupState.affectedPermissions[permNum], mCallingPackage) 442 == PERMISSION_DENIED) { 443 allAffectedPermissionsOfThisGroupAreGranted = false; 444 break; 445 } 446 } 447 } 448 449 if (allAffectedPermissionsOfThisGroupAreGranted) { 450 groupState.mState = GroupState.STATE_ALLOWED; 451 452 if (mightShowNextGroup) { 453 // The UI currently displays the first group with 454 // mState == STATE_UNKNOWN. So we are switching to next group until we 455 // could not allow a group that was still unknown 456 if (!showNextPermissionGroupGrantRequest()) { 457 setResultAndFinish(); 458 } 459 } 460 } else { 461 mightShowNextGroup = false; 462 } 463 } 464 } 465 466 @Override onStart()467 protected void onStart() { 468 super.onStart(); 469 470 try { 471 mPermissionChangeListener = new PermissionChangeListener(); 472 } catch (NameNotFoundException e) { 473 setResultAndFinish(); 474 return; 475 } 476 PackageManager pm = getPackageManager(); 477 pm.addOnPermissionsChangeListener(mPermissionChangeListener); 478 479 // get notified when the package is removed 480 mPackageRemovalMonitor = new PackageRemovalMonitor(this, mCallingPackage) { 481 @Override 482 public void onPackageRemoved() { 483 Log.w(LOG_TAG, mCallingPackage + " was uninstalled"); 484 485 finish(); 486 } 487 }; 488 mPackageRemovalMonitor.register(); 489 490 // check if the package was removed while this activity was not started 491 try { 492 pm.getPackageInfo(mCallingPackage, 0); 493 } catch (NameNotFoundException e) { 494 Log.w(LOG_TAG, mCallingPackage + " was uninstalled while this activity was stopped", e); 495 finish(); 496 } 497 498 updateIfPermissionsWereGranted(); 499 } 500 501 @Override onStop()502 protected void onStop() { 503 super.onStop(); 504 505 if (mPackageRemovalMonitor != null) { 506 mPackageRemovalMonitor.unregister(); 507 mPackageRemovalMonitor = null; 508 } 509 510 if (mPermissionChangeListener != null) { 511 getPackageManager().removeOnPermissionsChangeListener(mPermissionChangeListener); 512 mPermissionChangeListener = null; 513 } 514 } 515 516 @Override dispatchTouchEvent(MotionEvent ev)517 public boolean dispatchTouchEvent(MotionEvent ev) { 518 View rootView = getWindow().getDecorView(); 519 if (rootView.getTop() != 0) { 520 // We are animating the top view, need to compensate for that in motion events. 521 ev.setLocation(ev.getX(), ev.getY() - rootView.getTop()); 522 } 523 return super.dispatchTouchEvent(ev); 524 } 525 526 /** 527 * Compose a key that stores the GroupState.mState in the instance state. 528 * 529 * @param requestGrantPermissionGroupsKey The key of the permission group 530 * 531 * @return A unique key to be used in the instance state 532 */ getInstanceStateKey( Pair<String, Boolean> requestGrantPermissionGroupsKey)533 private static String getInstanceStateKey( 534 Pair<String, Boolean> requestGrantPermissionGroupsKey) { 535 return GrantPermissionsActivity.class.getName() + "_" 536 + requestGrantPermissionGroupsKey.first + "_" 537 + requestGrantPermissionGroupsKey.second; 538 } 539 540 @Override onSaveInstanceState(Bundle outState)541 protected void onSaveInstanceState(Bundle outState) { 542 super.onSaveInstanceState(outState); 543 544 mViewHandler.saveInstanceState(outState); 545 546 outState.putLong(KEY_REQUEST_ID, mRequestId); 547 548 int numGroups = mRequestGrantPermissionGroups.size(); 549 for (int i = 0; i < numGroups; i++) { 550 int state = mRequestGrantPermissionGroups.valueAt(i).mState; 551 552 if (state != GroupState.STATE_UNKNOWN) { 553 outState.putInt(getInstanceStateKey(mRequestGrantPermissionGroups.keyAt(i)), state); 554 } 555 } 556 } 557 558 /** 559 * @return the background group state for the permission group with the {@code name} 560 */ getBackgroundGroupState(String name)561 private GroupState getBackgroundGroupState(String name) { 562 return mRequestGrantPermissionGroups.get(new Pair<>(name, true)); 563 } 564 565 /** 566 * @return the foreground group state for the permission group with the {@code name} 567 */ getForegroundGroupState(String name)568 private GroupState getForegroundGroupState(String name) { 569 return mRequestGrantPermissionGroups.get(new Pair<>(name, false)); 570 } 571 shouldShowRequestForGroupState(GroupState groupState)572 private boolean shouldShowRequestForGroupState(GroupState groupState) { 573 if (groupState.mState == GroupState.STATE_SKIPPED) { 574 return false; 575 } 576 577 GroupState foregroundGroup = getForegroundGroupState(groupState.mGroup.getName()); 578 if (groupState.mGroup.isBackgroundGroup() 579 && (foregroundGroup != null && shouldShowRequestForGroupState(foregroundGroup))) { 580 // If an app requests both foreground and background permissions of the same group, 581 // we only show one request 582 return false; 583 } 584 585 return true; 586 } 587 showNextPermissionGroupGrantRequest()588 private boolean showNextPermissionGroupGrantRequest() { 589 int numGroupStates = mRequestGrantPermissionGroups.size(); 590 int numGrantRequests = 0; 591 for (int i = 0; i < numGroupStates; i++) { 592 if (shouldShowRequestForGroupState(mRequestGrantPermissionGroups.valueAt(i))) { 593 numGrantRequests++; 594 } 595 } 596 597 int currentIndex = 0; 598 for (GroupState groupState : mRequestGrantPermissionGroups.values()) { 599 if (!shouldShowRequestForGroupState(groupState)) { 600 continue; 601 } 602 603 if (groupState.mState == GroupState.STATE_UNKNOWN) { 604 GroupState foregroundGroupState; 605 GroupState backgroundGroupState; 606 if (groupState.mGroup.isBackgroundGroup()) { 607 backgroundGroupState = groupState; 608 foregroundGroupState = getForegroundGroupState(groupState.mGroup.getName()); 609 } else { 610 foregroundGroupState = groupState; 611 backgroundGroupState = getBackgroundGroupState(groupState.mGroup.getName()); 612 } 613 614 CharSequence appLabel = mAppPermissions.getAppLabel(); 615 616 Icon icon; 617 try { 618 icon = Icon.createWithResource(groupState.mGroup.getIconPkg(), 619 groupState.mGroup.getIconResId()); 620 } catch (Resources.NotFoundException e) { 621 Log.e(LOG_TAG, "Cannot load icon for group" + groupState.mGroup.getName(), e); 622 icon = null; 623 } 624 625 // If no background permissions are granted yet, we need to ask for background 626 // permissions 627 boolean needBackgroundPermission = false; 628 boolean isBackgroundPermissionUserSet = false; 629 if (backgroundGroupState != null) { 630 if (!backgroundGroupState.mGroup.areRuntimePermissionsGranted()) { 631 needBackgroundPermission = true; 632 isBackgroundPermissionUserSet = backgroundGroupState.mGroup.isUserSet(); 633 } 634 } 635 636 // If no foreground permissions are granted yet, we need to ask for foreground 637 // permissions 638 boolean needForegroundPermission = false; 639 boolean isForegroundPermissionUserSet = false; 640 if (foregroundGroupState != null) { 641 if (!foregroundGroupState.mGroup.areRuntimePermissionsGranted()) { 642 needForegroundPermission = true; 643 isForegroundPermissionUserSet = foregroundGroupState.mGroup.isUserSet(); 644 } 645 } 646 647 // The button doesn't show when its label is null 648 mButtonLabels = new CharSequence[NUM_BUTTONS]; 649 mButtonLabels[LABEL_ALLOW_BUTTON] = getString(R.string.grant_dialog_button_allow); 650 mButtonLabels[LABEL_ALLOW_ALWAYS_BUTTON] = null; 651 mButtonLabels[LABEL_ALLOW_FOREGROUND_BUTTON] = null; 652 mButtonLabels[LABEL_DENY_BUTTON] = getString(R.string.grant_dialog_button_deny); 653 if (isForegroundPermissionUserSet || isBackgroundPermissionUserSet) { 654 mButtonLabels[LABEL_DENY_AND_DONT_ASK_AGAIN_BUTTON] = 655 getString(R.string.grant_dialog_button_deny_and_dont_ask_again); 656 } else { 657 mButtonLabels[LABEL_DENY_AND_DONT_ASK_AGAIN_BUTTON] = null; 658 } 659 660 int messageId; 661 int detailMessageId = 0; 662 if (needForegroundPermission) { 663 messageId = groupState.mGroup.getRequest(); 664 665 if (groupState.mGroup.hasPermissionWithBackgroundMode()) { 666 mButtonLabels[LABEL_ALLOW_BUTTON] = null; 667 mButtonLabels[LABEL_ALLOW_FOREGROUND_BUTTON] = 668 getString(R.string.grant_dialog_button_allow_foreground); 669 if (needBackgroundPermission) { 670 mButtonLabels[LABEL_ALLOW_ALWAYS_BUTTON] = 671 getString(R.string.grant_dialog_button_allow_always); 672 if (isForegroundPermissionUserSet || isBackgroundPermissionUserSet) { 673 mButtonLabels[LABEL_DENY_BUTTON] = null; 674 } 675 } 676 } else { 677 detailMessageId = groupState.mGroup.getRequestDetail(); 678 } 679 } else { 680 if (needBackgroundPermission) { 681 messageId = groupState.mGroup.getBackgroundRequest(); 682 detailMessageId = groupState.mGroup.getBackgroundRequestDetail(); 683 mButtonLabels[LABEL_ALLOW_BUTTON] = 684 getString(R.string.grant_dialog_button_allow_background); 685 mButtonLabels[LABEL_DENY_BUTTON] = 686 getString(R.string.grant_dialog_button_deny_background); 687 mButtonLabels[LABEL_DENY_AND_DONT_ASK_AGAIN_BUTTON] = 688 getString(R.string 689 .grant_dialog_button_deny_background_and_dont_ask_again); 690 } else { 691 // Not reached as the permissions should be auto-granted 692 return false; 693 } 694 } 695 696 CharSequence message = getRequestMessage(appLabel, groupState.mGroup, this, 697 messageId); 698 699 Spanned detailMessage = null; 700 if (detailMessageId != 0) { 701 try { 702 detailMessage = Html.fromHtml( 703 getPackageManager().getResourcesForApplication( 704 groupState.mGroup.getDeclaringPackage()).getString( 705 detailMessageId), 0); 706 } catch (NameNotFoundException ignored) { 707 } 708 } 709 710 // Set the permission message as the title so it can be announced. 711 setTitle(message); 712 713 mViewHandler.updateUi(groupState.mGroup.getName(), numGrantRequests, currentIndex, 714 icon, message, detailMessage, mButtonLabels); 715 716 return true; 717 } 718 719 if (groupState.mState != GroupState.STATE_SKIPPED) { 720 currentIndex++; 721 } 722 } 723 724 return false; 725 } 726 727 @Override onPermissionGrantResult(String name, @GrantPermissionsViewHandler.Result int result)728 public void onPermissionGrantResult(String name, 729 @GrantPermissionsViewHandler.Result int result) { 730 logGrantPermissionActivityButtons(name, result); 731 GroupState foregroundGroupState = getForegroundGroupState(name); 732 GroupState backgroundGroupState = getBackgroundGroupState(name); 733 734 if (result == GRANTED_ALWAYS || result == GRANTED_FOREGROUND_ONLY 735 || result == DENIED_DO_NOT_ASK_AGAIN) { 736 KeyguardManager kgm = getSystemService(KeyguardManager.class); 737 738 if (kgm.isDeviceLocked()) { 739 kgm.requestDismissKeyguard(this, new KeyguardManager.KeyguardDismissCallback() { 740 @Override 741 public void onDismissError() { 742 Log.e(LOG_TAG, "Cannot dismiss keyguard perm=" + name + " result=" 743 + result); 744 } 745 746 @Override 747 public void onDismissCancelled() { 748 // do nothing (i.e. stay at the current permission group) 749 } 750 751 @Override 752 public void onDismissSucceeded() { 753 // Now the keyguard is dismissed, hence the device is not locked 754 // anymore 755 onPermissionGrantResult(name, result); 756 } 757 }); 758 759 return; 760 } 761 } 762 763 switch (result) { 764 case GRANTED_ALWAYS : 765 if (foregroundGroupState != null) { 766 onPermissionGrantResultSingleState(foregroundGroupState, true, false); 767 } 768 if (backgroundGroupState != null) { 769 onPermissionGrantResultSingleState(backgroundGroupState, true, false); 770 } 771 break; 772 case GRANTED_FOREGROUND_ONLY : 773 if (foregroundGroupState != null) { 774 onPermissionGrantResultSingleState(foregroundGroupState, true, false); 775 } 776 if (backgroundGroupState != null) { 777 onPermissionGrantResultSingleState(backgroundGroupState, false, false); 778 } 779 break; 780 case DENIED : 781 if (foregroundGroupState != null) { 782 onPermissionGrantResultSingleState(foregroundGroupState, false, false); 783 } 784 if (backgroundGroupState != null) { 785 onPermissionGrantResultSingleState(backgroundGroupState, false, false); 786 } 787 break; 788 case DENIED_DO_NOT_ASK_AGAIN : 789 if (foregroundGroupState != null) { 790 onPermissionGrantResultSingleState(foregroundGroupState, false, true); 791 } 792 if (backgroundGroupState != null) { 793 onPermissionGrantResultSingleState(backgroundGroupState, false, true); 794 } 795 break; 796 } 797 798 if (!showNextPermissionGroupGrantRequest()) { 799 setResultAndFinish(); 800 } 801 } 802 803 /** 804 * Grants or revoked the affected permissions for a single {@link groupState}. 805 * 806 * @param groupState The group state with the permissions to grant/revoke 807 * @param granted {@code true} if the permissions should be granted, {@code false} if they 808 * should be revoked 809 * @param doNotAskAgain if the permissions should be revoked should be app be allowed to ask 810 * again for the same permissions? 811 */ onPermissionGrantResultSingleState(GroupState groupState, boolean granted, boolean doNotAskAgain)812 private void onPermissionGrantResultSingleState(GroupState groupState, boolean granted, 813 boolean doNotAskAgain) { 814 if (groupState != null && groupState.mGroup != null 815 && groupState.mState == GroupState.STATE_UNKNOWN) { 816 if (granted) { 817 groupState.mGroup.grantRuntimePermissions(doNotAskAgain, 818 groupState.affectedPermissions); 819 groupState.mState = GroupState.STATE_ALLOWED; 820 821 reportRequestResult(groupState.affectedPermissions, 822 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED); 823 } else { 824 groupState.mGroup.revokeRuntimePermissions(doNotAskAgain, 825 groupState.affectedPermissions); 826 groupState.mState = GroupState.STATE_DENIED; 827 828 reportRequestResult(groupState.affectedPermissions, doNotAskAgain 829 ? 830 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_WITH_PREJUDICE 831 : PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED); 832 } 833 } 834 } 835 836 @Override onKeyDown(int keyCode, KeyEvent event)837 public boolean onKeyDown(int keyCode, KeyEvent event) { 838 // We do not allow backing out. 839 return keyCode == KeyEvent.KEYCODE_BACK; 840 } 841 842 @Override onKeyUp(int keyCode, KeyEvent event)843 public boolean onKeyUp(int keyCode, KeyEvent event) { 844 // We do not allow backing out. 845 return keyCode == KeyEvent.KEYCODE_BACK; 846 } 847 848 @Override finish()849 public void finish() { 850 setResultIfNeeded(RESULT_CANCELED); 851 super.finish(); 852 } 853 getCallingPackageInfo()854 private PackageInfo getCallingPackageInfo() { 855 try { 856 return getPackageManager().getPackageInfo(mCallingPackage, 857 PackageManager.GET_PERMISSIONS); 858 } catch (NameNotFoundException e) { 859 Log.i(LOG_TAG, "No package: " + mCallingPackage, e); 860 return null; 861 } 862 } 863 setResultIfNeeded(int resultCode)864 private void setResultIfNeeded(int resultCode) { 865 if (!mResultSet) { 866 mResultSet = true; 867 logRequestedPermissionGroups(); 868 Intent result = new Intent(PackageManager.ACTION_REQUEST_PERMISSIONS); 869 result.putExtra(PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES, mRequestedPermissions); 870 871 PackageManager pm = getPackageManager(); 872 int numRequestedPermissions = mRequestedPermissions.length; 873 int[] grantResults = new int[numRequestedPermissions]; 874 for (int i = 0; i < numRequestedPermissions; i++) { 875 grantResults[i] = pm.checkPermission(mRequestedPermissions[i], mCallingPackage); 876 } 877 878 result.putExtra(PackageManager.EXTRA_REQUEST_PERMISSIONS_RESULTS, grantResults); 879 setResult(resultCode, result); 880 } 881 } 882 setResultAndFinish()883 private void setResultAndFinish() { 884 setResultIfNeeded(RESULT_OK); 885 finish(); 886 } 887 logRequestedPermissionGroups()888 private void logRequestedPermissionGroups() { 889 if (mRequestGrantPermissionGroups.isEmpty()) { 890 return; 891 } 892 893 final int groupCount = mRequestGrantPermissionGroups.size(); 894 List<AppPermissionGroup> groups = new ArrayList<>(groupCount); 895 for (GroupState groupState : mRequestGrantPermissionGroups.values()) { 896 groups.add(groupState.mGroup); 897 } 898 899 SafetyNetLogger.logPermissionsRequested(mAppPermissions.getPackageInfo(), groups); 900 } 901 902 /** 903 * Get the actually requested permissions when a permission is requested. 904 * 905 * <p>>In some cases requesting to grant a single permission requires the system to grant 906 * additional permissions. E.g. before N-MR1 a single permission of a group caused the whole 907 * group to be granted. Another case are permissions that are split into two. For apps that 908 * target an SDK before the split, this method automatically adds the split off permission. 909 * 910 * @param permission The requested permission 911 * 912 * @return The actually requested permissions 913 */ computeAffectedPermissions(String permission)914 private ArrayList<String> computeAffectedPermissions(String permission) { 915 int requestingAppTargetSDK = 916 mAppPermissions.getPackageInfo().applicationInfo.targetSdkVersion; 917 918 // If a permission is split, all permissions the original permission is split into are 919 // affected 920 ArrayList<String> extendedBySplitPerms = new ArrayList<>(); 921 extendedBySplitPerms.add(permission); 922 923 List<PermissionManager.SplitPermissionInfo> splitPerms = getSystemService( 924 PermissionManager.class).getSplitPermissions(); 925 int numSplitPerms = splitPerms.size(); 926 for (int i = 0; i < numSplitPerms; i++) { 927 PermissionManager.SplitPermissionInfo splitPerm = splitPerms.get(i); 928 929 if (requestingAppTargetSDK < splitPerm.getTargetSdk() 930 && permission.equals(splitPerm.getSplitPermission())) { 931 extendedBySplitPerms.addAll(splitPerm.getNewPermissions()); 932 } 933 } 934 935 // For <= N_MR1 apps all permissions of the groups of the requested permissions are affected 936 if (requestingAppTargetSDK <= Build.VERSION_CODES.N_MR1) { 937 ArrayList<String> extendedBySplitPermsAndGroup = new ArrayList<>(); 938 939 int numExtendedBySplitPerms = extendedBySplitPerms.size(); 940 for (int splitPermNum = 0; splitPermNum < numExtendedBySplitPerms; splitPermNum++) { 941 AppPermissionGroup group = mAppPermissions.getGroupForPermission( 942 extendedBySplitPerms.get(splitPermNum)); 943 944 if (group == null) { 945 continue; 946 } 947 948 ArrayList<Permission> permissionsInGroup = group.getPermissions(); 949 int numPermissionsInGroup = permissionsInGroup.size(); 950 for (int permNum = 0; permNum < numPermissionsInGroup; permNum++) { 951 extendedBySplitPermsAndGroup.add(permissionsInGroup.get(permNum).getName()); 952 } 953 } 954 955 return extendedBySplitPermsAndGroup; 956 } else { 957 return extendedBySplitPerms; 958 } 959 } 960 logGrantPermissionActivityButtons(String permissionGroupName, int grantResult)961 private void logGrantPermissionActivityButtons(String permissionGroupName, int grantResult) { 962 int clickedButton = 0; 963 int presentedButtons = getButtonState(); 964 switch (grantResult) { 965 case GRANTED_ALWAYS: 966 if ((presentedButtons & (1 << LABEL_ALLOW_BUTTON)) != 0) { 967 clickedButton = 1 << LABEL_ALLOW_BUTTON; 968 } else { 969 clickedButton = 1 << LABEL_ALLOW_ALWAYS_BUTTON; 970 } 971 break; 972 case GRANTED_FOREGROUND_ONLY: 973 clickedButton = 1 << LABEL_ALLOW_FOREGROUND_BUTTON; 974 break; 975 case DENIED: 976 clickedButton = 1 << LABEL_DENY_BUTTON; 977 break; 978 case DENIED_DO_NOT_ASK_AGAIN: 979 clickedButton = 1 << LABEL_DENY_AND_DONT_ASK_AGAIN_BUTTON; 980 break; 981 default: 982 break; 983 } 984 985 PermissionControllerStatsLog.write(GRANT_PERMISSIONS_ACTIVITY_BUTTON_ACTIONS, 986 permissionGroupName, mCallingUid, mCallingPackage, presentedButtons, 987 clickedButton); 988 Log.v(LOG_TAG, "Logged buttons presented and clicked permissionGroupName=" 989 + permissionGroupName + " uid=" + mCallingUid + " package=" + mCallingPackage 990 + " presentedButtons=" + presentedButtons + " clickedButton=" + clickedButton); 991 } 992 getButtonState()993 private int getButtonState() { 994 if (mButtonLabels == null) { 995 return 0; 996 } 997 int buttonState = 0; 998 for (int i = NUM_BUTTONS - 1; i >= 0; i--) { 999 buttonState *= 2; 1000 if (mButtonLabels[i] != null) { 1001 buttonState++; 1002 } 1003 } 1004 return buttonState; 1005 } 1006 1007 private static final class GroupState { 1008 static final int STATE_UNKNOWN = 0; 1009 static final int STATE_ALLOWED = 1; 1010 static final int STATE_DENIED = 2; 1011 static final int STATE_SKIPPED = 3; 1012 1013 final AppPermissionGroup mGroup; 1014 int mState = STATE_UNKNOWN; 1015 1016 /** Permissions of this group that need to be granted, null == no permissions of group */ 1017 String[] affectedPermissions; 1018 GroupState(AppPermissionGroup group)1019 GroupState(AppPermissionGroup group) { 1020 mGroup = group; 1021 } 1022 } 1023 1024 private class PermissionChangeListener implements PackageManager.OnPermissionsChangedListener { 1025 final int mCallingPackageUid; 1026 PermissionChangeListener()1027 PermissionChangeListener() throws NameNotFoundException { 1028 mCallingPackageUid = getPackageManager().getPackageUid(mCallingPackage, 0); 1029 } 1030 1031 @Override onPermissionsChanged(int uid)1032 public void onPermissionsChanged(int uid) { 1033 if (uid == mCallingPackageUid) { 1034 updateIfPermissionsWereGranted(); 1035 } 1036 } 1037 } 1038 } 1039