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.permissioncontroller.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.permissioncontroller.PermissionControllerStatsLog.GRANT_PERMISSIONS_ACTIVITY_BUTTON_ACTIONS; 23 import static com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_DENIED; 24 import static com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_GRANTED; 25 import static com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED; 26 import static com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_POLICY_FIXED; 27 import static com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_RESTRICTED_PERMISSION; 28 import static com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_USER_FIXED; 29 import static com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED; 30 import static com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_IN_SETTINGS; 31 import static com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_WITH_PREJUDICE; 32 import static com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_WITH_PREJUDICE_IN_SETTINGS; 33 import static com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED; 34 import static com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED_IN_SETTINGS; 35 import static com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED_ONE_TIME; 36 import static com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_IGNORED; 37 import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.CANCELED; 38 import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.DENIED; 39 import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.DENIED_DO_NOT_ASK_AGAIN; 40 import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.GRANTED_ALWAYS; 41 import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.GRANTED_FOREGROUND_ONLY; 42 import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.GRANTED_ONE_TIME; 43 import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.LINKED_TO_SETTINGS; 44 import static com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_CALLER_NAME; 45 import static com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_RESULT_PERMISSION_INTERACTED; 46 import static com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_RESULT_PERMISSION_RESULT; 47 import static com.android.permissioncontroller.permission.utils.Utils.getRequestMessage; 48 49 import android.Manifest; 50 import android.app.Activity; 51 import android.app.KeyguardManager; 52 import android.app.admin.DevicePolicyManager; 53 import android.content.Intent; 54 import android.content.pm.PackageInfo; 55 import android.content.pm.PackageManager; 56 import android.content.pm.PackageManager.NameNotFoundException; 57 import android.content.res.Resources; 58 import android.graphics.drawable.Icon; 59 import android.os.Build; 60 import android.os.Bundle; 61 import android.os.UserHandle; 62 import android.permission.PermissionManager; 63 import android.text.Annotation; 64 import android.text.SpannableString; 65 import android.text.Spanned; 66 import android.text.style.ClickableSpan; 67 import android.util.ArrayMap; 68 import android.util.ArraySet; 69 import android.util.Log; 70 import android.util.Pair; 71 import android.view.MotionEvent; 72 import android.view.View; 73 import android.view.Window; 74 import android.view.WindowManager; 75 76 import androidx.annotation.NonNull; 77 import androidx.annotation.Nullable; 78 import androidx.core.util.Consumer; 79 80 import com.android.permissioncontroller.Constants; 81 import com.android.permissioncontroller.DeviceUtils; 82 import com.android.permissioncontroller.PermissionControllerStatsLog; 83 import com.android.permissioncontroller.R; 84 import com.android.permissioncontroller.permission.model.AppPermissionGroup; 85 import com.android.permissioncontroller.permission.model.AppPermissions; 86 import com.android.permissioncontroller.permission.model.Permission; 87 import com.android.permissioncontroller.permission.ui.auto.GrantPermissionsAutoViewHandler; 88 import com.android.permissioncontroller.permission.ui.wear.GrantPermissionsWearViewHandler; 89 import com.android.permissioncontroller.permission.utils.ArrayUtils; 90 import com.android.permissioncontroller.permission.utils.PackageRemovalMonitor; 91 import com.android.permissioncontroller.permission.utils.SafetyNetLogger; 92 import com.android.permissioncontroller.permission.utils.Utils; 93 94 import java.util.ArrayList; 95 import java.util.Collections; 96 import java.util.List; 97 import java.util.Random; 98 99 public class GrantPermissionsActivity extends Activity 100 implements GrantPermissionsViewHandler.ResultListener { 101 102 private static final String LOG_TAG = "GrantPermissionsActivity"; 103 104 private static final String KEY_REQUEST_ID = GrantPermissionsActivity.class.getName() 105 + "_REQUEST_ID"; 106 public static final String ANNOTATION_ID = "link"; 107 108 public static final int NEXT_BUTTON = 11; 109 public static final int ALLOW_BUTTON = 0; 110 public static final int ALLOW_ALWAYS_BUTTON = 1; 111 public static final int ALLOW_FOREGROUND_BUTTON = 2; 112 public static final int DENY_BUTTON = 3; 113 public static final int DENY_AND_DONT_ASK_AGAIN_BUTTON = 4; 114 public static final int ALLOW_ONE_TIME_BUTTON = 5; 115 public static final int NO_UPGRADE_BUTTON = 6; 116 public static final int NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON = 7; 117 public static final int NO_UPGRADE_OT_BUTTON = 8; // one-time 118 public static final int NO_UPGRADE_OT_AND_DONT_ASK_AGAIN_BUTTON = 9; // one-time 119 public static final int LINK_TO_SETTINGS = 10; 120 121 private static final int APP_PERMISSION_REQUEST_CODE = 1; 122 123 /** Unique Id of a request */ 124 private long mRequestId; 125 126 private String[] mRequestedPermissions; 127 private boolean[] mButtonVisibilities; 128 private boolean mCouldHaveFgCapabilities; 129 130 private ArrayMap<Pair<String, Boolean>, GroupState> mRequestGrantPermissionGroups = 131 new ArrayMap<>(); 132 private ArraySet<String> mPermissionGroupsToSkip = new ArraySet<>(); 133 private Consumer<Intent> mActivityResultCallback; 134 135 private GrantPermissionsViewHandler mViewHandler; 136 private AppPermissions mAppPermissions; 137 138 boolean mResultSet; 139 140 /** 141 * Listens for changes to the permission of the app the permissions are currently getting 142 * granted to. {@code null} when unregistered. 143 */ 144 private @Nullable PackageManager.OnPermissionsChangedListener mPermissionChangeListener; 145 146 /** 147 * Listens for changes to the app the permissions are currently getting granted to. {@code null} 148 * when unregistered. 149 */ 150 private @Nullable PackageRemovalMonitor mPackageRemovalMonitor; 151 152 /** Package that requested the permission grant */ 153 private String mCallingPackage; 154 /** uid of {@link #mCallingPackage} */ 155 private int mCallingUid; 156 /** Notifier for auto-granted permissions */ 157 private AutoGrantPermissionsNotifier mAutoGrantPermissionsNotifier; 158 private PackageInfo mCallingPackageInfo; 159 getPermissionPolicy()160 private int getPermissionPolicy() { 161 DevicePolicyManager devicePolicyManager = getSystemService(DevicePolicyManager.class); 162 return devicePolicyManager.getPermissionPolicy(null); 163 } 164 165 /** 166 * Try to add a single permission that is requested to be granted. 167 * 168 * <p>This does <u>not</u> expand the permissions into the {@link #computeAffectedPermissions 169 * affected permissions}. 170 * 171 * @param group The group the permission belongs to (might be a background permission group) 172 * @param permName The name of the permission to add 173 * @param isFirstInstance Is this the first time the groupStates get created 174 */ addRequestedPermissions(AppPermissionGroup group, String permName, boolean isFirstInstance)175 private void addRequestedPermissions(AppPermissionGroup group, String permName, 176 boolean isFirstInstance) { 177 if (!group.isGrantingAllowed()) { 178 reportRequestResult(permName, 179 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED); 180 // Skip showing groups that we know cannot be granted. 181 return; 182 } 183 184 Permission permission = group.getPermission(permName); 185 186 // If the permission is restricted it does not show in the UI and 187 // is not added to the group at all, so check that first. 188 if (permission == null && ArrayUtils.contains( 189 mAppPermissions.getPackageInfo().requestedPermissions, permName)) { 190 reportRequestResult(permName, 191 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_RESTRICTED_PERMISSION); 192 return; 193 // We allow the user to choose only non-fixed permissions. A permission 194 // is fixed either by device policy or the user denying with prejudice. 195 } else if (group.isUserFixed()) { 196 reportRequestResult(permName, 197 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_USER_FIXED); 198 return; 199 } else if (group.isPolicyFixed() && !group.areRuntimePermissionsGranted() 200 || permission.isPolicyFixed()) { 201 reportRequestResult(permName, 202 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_POLICY_FIXED); 203 return; 204 } 205 206 Pair<String, Boolean> groupKey = new Pair<>(group.getName(), 207 group.isBackgroundGroup()); 208 209 GroupState state = mRequestGrantPermissionGroups.get(groupKey); 210 if (state == null) { 211 state = new GroupState(group); 212 mRequestGrantPermissionGroups.put(groupKey, state); 213 } 214 state.affectedPermissions = ArrayUtils.appendString( 215 state.affectedPermissions, permName); 216 217 boolean skipGroup = false; 218 switch (getPermissionPolicy()) { 219 case DevicePolicyManager.PERMISSION_POLICY_AUTO_GRANT: { 220 final String[] filterPermissions = new String[]{permName}; 221 group.grantRuntimePermissions(false, false, filterPermissions); 222 group.setPolicyFixed(filterPermissions); 223 state.mState = GroupState.STATE_ALLOWED; 224 skipGroup = true; 225 226 getAutoGrantNotifier().onPermissionAutoGranted(permName); 227 reportRequestResult(permName, 228 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_GRANTED); 229 } break; 230 231 case DevicePolicyManager.PERMISSION_POLICY_AUTO_DENY: { 232 final String[] filterPermissions = new String[]{permName}; 233 group.setPolicyFixed(filterPermissions); 234 state.mState = GroupState.STATE_DENIED; 235 skipGroup = true; 236 237 reportRequestResult(permName, 238 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_DENIED); 239 } break; 240 241 default: { 242 if (group.areRuntimePermissionsGranted()) { 243 group.grantRuntimePermissions(false, false, new String[]{permName}); 244 state.mState = GroupState.STATE_ALLOWED; 245 skipGroup = true; 246 247 reportRequestResult(permName, 248 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_GRANTED); 249 } 250 } break; 251 } 252 253 if (skipGroup && isFirstInstance) { 254 // Only allow to skip groups when this is the first time the dialog was created. 255 // Otherwise the number of groups changes between instances of the dialog. 256 state.mState = GroupState.STATE_SKIPPED; 257 } 258 } 259 260 /** 261 * Report the result of a grant of a permission. 262 * 263 * @param permission The permission that was granted or denied 264 * @param result The permission grant result 265 */ reportRequestResult(@onNull String permission, int result)266 private void reportRequestResult(@NonNull String permission, int result) { 267 boolean isImplicit = !ArrayUtils.contains(mRequestedPermissions, permission); 268 269 Log.v(LOG_TAG, 270 "Permission grant result requestId=" + mRequestId + " callingUid=" + mCallingUid 271 + " callingPackage=" + mCallingPackage + " permission=" + permission 272 + " isImplicit=" + isImplicit + " result=" + result); 273 274 PermissionControllerStatsLog.write( 275 PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED, mRequestId, 276 mCallingUid, mCallingPackage, permission, isImplicit, result); 277 } 278 279 /** 280 * Report the result of a grant of a permission. 281 * 282 * @param permissions The permissions that were granted or denied 283 * @param result The permission grant result 284 */ reportRequestResult(@onNull String[] permissions, int result)285 private void reportRequestResult(@NonNull String[] permissions, int result) { 286 for (String permission : permissions) { 287 reportRequestResult(permission, result); 288 } 289 } 290 291 @Override onCreate(Bundle icicle)292 public void onCreate(Bundle icicle) { 293 super.onCreate(icicle); 294 295 if (icicle == null) { 296 mRequestId = new Random().nextLong(); 297 } else { 298 mRequestId = icicle.getLong(KEY_REQUEST_ID); 299 } 300 301 getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); 302 303 // Cache this as this can only read on onCreate, not later. 304 mCallingPackage = getCallingPackage(); 305 try { 306 mCouldHaveFgCapabilities = Utils.couldHaveForegroundCapabilities(this, mCallingPackage); 307 } catch (NameNotFoundException e) { 308 Log.e(LOG_TAG, "Calling package " + mCallingPackage + " not found", e); 309 } 310 311 setFinishOnTouchOutside(false); 312 313 setTitle(R.string.permission_request_title); 314 315 mRequestedPermissions = getIntent().getStringArrayExtra( 316 PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES); 317 if (mRequestedPermissions == null) { 318 mRequestedPermissions = new String[0]; 319 } 320 321 final int requestedPermCount = mRequestedPermissions.length; 322 323 if (requestedPermCount == 0) { 324 setResultAndFinish(); 325 return; 326 } 327 328 PackageInfo callingPackageInfo = getCallingPackageInfo(); 329 330 if (callingPackageInfo == null || callingPackageInfo.requestedPermissions == null 331 || callingPackageInfo.requestedPermissions.length <= 0) { 332 setResultAndFinish(); 333 return; 334 } 335 336 // Don't allow legacy apps to request runtime permissions. 337 if (callingPackageInfo.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) { 338 // Returning empty arrays means a cancellation. 339 mRequestedPermissions = new String[0]; 340 setResultAndFinish(); 341 return; 342 } 343 344 mCallingPackageInfo = callingPackageInfo; 345 346 mCallingUid = callingPackageInfo.applicationInfo.uid; 347 348 UserHandle userHandle = UserHandle.getUserHandleForUid(mCallingUid); 349 350 if (DeviceUtils.isTelevision(this)) { 351 mViewHandler = new com.android.permissioncontroller.permission.ui.television 352 .GrantPermissionsViewHandlerImpl(this, 353 mCallingPackage).setResultListener(this); 354 } else if (DeviceUtils.isWear(this)) { 355 mViewHandler = new GrantPermissionsWearViewHandler(this).setResultListener(this); 356 } else if (DeviceUtils.isAuto(this)) { 357 mViewHandler = new GrantPermissionsAutoViewHandler(this, mCallingPackage) 358 .setResultListener(this); 359 } else { 360 mViewHandler = new com.android.permissioncontroller.permission.ui.handheld 361 .GrantPermissionsViewHandlerImpl(this, mCallingPackage, userHandle) 362 .setResultListener(this); 363 } 364 365 mAppPermissions = new AppPermissions(this, callingPackageInfo, false, 366 new Runnable() { 367 @Override 368 public void run() { 369 setResultAndFinish(); 370 } 371 }); 372 373 for (String requestedPermission : mRequestedPermissions) { 374 if (requestedPermission == null) { 375 continue; 376 } 377 378 ArrayList<String> affectedPermissions = 379 computeAffectedPermissions(requestedPermission); 380 381 int numAffectedPermissions = affectedPermissions.size(); 382 for (int i = 0; i < numAffectedPermissions; i++) { 383 AppPermissionGroup group = 384 mAppPermissions.getGroupForPermission(affectedPermissions.get(i)); 385 if (group == null) { 386 reportRequestResult(affectedPermissions.get(i), 387 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED); 388 389 continue; 390 } 391 392 if (mAppPermissions.getPackageInfo().applicationInfo.targetSdkVersion 393 >= Build.VERSION_CODES.R && mRequestedPermissions.length > 1 394 && group.isBackgroundGroup()) { 395 Log.e(LOG_TAG, "Apps targeting " + Build.VERSION_CODES.R + " must" 396 + " have foreground permission before requesting background and must" 397 + " request background on its own."); 398 finish(); 399 } 400 401 addRequestedPermissions(group, affectedPermissions.get(i), icicle == null); 402 } 403 } 404 405 int numGroupStates = mRequestGrantPermissionGroups.size(); 406 for (int groupStateNum = 0; groupStateNum < numGroupStates; groupStateNum++) { 407 GroupState groupState = mRequestGrantPermissionGroups.valueAt(groupStateNum); 408 AppPermissionGroup group = groupState.mGroup; 409 410 // Restore permission group state after lifecycle events 411 if (icicle != null) { 412 groupState.mState = icicle.getInt( 413 getInstanceStateKey(mRequestGrantPermissionGroups.keyAt(groupStateNum)), 414 groupState.mState); 415 } 416 417 // Do not attempt to grant background access if foreground access is not either already 418 // granted or requested 419 if (group.isBackgroundGroup()) { 420 // Check if a foreground permission is already granted 421 boolean foregroundGroupAlreadyGranted = mAppPermissions.getPermissionGroup( 422 group.getName()).areRuntimePermissionsGranted(); 423 boolean hasForegroundRequest = (getForegroundGroupState(group.getName()) != null); 424 425 if (!foregroundGroupAlreadyGranted && !hasForegroundRequest) { 426 // The background permission cannot be granted at this time 427 int numPermissions = groupState.affectedPermissions.length; 428 for (int permissionNum = 0; permissionNum < numPermissions; permissionNum++) { 429 Log.w(LOG_TAG, 430 "Cannot grant " + groupState.affectedPermissions[permissionNum] 431 + " as the matching foreground permission is not already " 432 + "granted."); 433 } 434 435 groupState.mState = GroupState.STATE_SKIPPED; 436 437 reportRequestResult(groupState.affectedPermissions, 438 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED); 439 } 440 } 441 } 442 443 setContentView(mViewHandler.createView()); 444 445 Window window = getWindow(); 446 WindowManager.LayoutParams layoutParams = window.getAttributes(); 447 mViewHandler.updateWindowAttributes(layoutParams); 448 window.setAttributes(layoutParams); 449 450 // Restore UI state after lifecycle events. This has to be before 451 // showNextPermissionGroupGrantRequest is called. showNextPermissionGroupGrantRequest might 452 // update the UI and the UI behaves differently for updates and initial creations. 453 if (icicle != null) { 454 mViewHandler.loadInstanceState(icicle); 455 } 456 457 if (!showNextPermissionGroupGrantRequest()) { 458 setResultAndFinish(); 459 } 460 } 461 462 /** 463 * Update the {@link #mRequestedPermissions} if the system reports them as granted. 464 * 465 * <p>This also updates the {@link #mAppPermissions} state and switches to the next group grant 466 * request if the current group becomes granted. 467 */ updateIfPermissionsWereGranted()468 private void updateIfPermissionsWereGranted() { 469 PackageManager pm = getPackageManager(); 470 471 boolean mightShowNextGroup = true; 472 int numGroupStates = mRequestGrantPermissionGroups.size(); 473 for (int i = 0; i < numGroupStates; i++) { 474 GroupState groupState = mRequestGrantPermissionGroups.valueAt(i); 475 476 if (groupState == null || groupState.mState != GroupState.STATE_UNKNOWN) { 477 // Group has already been approved / denied via the UI by the user 478 continue; 479 } 480 481 boolean allAffectedPermissionsOfThisGroupAreGranted = true; 482 483 if (groupState.affectedPermissions == null) { 484 // It is not clear which permissions belong to this group, hence never skip this 485 // view 486 allAffectedPermissionsOfThisGroupAreGranted = false; 487 } else { 488 for (int permNum = 0; permNum < groupState.affectedPermissions.length; 489 permNum++) { 490 if (pm.checkPermission(groupState.affectedPermissions[permNum], mCallingPackage) 491 == PERMISSION_DENIED) { 492 allAffectedPermissionsOfThisGroupAreGranted = false; 493 break; 494 } 495 } 496 } 497 498 if (allAffectedPermissionsOfThisGroupAreGranted) { 499 groupState.mState = GroupState.STATE_ALLOWED; 500 501 if (mightShowNextGroup) { 502 // The UI currently displays the first group with 503 // mState == STATE_UNKNOWN. So we are switching to next group until we 504 // could not allow a group that was still unknown 505 if (!showNextPermissionGroupGrantRequest()) { 506 setResultAndFinish(); 507 } 508 } 509 } else { 510 mightShowNextGroup = false; 511 } 512 } 513 } 514 515 @Override onStart()516 protected void onStart() { 517 super.onStart(); 518 519 try { 520 mPermissionChangeListener = new PermissionChangeListener(); 521 } catch (NameNotFoundException e) { 522 setResultAndFinish(); 523 return; 524 } 525 PackageManager pm = getPackageManager(); 526 pm.addOnPermissionsChangeListener(mPermissionChangeListener); 527 528 // get notified when the package is removed 529 mPackageRemovalMonitor = new PackageRemovalMonitor(this, mCallingPackage) { 530 @Override 531 public void onPackageRemoved() { 532 Log.w(LOG_TAG, mCallingPackage + " was uninstalled"); 533 534 finish(); 535 } 536 }; 537 mPackageRemovalMonitor.register(); 538 539 // check if the package was removed while this activity was not started 540 try { 541 pm.getPackageInfo(mCallingPackage, 0); 542 } catch (NameNotFoundException e) { 543 Log.w(LOG_TAG, mCallingPackage + " was uninstalled while this activity was stopped", e); 544 finish(); 545 } 546 547 updateIfPermissionsWereGranted(); 548 } 549 550 @Override onResume()551 protected void onResume() { 552 if (!showNextPermissionGroupGrantRequest()) { 553 setResultAndFinish(); 554 } 555 super.onResume(); 556 } 557 558 @Override onStop()559 protected void onStop() { 560 super.onStop(); 561 562 if (mPackageRemovalMonitor != null) { 563 mPackageRemovalMonitor.unregister(); 564 mPackageRemovalMonitor = null; 565 } 566 567 if (mPermissionChangeListener != null) { 568 getPackageManager().removeOnPermissionsChangeListener(mPermissionChangeListener); 569 mPermissionChangeListener = null; 570 } 571 } 572 573 @Override dispatchTouchEvent(MotionEvent ev)574 public boolean dispatchTouchEvent(MotionEvent ev) { 575 View rootView = getWindow().getDecorView(); 576 if (rootView.getTop() != 0) { 577 // We are animating the top view, need to compensate for that in motion events. 578 ev.setLocation(ev.getX(), ev.getY() - rootView.getTop()); 579 } 580 return super.dispatchTouchEvent(ev); 581 } 582 583 /** 584 * Compose a key that stores the GroupState.mState in the instance state. 585 * 586 * @param requestGrantPermissionGroupsKey The key of the permission group 587 * 588 * @return A unique key to be used in the instance state 589 */ getInstanceStateKey( Pair<String, Boolean> requestGrantPermissionGroupsKey)590 private static String getInstanceStateKey( 591 Pair<String, Boolean> requestGrantPermissionGroupsKey) { 592 return GrantPermissionsActivity.class.getName() + "_" 593 + requestGrantPermissionGroupsKey.first + "_" 594 + requestGrantPermissionGroupsKey.second; 595 } 596 597 @Override onSaveInstanceState(Bundle outState)598 protected void onSaveInstanceState(Bundle outState) { 599 super.onSaveInstanceState(outState); 600 601 mViewHandler.saveInstanceState(outState); 602 603 outState.putLong(KEY_REQUEST_ID, mRequestId); 604 605 int numGroups = mRequestGrantPermissionGroups.size(); 606 for (int i = 0; i < numGroups; i++) { 607 int state = mRequestGrantPermissionGroups.valueAt(i).mState; 608 609 if (state != GroupState.STATE_UNKNOWN) { 610 outState.putInt(getInstanceStateKey(mRequestGrantPermissionGroups.keyAt(i)), state); 611 } 612 } 613 } 614 615 /** 616 * @return the background group state for the permission group with the {@code name} 617 */ getBackgroundGroupState(String name)618 private GroupState getBackgroundGroupState(String name) { 619 return mRequestGrantPermissionGroups.get(new Pair<>(name, true)); 620 } 621 622 /** 623 * @return the foreground group state for the permission group with the {@code name} 624 */ getForegroundGroupState(String name)625 private GroupState getForegroundGroupState(String name) { 626 return mRequestGrantPermissionGroups.get(new Pair<>(name, false)); 627 } 628 shouldShowRequestForGroupState(GroupState groupState)629 private boolean shouldShowRequestForGroupState(GroupState groupState) { 630 if (groupState.mState == GroupState.STATE_SKIPPED) { 631 return false; 632 } 633 634 GroupState foregroundGroup = getForegroundGroupState(groupState.mGroup.getName()); 635 if (groupState.mGroup.isBackgroundGroup() 636 && (foregroundGroup != null && shouldShowRequestForGroupState(foregroundGroup))) { 637 // If an app requests both foreground and background permissions of the same group, 638 // we only show one request 639 return false; 640 } 641 642 return !mPermissionGroupsToSkip.contains(groupState.mGroup.getName()); 643 } 644 showNextPermissionGroupGrantRequest()645 private boolean showNextPermissionGroupGrantRequest() { 646 int numGroupStates = mRequestGrantPermissionGroups.size(); 647 int numGrantRequests = 0; 648 for (int i = 0; i < numGroupStates; i++) { 649 if (shouldShowRequestForGroupState(mRequestGrantPermissionGroups.valueAt(i))) { 650 numGrantRequests++; 651 } 652 } 653 654 int currentIndex = 0; 655 List<GroupState> groupStates = new ArrayList<>(mRequestGrantPermissionGroups.values()); 656 Collections.sort(groupStates, (s1, s2) -> -Boolean.compare(s1.mGroup.supportsOneTimeGrant(), 657 s2.mGroup.supportsOneTimeGrant())); 658 for (GroupState groupState : groupStates) { 659 if (!shouldShowRequestForGroupState(groupState)) { 660 continue; 661 } 662 663 if (groupState.mState == GroupState.STATE_UNKNOWN) { 664 GroupState foregroundGroupState; 665 GroupState backgroundGroupState; 666 if (groupState.mGroup.isBackgroundGroup()) { 667 backgroundGroupState = groupState; 668 foregroundGroupState = getForegroundGroupState(groupState.mGroup.getName()); 669 } else { 670 foregroundGroupState = groupState; 671 backgroundGroupState = getBackgroundGroupState(groupState.mGroup.getName()); 672 } 673 674 CharSequence appLabel = mAppPermissions.getAppLabel(); 675 676 Icon icon; 677 try { 678 icon = Icon.createWithResource(groupState.mGroup.getIconPkg(), 679 groupState.mGroup.getIconResId()); 680 } catch (Resources.NotFoundException e) { 681 Log.e(LOG_TAG, "Cannot load icon for group" + groupState.mGroup.getName(), e); 682 icon = null; 683 } 684 685 // If no background permissions are granted yet, we need to ask for background 686 // permissions 687 boolean needBackgroundPermission = false; 688 boolean isBackgroundPermissionUserSet = false; 689 if (backgroundGroupState != null) { 690 if (!backgroundGroupState.mGroup.areRuntimePermissionsGranted()) { 691 needBackgroundPermission = true; 692 isBackgroundPermissionUserSet = backgroundGroupState.mGroup.isUserSet(); 693 } 694 } 695 696 // If no foreground permissions are granted yet, we need to ask for foreground 697 // permissions 698 boolean needForegroundPermission = false; 699 boolean isForegroundPermissionUserSet = false; 700 if (foregroundGroupState != null) { 701 if (!foregroundGroupState.mGroup.areRuntimePermissionsGranted()) { 702 needForegroundPermission = true; 703 isForegroundPermissionUserSet = foregroundGroupState.mGroup.isUserSet(); 704 } 705 } 706 707 mButtonVisibilities = new boolean[NEXT_BUTTON]; 708 mButtonVisibilities[ALLOW_BUTTON] = true; 709 mButtonVisibilities[DENY_BUTTON] = true; 710 mButtonVisibilities[ALLOW_ONE_TIME_BUTTON] = 711 groupState.mGroup.supportsOneTimeGrant(); 712 713 int messageId; 714 int detailMessageId = 0; 715 716 if (mAppPermissions.getPackageInfo().applicationInfo.targetSdkVersion 717 >= Build.VERSION_CODES.R) { 718 if (groupState.mGroup.hasPermissionWithBackgroundMode() 719 || groupState.mGroup.isBackgroundGroup()) { 720 if (needForegroundPermission && needBackgroundPermission) { 721 if (mCouldHaveFgCapabilities) { 722 sendToSettings(groupState); 723 return true; 724 } 725 // Shouldn't be reached as background must be requested as a singleton 726 return false; 727 } else if (needForegroundPermission) { 728 // Case: sdk >= R, BG/FG permission requesting FG only 729 messageId = groupState.mGroup.getRequest(); 730 if (mCouldHaveFgCapabilities) { 731 sendToSettings(groupState); 732 return true; 733 } 734 mButtonVisibilities[ALLOW_BUTTON] = false; 735 mButtonVisibilities[ALLOW_FOREGROUND_BUTTON] = true; 736 mButtonVisibilities[DENY_BUTTON] = 737 !isForegroundPermissionUserSet; 738 mButtonVisibilities[DENY_AND_DONT_ASK_AGAIN_BUTTON] = 739 isForegroundPermissionUserSet; 740 } else if (needBackgroundPermission) { 741 // Case: sdk >= R, BG/FG permission requesting BG only 742 sendToSettings(groupState); 743 return true; 744 } else { 745 // Not reached as the permissions should be auto-granted 746 return false; 747 } 748 } else { 749 // Case: sdk >= R, Requesting normal permission 750 messageId = groupState.mGroup.getRequest(); 751 mButtonVisibilities[DENY_BUTTON] = 752 !isForegroundPermissionUserSet; 753 mButtonVisibilities[DENY_AND_DONT_ASK_AGAIN_BUTTON] = 754 isForegroundPermissionUserSet; 755 if (groupState.mGroup.getName().equals(Manifest.permission_group.CAMERA) 756 || groupState.mGroup.getName().equals( 757 Manifest.permission_group.MICROPHONE)) { 758 mButtonVisibilities[ALLOW_BUTTON] = false; 759 if (mCouldHaveFgCapabilities 760 || Utils.isEmergencyApp(this, mCallingPackage)) { 761 mButtonVisibilities[ALLOW_ALWAYS_BUTTON] = true; 762 mButtonVisibilities[ALLOW_ONE_TIME_BUTTON] = false; 763 } else { 764 mButtonVisibilities[ALLOW_FOREGROUND_BUTTON] = true; 765 } 766 } 767 } 768 } else { 769 if (groupState.mGroup.hasPermissionWithBackgroundMode() 770 || groupState.mGroup.isBackgroundGroup()) { 771 if (mCouldHaveFgCapabilities) { 772 // Only allow granting of background location 773 messageId = groupState.mGroup.getBackgroundRequest(); 774 detailMessageId = groupState.mGroup.getBackgroundRequestDetail(); 775 mButtonVisibilities[ALLOW_BUTTON] = false; 776 mButtonVisibilities[ALLOW_ONE_TIME_BUTTON] = false; 777 mButtonVisibilities[DENY_BUTTON] = 778 !isForegroundPermissionUserSet; 779 mButtonVisibilities[DENY_AND_DONT_ASK_AGAIN_BUTTON] = 780 isForegroundPermissionUserSet; 781 } else { 782 if (needForegroundPermission && needBackgroundPermission) { 783 // Case: sdk < R, BG/FG permission requesting both 784 messageId = groupState.mGroup.getBackgroundRequest(); 785 detailMessageId = groupState.mGroup.getBackgroundRequestDetail(); 786 mButtonVisibilities[ALLOW_BUTTON] = false; 787 mButtonVisibilities[ALLOW_FOREGROUND_BUTTON] = true; 788 mButtonVisibilities[DENY_BUTTON] = 789 !isForegroundPermissionUserSet; 790 mButtonVisibilities[DENY_AND_DONT_ASK_AGAIN_BUTTON] = 791 isForegroundPermissionUserSet; 792 } else if (needForegroundPermission) { 793 // Case: sdk < R, BG/FG permission requesting FG only 794 messageId = groupState.mGroup.getRequest(); 795 mButtonVisibilities[ALLOW_BUTTON] = false; 796 mButtonVisibilities[ALLOW_FOREGROUND_BUTTON] = true; 797 mButtonVisibilities[DENY_BUTTON] = 798 !isForegroundPermissionUserSet; 799 mButtonVisibilities[DENY_AND_DONT_ASK_AGAIN_BUTTON] = 800 isForegroundPermissionUserSet; 801 } else if (needBackgroundPermission) { 802 // Case: sdk < R, BG/FG permission requesting BG only 803 messageId = groupState.mGroup.getUpgradeRequest(); 804 detailMessageId = groupState.mGroup.getUpgradeRequestDetail(); 805 mButtonVisibilities[ALLOW_BUTTON] = false; 806 mButtonVisibilities[DENY_BUTTON] = false; 807 mButtonVisibilities[ALLOW_ONE_TIME_BUTTON] = false; 808 if (mAppPermissions.getPermissionGroup( 809 groupState.mGroup.getName()).isOneTime()) { 810 mButtonVisibilities[NO_UPGRADE_OT_BUTTON] = 811 !isBackgroundPermissionUserSet; 812 mButtonVisibilities[NO_UPGRADE_OT_AND_DONT_ASK_AGAIN_BUTTON] = 813 isBackgroundPermissionUserSet; 814 } else { 815 mButtonVisibilities[NO_UPGRADE_BUTTON] = 816 !isBackgroundPermissionUserSet; 817 mButtonVisibilities[NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON] = 818 isBackgroundPermissionUserSet; 819 } 820 } else { 821 // Not reached as the permissions should be auto-granted 822 return false; 823 } 824 } 825 } else { 826 // Case: sdk < R, Requesting normal permission 827 messageId = groupState.mGroup.getRequest(); 828 mButtonVisibilities[DENY_BUTTON] = 829 !isForegroundPermissionUserSet; 830 mButtonVisibilities[DENY_AND_DONT_ASK_AGAIN_BUTTON] = 831 isForegroundPermissionUserSet; 832 if (groupState.mGroup.getName().equals(Manifest.permission_group.CAMERA) 833 || groupState.mGroup.getName().equals( 834 Manifest.permission_group.MICROPHONE)) { 835 mButtonVisibilities[ALLOW_BUTTON] = false; 836 if (mCouldHaveFgCapabilities 837 || Utils.isEmergencyApp(this, mCallingPackage)) { 838 mButtonVisibilities[ALLOW_ALWAYS_BUTTON] = true; 839 mButtonVisibilities[ALLOW_ONE_TIME_BUTTON] = false; 840 } else { 841 mButtonVisibilities[ALLOW_FOREGROUND_BUTTON] = true; 842 } 843 } 844 } 845 } 846 847 CharSequence message = getRequestMessage(appLabel, groupState.mGroup, this, 848 messageId); 849 850 Spanned detailMessage = null; 851 if (detailMessageId != 0) { 852 detailMessage = 853 new SpannableString(getText(detailMessageId)); 854 Annotation[] annotations = detailMessage.getSpans( 855 0, detailMessage.length(), Annotation.class); 856 int numAnnotations = annotations.length; 857 for (int i = 0; i < numAnnotations; i++) { 858 Annotation annotation = annotations[i]; 859 if (annotation.getValue().equals(ANNOTATION_ID)) { 860 int start = detailMessage.getSpanStart(annotation); 861 int end = detailMessage.getSpanEnd(annotation); 862 ClickableSpan clickableSpan = getLinkToAppPermissions(groupState); 863 SpannableString spannableString = 864 new SpannableString(detailMessage); 865 spannableString.setSpan(clickableSpan, start, end, 0); 866 detailMessage = spannableString; 867 mButtonVisibilities[LINK_TO_SETTINGS] = true; 868 break; 869 } 870 } 871 } 872 873 // Set the permission message as the title so it can be announced. 874 setTitle(message); 875 876 mViewHandler.updateUi(groupState.mGroup.getName(), numGrantRequests, currentIndex, 877 icon, message, detailMessage, mButtonVisibilities); 878 879 return true; 880 } 881 882 if (groupState.mState != GroupState.STATE_SKIPPED) { 883 currentIndex++; 884 } 885 } 886 887 return false; 888 } 889 sendToSettings(GroupState groupState)890 private void sendToSettings(GroupState groupState) { 891 if (mActivityResultCallback == null) { 892 startAppPermissionFragment(groupState); 893 mActivityResultCallback = data -> { 894 if (data == null || data.getStringExtra( 895 EXTRA_RESULT_PERMISSION_INTERACTED) == null) { 896 // User didn't interact, count against rate limit 897 if (groupState.mGroup.isUserSet()) { 898 groupState.mGroup.setUserFixed(true); 899 } else { 900 groupState.mGroup.setUserSet(true); 901 } 902 } 903 mPermissionGroupsToSkip.add(groupState.mGroup.getName()); 904 }; 905 } 906 } 907 getLinkToAppPermissions(GroupState groupState)908 private ClickableSpan getLinkToAppPermissions(GroupState groupState) { 909 return new ClickableSpan() { 910 @Override 911 public void onClick(View widget) { 912 logGrantPermissionActivityButtons(groupState.mGroup.getName(), LINKED_TO_SETTINGS); 913 startAppPermissionFragment(groupState); 914 mActivityResultCallback = data -> { 915 if (data != null) { 916 String groupName = data.getStringExtra(EXTRA_RESULT_PERMISSION_INTERACTED); 917 if (groupName != null) { 918 mPermissionGroupsToSkip.add(groupName); 919 int result = data.getIntExtra(EXTRA_RESULT_PERMISSION_RESULT, -1); 920 logSettingsInteraction(groupName, result); 921 } 922 } 923 }; 924 } 925 }; 926 } 927 928 private void logSettingsInteraction(String groupName, int result) { 929 GroupState foregroundGroupState = getForegroundGroupState(groupName); 930 GroupState backgroundGroupState = getBackgroundGroupState(groupName); 931 switch (result) { 932 case GRANTED_ALWAYS: 933 if (foregroundGroupState != null) { 934 reportRequestResult(foregroundGroupState.affectedPermissions, 935 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED_IN_SETTINGS); 936 } 937 if (backgroundGroupState != null) { 938 reportRequestResult(backgroundGroupState.affectedPermissions, 939 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED_IN_SETTINGS); 940 } 941 break; 942 case GRANTED_FOREGROUND_ONLY: 943 if (foregroundGroupState != null) { 944 reportRequestResult(foregroundGroupState.affectedPermissions, 945 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED_IN_SETTINGS); 946 } 947 if (backgroundGroupState != null) { 948 reportRequestResult(backgroundGroupState.affectedPermissions, 949 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_IN_SETTINGS); 950 } 951 break; 952 case DENIED: 953 if (foregroundGroupState != null) { 954 reportRequestResult(foregroundGroupState.affectedPermissions, 955 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_IN_SETTINGS); 956 } 957 if (backgroundGroupState != null) { 958 reportRequestResult(backgroundGroupState.affectedPermissions, 959 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_IN_SETTINGS); 960 } 961 break; 962 case DENIED_DO_NOT_ASK_AGAIN: 963 if (foregroundGroupState != null) { 964 reportRequestResult(foregroundGroupState.affectedPermissions, 965 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_WITH_PREJUDICE_IN_SETTINGS); 966 } 967 if (backgroundGroupState != null) { 968 reportRequestResult(backgroundGroupState.affectedPermissions, 969 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_WITH_PREJUDICE_IN_SETTINGS); 970 } 971 break; 972 } 973 } 974 975 private void startAppPermissionFragment(GroupState groupState) { 976 Intent intent = new Intent(Intent.ACTION_MANAGE_APP_PERMISSION) 977 .putExtra(Intent.EXTRA_PACKAGE_NAME, mAppPermissions.getPackageInfo().packageName) 978 .putExtra(Intent.EXTRA_PERMISSION_GROUP_NAME, groupState.mGroup.getName()) 979 .putExtra(Intent.EXTRA_USER, groupState.mGroup.getUser()) 980 .putExtra(EXTRA_CALLER_NAME, GrantPermissionsActivity.class.getName()) 981 .putExtra(Constants.EXTRA_SESSION_ID, mRequestId) 982 .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); 983 startActivityForResult(intent, APP_PERMISSION_REQUEST_CODE); 984 } 985 986 @Override 987 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 988 if (requestCode == APP_PERMISSION_REQUEST_CODE && mActivityResultCallback != null) { 989 mActivityResultCallback.accept(data); 990 mActivityResultCallback = null; 991 } 992 } 993 994 @Override 995 public void onPermissionGrantResult(String name, 996 @GrantPermissionsViewHandler.Result int result) { 997 GroupState foregroundGroupState = getForegroundGroupState(name); 998 GroupState backgroundGroupState = getBackgroundGroupState(name); 999 1000 if (result == GRANTED_ALWAYS || result == GRANTED_FOREGROUND_ONLY 1001 || result == DENIED_DO_NOT_ASK_AGAIN) { 1002 KeyguardManager kgm = getSystemService(KeyguardManager.class); 1003 1004 if (kgm.isDeviceLocked()) { 1005 kgm.requestDismissKeyguard(this, new KeyguardManager.KeyguardDismissCallback() { 1006 @Override 1007 public void onDismissError() { 1008 Log.e(LOG_TAG, "Cannot dismiss keyguard perm=" + name + " result=" 1009 + result); 1010 } 1011 1012 @Override 1013 public void onDismissCancelled() { 1014 // do nothing (i.e. stay at the current permission group) 1015 } 1016 1017 @Override 1018 public void onDismissSucceeded() { 1019 // Now the keyguard is dismissed, hence the device is not locked 1020 // anymore 1021 onPermissionGrantResult(name, result); 1022 } 1023 }); 1024 1025 return; 1026 } 1027 } 1028 1029 logGrantPermissionActivityButtons(name, result); 1030 switch (result) { 1031 case CANCELED: 1032 if (foregroundGroupState != null) { 1033 reportRequestResult(foregroundGroupState.affectedPermissions, 1034 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_IGNORED); 1035 } 1036 if (backgroundGroupState != null) { 1037 reportRequestResult(backgroundGroupState.affectedPermissions, 1038 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_IGNORED); 1039 } 1040 setResultAndFinish(); 1041 return; 1042 case GRANTED_ALWAYS : 1043 if (foregroundGroupState != null) { 1044 onPermissionGrantResultSingleState(foregroundGroupState, true, false, false); 1045 } 1046 if (backgroundGroupState != null) { 1047 onPermissionGrantResultSingleState(backgroundGroupState, true, false, false); 1048 } 1049 break; 1050 case GRANTED_FOREGROUND_ONLY : 1051 if (foregroundGroupState != null) { 1052 onPermissionGrantResultSingleState(foregroundGroupState, true, false, false); 1053 } 1054 if (backgroundGroupState != null) { 1055 onPermissionGrantResultSingleState(backgroundGroupState, false, false, false); 1056 } 1057 break; 1058 case GRANTED_ONE_TIME: 1059 if (foregroundGroupState != null) { 1060 onPermissionGrantResultSingleState(foregroundGroupState, true, true, false); 1061 } 1062 if (backgroundGroupState != null) { 1063 onPermissionGrantResultSingleState(backgroundGroupState, false, true, false); 1064 } 1065 break; 1066 case DENIED : 1067 if (foregroundGroupState != null) { 1068 onPermissionGrantResultSingleState(foregroundGroupState, false, false, false); 1069 } 1070 if (backgroundGroupState != null) { 1071 onPermissionGrantResultSingleState(backgroundGroupState, false, false, false); 1072 } 1073 break; 1074 case DENIED_DO_NOT_ASK_AGAIN : 1075 if (foregroundGroupState != null) { 1076 onPermissionGrantResultSingleState(foregroundGroupState, false, false, true); 1077 } 1078 if (backgroundGroupState != null) { 1079 onPermissionGrantResultSingleState(backgroundGroupState, false, false, true); 1080 } 1081 break; 1082 } 1083 1084 if (!showNextPermissionGroupGrantRequest()) { 1085 setResultAndFinish(); 1086 } 1087 } 1088 1089 /** 1090 * Grants or revoked the affected permissions for a single {@link GroupState}. 1091 * 1092 * @param groupState The group state with the permissions to grant/revoke 1093 * @param granted {@code true} if the permissions should be granted, {@code false} if they 1094 * should be revoked 1095 * @param isOneTime if the permission is temporary and should be revoked automatically 1096 * @param doNotAskAgain if the permissions should be revoked should be app be allowed to ask 1097 * again for the same permissions? 1098 */ 1099 private void onPermissionGrantResultSingleState(GroupState groupState, boolean granted, 1100 boolean isOneTime, boolean doNotAskAgain) { 1101 if (groupState != null && groupState.mGroup != null 1102 && groupState.mState == GroupState.STATE_UNKNOWN) { 1103 if (granted) { 1104 int permissionGrantRequestResult = 1105 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED; 1106 1107 if (isOneTime) { 1108 groupState.mGroup.setOneTime(true); 1109 permissionGrantRequestResult = 1110 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED_ONE_TIME; 1111 } else { 1112 groupState.mGroup.setOneTime(false); 1113 } 1114 1115 groupState.mGroup.grantRuntimePermissions(true, doNotAskAgain, 1116 groupState.affectedPermissions); 1117 groupState.mState = GroupState.STATE_ALLOWED; 1118 1119 reportRequestResult(groupState.affectedPermissions, permissionGrantRequestResult); 1120 } else { 1121 groupState.mGroup.revokeRuntimePermissions(doNotAskAgain, 1122 groupState.affectedPermissions); 1123 groupState.mGroup.setOneTime(false); 1124 groupState.mState = GroupState.STATE_DENIED; 1125 1126 reportRequestResult(groupState.affectedPermissions, doNotAskAgain 1127 ? 1128 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_WITH_PREJUDICE 1129 : PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED); 1130 } 1131 } 1132 } 1133 1134 @Override 1135 public void onBackPressed() { 1136 mViewHandler.onBackPressed(); 1137 } 1138 1139 @Override 1140 public void finish() { 1141 setResultIfNeeded(RESULT_CANCELED); 1142 if (mAutoGrantPermissionsNotifier != null) { 1143 mAutoGrantPermissionsNotifier.notifyOfAutoGrantPermissions(true); 1144 } 1145 super.finish(); 1146 } 1147 1148 private PackageInfo getCallingPackageInfo() { 1149 try { 1150 return getPackageManager().getPackageInfo(mCallingPackage, 1151 PackageManager.GET_PERMISSIONS); 1152 } catch (NameNotFoundException e) { 1153 Log.i(LOG_TAG, "No package: " + mCallingPackage, e); 1154 return null; 1155 } 1156 } 1157 1158 private void setResultIfNeeded(int resultCode) { 1159 if (!mResultSet) { 1160 mResultSet = true; 1161 logRequestedPermissionGroups(); 1162 Intent result = new Intent(PackageManager.ACTION_REQUEST_PERMISSIONS); 1163 result.putExtra(PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES, mRequestedPermissions); 1164 1165 PackageManager pm = getPackageManager(); 1166 int numRequestedPermissions = mRequestedPermissions.length; 1167 int[] grantResults = new int[numRequestedPermissions]; 1168 for (int i = 0; i < numRequestedPermissions; i++) { 1169 grantResults[i] = pm.checkPermission(mRequestedPermissions[i], mCallingPackage); 1170 } 1171 1172 result.putExtra(PackageManager.EXTRA_REQUEST_PERMISSIONS_RESULTS, grantResults); 1173 setResult(resultCode, result); 1174 } 1175 } 1176 1177 private void setResultAndFinish() { 1178 setResultIfNeeded(RESULT_OK); 1179 finish(); 1180 } 1181 1182 private void logRequestedPermissionGroups() { 1183 if (mRequestGrantPermissionGroups.isEmpty()) { 1184 return; 1185 } 1186 1187 final int groupCount = mRequestGrantPermissionGroups.size(); 1188 List<AppPermissionGroup> groups = new ArrayList<>(groupCount); 1189 for (GroupState groupState : mRequestGrantPermissionGroups.values()) { 1190 groups.add(groupState.mGroup); 1191 } 1192 1193 SafetyNetLogger.logPermissionsRequested(mAppPermissions.getPackageInfo(), groups); 1194 } 1195 1196 /** 1197 * Get the actually requested permissions when a permission is requested. 1198 * 1199 * <p>>In some cases requesting to grant a single permission requires the system to grant 1200 * additional permissions. E.g. before N-MR1 a single permission of a group caused the whole 1201 * group to be granted. Another case are permissions that are split into two. For apps that 1202 * target an SDK before the split, this method automatically adds the split off permission. 1203 * 1204 * @param permission The requested permission 1205 * 1206 * @return The actually requested permissions 1207 */ 1208 private ArrayList<String> computeAffectedPermissions(String permission) { 1209 int requestingAppTargetSDK = 1210 mAppPermissions.getPackageInfo().applicationInfo.targetSdkVersion; 1211 1212 // If a permission is split, all permissions the original permission is split into are 1213 // affected 1214 ArrayList<String> extendedBySplitPerms = new ArrayList<>(); 1215 extendedBySplitPerms.add(permission); 1216 1217 List<PermissionManager.SplitPermissionInfo> splitPerms = getSystemService( 1218 PermissionManager.class).getSplitPermissions(); 1219 int numSplitPerms = splitPerms.size(); 1220 for (int i = 0; i < numSplitPerms; i++) { 1221 PermissionManager.SplitPermissionInfo splitPerm = splitPerms.get(i); 1222 1223 if (requestingAppTargetSDK < splitPerm.getTargetSdk() 1224 && permission.equals(splitPerm.getSplitPermission())) { 1225 extendedBySplitPerms.addAll(splitPerm.getNewPermissions()); 1226 } 1227 } 1228 1229 // For <= N_MR1 apps all permissions of the groups of the requested permissions are affected 1230 if (requestingAppTargetSDK <= Build.VERSION_CODES.N_MR1) { 1231 ArrayList<String> extendedBySplitPermsAndGroup = new ArrayList<>(); 1232 1233 int numExtendedBySplitPerms = extendedBySplitPerms.size(); 1234 for (int splitPermNum = 0; splitPermNum < numExtendedBySplitPerms; splitPermNum++) { 1235 AppPermissionGroup group = mAppPermissions.getGroupForPermission( 1236 extendedBySplitPerms.get(splitPermNum)); 1237 1238 if (group == null) { 1239 continue; 1240 } 1241 1242 ArrayList<Permission> permissionsInGroup = group.getPermissions(); 1243 int numPermissionsInGroup = permissionsInGroup.size(); 1244 for (int permNum = 0; permNum < numPermissionsInGroup; permNum++) { 1245 extendedBySplitPermsAndGroup.add(permissionsInGroup.get(permNum).getName()); 1246 } 1247 } 1248 1249 return extendedBySplitPermsAndGroup; 1250 } else { 1251 return extendedBySplitPerms; 1252 } 1253 } 1254 1255 private void logGrantPermissionActivityButtons(String permissionGroupName, int grantResult) { 1256 int clickedButton = 0; 1257 int presentedButtons = getButtonState(); 1258 switch (grantResult) { 1259 case GRANTED_ALWAYS: 1260 if (mButtonVisibilities[ALLOW_BUTTON]) { 1261 clickedButton = 1 << ALLOW_BUTTON; 1262 } else { 1263 clickedButton = 1 << ALLOW_ALWAYS_BUTTON; 1264 } 1265 break; 1266 case GRANTED_FOREGROUND_ONLY: 1267 clickedButton = 1 << ALLOW_FOREGROUND_BUTTON; 1268 break; 1269 case DENIED: 1270 if (mButtonVisibilities[NO_UPGRADE_BUTTON]) { 1271 clickedButton = 1 << NO_UPGRADE_BUTTON; 1272 } else if (mButtonVisibilities[NO_UPGRADE_OT_BUTTON]) { 1273 clickedButton = 1 << NO_UPGRADE_OT_BUTTON; 1274 } else if (mButtonVisibilities[DENY_BUTTON]) { 1275 clickedButton = 1 << DENY_BUTTON; 1276 } 1277 break; 1278 case DENIED_DO_NOT_ASK_AGAIN: 1279 if (mButtonVisibilities[NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON]) { 1280 clickedButton = 1 << NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON; 1281 } else if (mButtonVisibilities[NO_UPGRADE_OT_AND_DONT_ASK_AGAIN_BUTTON]) { 1282 clickedButton = 1 << NO_UPGRADE_OT_AND_DONT_ASK_AGAIN_BUTTON; 1283 } else if (mButtonVisibilities[DENY_AND_DONT_ASK_AGAIN_BUTTON]) { 1284 clickedButton = 1 << DENY_AND_DONT_ASK_AGAIN_BUTTON; 1285 } 1286 break; 1287 case GRANTED_ONE_TIME: 1288 clickedButton = 1 << ALLOW_ONE_TIME_BUTTON; 1289 break; 1290 case LINKED_TO_SETTINGS: 1291 clickedButton = 1 << LINK_TO_SETTINGS; 1292 case CANCELED: 1293 // fall through 1294 default: 1295 break; 1296 } 1297 1298 PermissionControllerStatsLog.write(GRANT_PERMISSIONS_ACTIVITY_BUTTON_ACTIONS, 1299 permissionGroupName, mCallingUid, mCallingPackage, presentedButtons, 1300 clickedButton, mRequestId); 1301 Log.v(LOG_TAG, "Logged buttons presented and clicked permissionGroupName=" 1302 + permissionGroupName + " uid=" + mCallingUid + " package=" + mCallingPackage 1303 + " presentedButtons=" + presentedButtons + " clickedButton=" + clickedButton 1304 + " sessionId=" + mRequestId); 1305 } 1306 1307 private int getButtonState() { 1308 if (mButtonVisibilities == null) { 1309 return 0; 1310 } 1311 int buttonState = 0; 1312 for (int i = NEXT_BUTTON - 1; i >= 0; i--) { 1313 buttonState *= 2; 1314 if (mButtonVisibilities[i]) { 1315 buttonState++; 1316 } 1317 } 1318 return buttonState; 1319 } 1320 1321 private static final class GroupState { 1322 static final int STATE_UNKNOWN = 0; 1323 static final int STATE_ALLOWED = 1; 1324 static final int STATE_DENIED = 2; 1325 static final int STATE_SKIPPED = 3; 1326 1327 final AppPermissionGroup mGroup; 1328 int mState = STATE_UNKNOWN; 1329 1330 /** Permissions of this group that need to be granted, null == no permissions of group */ 1331 String[] affectedPermissions; 1332 1333 GroupState(AppPermissionGroup group) { 1334 mGroup = group; 1335 } 1336 } 1337 1338 private class PermissionChangeListener implements PackageManager.OnPermissionsChangedListener { 1339 final int mCallingPackageUid; 1340 1341 PermissionChangeListener() throws NameNotFoundException { 1342 mCallingPackageUid = getPackageManager().getPackageUid(mCallingPackage, 0); 1343 } 1344 1345 @Override 1346 public void onPermissionsChanged(int uid) { 1347 if (uid == mCallingPackageUid) { 1348 updateIfPermissionsWereGranted(); 1349 } 1350 } 1351 } 1352 1353 /** 1354 * Creates the AutoGrantPermissionsNotifier lazily in case there's no policy set 1355 * device-wide (common case). 1356 * 1357 * @return An initalized {@code AutoGrantPermissionsNotifier} instance. 1358 */ 1359 private @NonNull AutoGrantPermissionsNotifier getAutoGrantNotifier() { 1360 if (mAutoGrantPermissionsNotifier == null) { 1361 mAutoGrantPermissionsNotifier = new AutoGrantPermissionsNotifier( 1362 this, mCallingPackageInfo); 1363 } 1364 1365 return mAutoGrantPermissionsNotifier; 1366 } 1367 } 1368