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.model; 18 19 import static android.Manifest.permission.ACCESS_BACKGROUND_LOCATION; 20 import static android.Manifest.permission.ACCESS_FINE_LOCATION; 21 import static android.app.AppOpsManager.MODE_ALLOWED; 22 import static android.app.AppOpsManager.MODE_FOREGROUND; 23 import static android.app.AppOpsManager.MODE_IGNORED; 24 import static android.app.AppOpsManager.OPSTR_LEGACY_STORAGE; 25 import static android.content.pm.PackageManager.PERMISSION_GRANTED; 26 27 import android.app.ActivityManager; 28 import android.app.AppOpsManager; 29 import android.app.Application; 30 import android.content.Context; 31 import android.content.pm.PackageInfo; 32 import android.content.pm.PackageItemInfo; 33 import android.content.pm.PackageManager; 34 import android.content.pm.PackageManager.NameNotFoundException; 35 import android.content.pm.PermissionGroupInfo; 36 import android.content.pm.PermissionInfo; 37 import android.os.Build; 38 import android.os.UserHandle; 39 import android.permission.PermissionManager; 40 import android.text.TextUtils; 41 import android.util.ArrayMap; 42 import android.util.Log; 43 44 import androidx.annotation.NonNull; 45 import androidx.annotation.Nullable; 46 import androidx.annotation.StringRes; 47 48 import com.android.permissioncontroller.R; 49 import com.android.permissioncontroller.permission.service.LocationAccessCheck; 50 import com.android.permissioncontroller.permission.utils.ArrayUtils; 51 import com.android.permissioncontroller.permission.utils.LocationUtils; 52 import com.android.permissioncontroller.permission.utils.SoftRestrictedPermissionPolicy; 53 import com.android.permissioncontroller.permission.utils.Utils; 54 55 import java.text.Collator; 56 import java.util.ArrayList; 57 import java.util.List; 58 import java.util.Objects; 59 import java.util.Set; 60 61 /** 62 * All permissions of a permission group that are requested by an app. 63 * 64 * <p>Some permissions only grant access to the protected resource while the app is running in the 65 * foreground. These permissions are considered "split" into this foreground and a matching 66 * "background" permission. 67 * 68 * <p>All background permissions of the group are not in the main group and will not be affected 69 * by operations on the group. The background permissions can be found in the {@link 70 * #getBackgroundPermissions() background permissions group}. 71 */ 72 public final class AppPermissionGroup implements Comparable<AppPermissionGroup> { 73 private static final String LOG_TAG = AppPermissionGroup.class.getSimpleName(); 74 private static final String PLATFORM_PACKAGE_NAME = "android"; 75 76 private static final String KILL_REASON_APP_OP_CHANGE = "Permission related app op changed"; 77 78 /** 79 * Importance level to define the threshold for whether a package is in a state which resets the 80 * timer on its one-time permission session 81 */ 82 private static final int ONE_TIME_PACKAGE_IMPORTANCE_LEVEL_TO_RESET_TIMER = 83 ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND; 84 85 /** 86 * Importance level to define the threshold for whether a package is in a state which keeps its 87 * one-time permission session alive after the timer ends 88 */ 89 private static final int ONE_TIME_PACKAGE_IMPORTANCE_LEVEL_TO_KEEP_SESSION_ALIVE = 90 ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE; 91 92 private final Context mContext; 93 private final UserHandle mUserHandle; 94 private final PackageManager mPackageManager; 95 private final AppOpsManager mAppOps; 96 private final ActivityManager mActivityManager; 97 private final Collator mCollator; 98 99 private final PackageInfo mPackageInfo; 100 private final String mName; 101 private final String mDeclaringPackage; 102 private final CharSequence mLabel; 103 private final CharSequence mFullLabel; 104 private final @StringRes int mRequest; 105 private final @StringRes int mRequestDetail; 106 private final @StringRes int mBackgroundRequest; 107 private final @StringRes int mBackgroundRequestDetail; 108 private final @StringRes int mUpgradeRequest; 109 private final @StringRes int mUpgradeRequestDetail; 110 private final CharSequence mDescription; 111 private final ArrayMap<String, Permission> mPermissions = new ArrayMap<>(); 112 private final String mIconPkg; 113 private final int mIconResId; 114 115 /** Delay changes until {@link #persistChanges} is called */ 116 private final boolean mDelayChanges; 117 118 /** 119 * Some permissions are split into foreground and background permission. All non-split and 120 * foreground permissions are in {@link #mPermissions}, all background permissions are in 121 * this field. 122 */ 123 private AppPermissionGroup mBackgroundPermissions; 124 125 private final boolean mAppSupportsRuntimePermissions; 126 private final boolean mIsEphemeralApp; 127 private final boolean mIsNonIsolatedStorage; 128 private boolean mContainsEphemeralPermission; 129 private boolean mContainsPreRuntimePermission; 130 131 /** 132 * Does this group contain at least one permission that is split into a foreground and 133 * background permission? This does not necessarily mean that the app also requested the 134 * background permission. 135 */ 136 private boolean mHasPermissionWithBackgroundMode; 137 138 /** 139 * Set if {@link LocationAccessCheck#checkLocationAccessSoon()} should be triggered once the 140 * changes are persisted. 141 */ 142 private boolean mTriggerLocationAccessCheckOnPersist; 143 144 /** 145 * Create the app permission group. 146 * 147 * @param context the {@code Context} to retrieve system services. 148 * @param packageInfo package information about the app. 149 * @param permissionName the name of the permission this object represents. 150 * @param delayChanges whether to delay changes until {@link #persistChanges} is called. 151 * 152 * @return the AppPermissionGroup. 153 */ create(Context context, PackageInfo packageInfo, String permissionName, boolean delayChanges)154 public static AppPermissionGroup create(Context context, PackageInfo packageInfo, 155 String permissionName, boolean delayChanges) { 156 PermissionInfo permissionInfo; 157 try { 158 permissionInfo = context.getPackageManager().getPermissionInfo(permissionName, 0); 159 } catch (PackageManager.NameNotFoundException e) { 160 return null; 161 } 162 163 if ((permissionInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE) 164 != PermissionInfo.PROTECTION_DANGEROUS 165 || (permissionInfo.flags & PermissionInfo.FLAG_INSTALLED) == 0 166 || (permissionInfo.flags & PermissionInfo.FLAG_REMOVED) != 0) { 167 return null; 168 } 169 170 String group = Utils.getGroupOfPermission(permissionInfo); 171 PackageItemInfo groupInfo = permissionInfo; 172 if (group != null) { 173 try { 174 groupInfo = context.getPackageManager().getPermissionGroupInfo(group, 0); 175 } catch (PackageManager.NameNotFoundException e) { 176 /* ignore */ 177 } 178 } 179 180 List<PermissionInfo> permissionInfos = null; 181 if (groupInfo instanceof PermissionGroupInfo) { 182 try { 183 permissionInfos = Utils.getPermissionInfosForGroup(context.getPackageManager(), 184 groupInfo.name); 185 } catch (PackageManager.NameNotFoundException e) { 186 /* ignore */ 187 } 188 } 189 190 return create(context, packageInfo, groupInfo, permissionInfos, delayChanges); 191 } 192 193 /** 194 * Create the app permission group. 195 * 196 * @param app the current application 197 * @param packageName the name of the package 198 * @param permissionGroupName the name of the permission group 199 * @param user the user of the package 200 * @param delayChanges whether to delay changes until {@link #persistChanges} is called. 201 * 202 * @return the AppPermissionGroup. 203 */ create(Application app, String packageName, String permissionGroupName, UserHandle user, boolean delayChanges)204 public static AppPermissionGroup create(Application app, String packageName, 205 String permissionGroupName, UserHandle user, boolean delayChanges) { 206 try { 207 PackageInfo packageInfo = Utils.getUserContext(app, user).getPackageManager() 208 .getPackageInfo(packageName, PackageManager.GET_PERMISSIONS); 209 PackageItemInfo groupInfo = Utils.getGroupInfo(permissionGroupName, app); 210 if (groupInfo == null) { 211 return null; 212 } 213 214 List<PermissionInfo> permissionInfos = null; 215 if (groupInfo instanceof PermissionGroupInfo) { 216 permissionInfos = Utils.getPermissionInfosForGroup(app.getPackageManager(), 217 groupInfo.name); 218 } 219 return create(app, packageInfo, groupInfo, permissionInfos, delayChanges); 220 } catch (PackageManager.NameNotFoundException e) { 221 return null; 222 } 223 } 224 225 /** 226 * Create the app permission group. 227 * 228 * @param context the {@code Context} to retrieve system services. 229 * @param packageInfo package information about the app. 230 * @param groupInfo the information about the group created. 231 * @param permissionInfos the information about the permissions belonging to the group. 232 * @param delayChanges whether to delay changes until {@link #persistChanges} is called. 233 * 234 * @return the AppPermissionGroup. 235 */ create(Context context, PackageInfo packageInfo, PackageItemInfo groupInfo, List<PermissionInfo> permissionInfos, boolean delayChanges)236 public static AppPermissionGroup create(Context context, PackageInfo packageInfo, 237 PackageItemInfo groupInfo, List<PermissionInfo> permissionInfos, boolean delayChanges) { 238 PackageManager packageManager = context.getPackageManager(); 239 CharSequence groupLabel = groupInfo.loadLabel(packageManager); 240 CharSequence fullGroupLabel = groupInfo.loadSafeLabel(packageManager, 0, 241 TextUtils.SAFE_STRING_FLAG_TRIM | TextUtils.SAFE_STRING_FLAG_FIRST_LINE); 242 return create(context, packageInfo, groupInfo, permissionInfos, groupLabel, 243 fullGroupLabel, delayChanges); 244 } 245 246 /** 247 * Create the app permission group. 248 * 249 * @param context the {@code Context} to retrieve system services. 250 * @param packageInfo package information about the app. 251 * @param groupInfo the information about the group created. 252 * @param permissionInfos the information about the permissions belonging to the group. 253 * @param groupLabel the label of the group. 254 * @param fullGroupLabel the untruncated label of the group. 255 * @param delayChanges whether to delay changes until {@link #persistChanges} is called. 256 * 257 * @return the AppPermissionGroup. 258 */ create(Context context, PackageInfo packageInfo, PackageItemInfo groupInfo, List<PermissionInfo> permissionInfos, CharSequence groupLabel, CharSequence fullGroupLabel, boolean delayChanges)259 public static AppPermissionGroup create(Context context, PackageInfo packageInfo, 260 PackageItemInfo groupInfo, List<PermissionInfo> permissionInfos, 261 CharSequence groupLabel, CharSequence fullGroupLabel, boolean delayChanges) { 262 PackageManager packageManager = context.getPackageManager(); 263 UserHandle userHandle = UserHandle.getUserHandleForUid(packageInfo.applicationInfo.uid); 264 265 if (groupInfo instanceof PermissionInfo) { 266 permissionInfos = new ArrayList<>(); 267 permissionInfos.add((PermissionInfo) groupInfo); 268 } 269 270 if (permissionInfos == null || permissionInfos.isEmpty()) { 271 return null; 272 } 273 274 AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class); 275 276 AppPermissionGroup group = new AppPermissionGroup(context, packageInfo, groupInfo.name, 277 groupInfo.packageName, groupLabel, fullGroupLabel, 278 loadGroupDescription(context, groupInfo, packageManager), getRequest(groupInfo), 279 getRequestDetail(groupInfo), getBackgroundRequest(groupInfo), 280 getBackgroundRequestDetail(groupInfo), getUpgradeRequest(groupInfo), 281 getUpgradeRequestDetail(groupInfo), groupInfo.packageName, groupInfo.icon, 282 userHandle, delayChanges, appOpsManager); 283 284 final Set<String> exemptedRestrictedPermissions = context.getPackageManager() 285 .getWhitelistedRestrictedPermissions(packageInfo.packageName, 286 Utils.FLAGS_PERMISSION_WHITELIST_ALL); 287 288 // Parse and create permissions reqested by the app 289 ArrayMap<String, Permission> allPermissions = new ArrayMap<>(); 290 final int permissionCount = packageInfo.requestedPermissions == null ? 0 291 : packageInfo.requestedPermissions.length; 292 String packageName = packageInfo.packageName; 293 for (int i = 0; i < permissionCount; i++) { 294 String requestedPermission = packageInfo.requestedPermissions[i]; 295 296 PermissionInfo requestedPermissionInfo = null; 297 298 for (PermissionInfo permissionInfo : permissionInfos) { 299 if (requestedPermission.equals(permissionInfo.name)) { 300 requestedPermissionInfo = permissionInfo; 301 break; 302 } 303 } 304 305 if (requestedPermissionInfo == null) { 306 continue; 307 } 308 309 // Collect only runtime permissions. 310 if ((requestedPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE) 311 != PermissionInfo.PROTECTION_DANGEROUS) { 312 continue; 313 } 314 315 // Don't allow toggling non-platform permission groups for legacy apps via app ops. 316 if (packageInfo.applicationInfo.targetSdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1 317 && !PLATFORM_PACKAGE_NAME.equals(groupInfo.packageName)) { 318 continue; 319 } 320 321 final boolean granted = (packageInfo.requestedPermissionsFlags[i] 322 & PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0; 323 324 final String appOp = PLATFORM_PACKAGE_NAME.equals(requestedPermissionInfo.packageName) 325 ? AppOpsManager.permissionToOp(requestedPermissionInfo.name) : null; 326 327 final boolean appOpAllowed; 328 if (appOp == null) { 329 appOpAllowed = false; 330 } else { 331 int appOpsMode = appOpsManager.unsafeCheckOpRaw(appOp, 332 packageInfo.applicationInfo.uid, packageName); 333 appOpAllowed = appOpsMode == MODE_ALLOWED || appOpsMode == MODE_FOREGROUND; 334 } 335 336 final int flags = packageManager.getPermissionFlags( 337 requestedPermission, packageName, userHandle); 338 339 Permission permission = new Permission(requestedPermission, requestedPermissionInfo, 340 granted, appOp, appOpAllowed, flags); 341 342 if (requestedPermissionInfo.backgroundPermission != null) { 343 group.mHasPermissionWithBackgroundMode = true; 344 } 345 346 allPermissions.put(requestedPermission, permission); 347 } 348 349 int numPermissions = allPermissions.size(); 350 if (numPermissions == 0) { 351 return null; 352 } 353 354 // Link up foreground and background permissions 355 for (int i = 0; i < allPermissions.size(); i++) { 356 Permission permission = allPermissions.valueAt(i); 357 358 if (permission.getBackgroundPermissionName() != null) { 359 Permission backgroundPermission = allPermissions.get( 360 permission.getBackgroundPermissionName()); 361 362 if (backgroundPermission != null) { 363 backgroundPermission.addForegroundPermissions(permission); 364 permission.setBackgroundPermission(backgroundPermission); 365 366 // The background permissions isAppOpAllowed refers to the background state of 367 // the foregound permission's appOp. Hence we can only set it once we know the 368 // matching foreground permission. 369 // @see #allowAppOp 370 if (context.getSystemService(AppOpsManager.class).unsafeCheckOpRaw( 371 permission.getAppOp(), packageInfo.applicationInfo.uid, 372 packageInfo.packageName) == MODE_ALLOWED) { 373 backgroundPermission.setAppOpAllowed(true); 374 } 375 } 376 } 377 } 378 379 // Add permissions found to this group 380 for (int i = 0; i < numPermissions; i++) { 381 Permission permission = allPermissions.valueAt(i); 382 383 if ((!permission.isHardRestricted() 384 || exemptedRestrictedPermissions.contains(permission.getName())) 385 && (!permission.isSoftRestricted() 386 || SoftRestrictedPermissionPolicy.shouldShow(packageInfo, permission))) { 387 if (permission.isBackgroundPermission()) { 388 if (group.getBackgroundPermissions() == null) { 389 group.mBackgroundPermissions = new AppPermissionGroup(group.mContext, 390 group.getApp(), group.getName(), group.getDeclaringPackage(), 391 group.getLabel(), group.getFullLabel(), group.getDescription(), 392 group.getRequest(), group.getRequestDetail(), 393 group.getBackgroundRequest(), group.getBackgroundRequestDetail(), 394 group.getUpgradeRequest(), group.getUpgradeRequestDetail(), 395 group.getIconPkg(), group.getIconResId(), group.getUser(), 396 delayChanges, appOpsManager); 397 } 398 399 group.getBackgroundPermissions().addPermission(permission); 400 } else { 401 group.addPermission(permission); 402 } 403 } 404 } 405 406 if (group.getPermissions().isEmpty()) { 407 return null; 408 } 409 410 return group; 411 } 412 getRequest(PackageItemInfo group)413 private static @StringRes int getRequest(PackageItemInfo group) { 414 return Utils.getRequest(group.name); 415 } 416 loadGroupDescription(Context context, PackageItemInfo group, @NonNull PackageManager packageManager)417 private static CharSequence loadGroupDescription(Context context, PackageItemInfo group, 418 @NonNull PackageManager packageManager) { 419 CharSequence description = null; 420 if (group instanceof PermissionGroupInfo) { 421 description = ((PermissionGroupInfo) group).loadDescription(packageManager); 422 } else if (group instanceof PermissionInfo) { 423 description = ((PermissionInfo) group).loadDescription(packageManager); 424 } 425 426 if (description == null || description.length() <= 0) { 427 description = context.getString(R.string.default_permission_description); 428 } 429 430 return description; 431 } 432 AppPermissionGroup(Context context, PackageInfo packageInfo, String name, String declaringPackage, CharSequence label, CharSequence fullLabel, CharSequence description, @StringRes int request, @StringRes int requestDetail, @StringRes int backgroundRequest, @StringRes int backgroundRequestDetail, @StringRes int upgradeRequest, @StringRes int upgradeRequestDetail, String iconPkg, int iconResId, UserHandle userHandle, boolean delayChanges, @NonNull AppOpsManager appOpsManager)433 private AppPermissionGroup(Context context, PackageInfo packageInfo, String name, 434 String declaringPackage, CharSequence label, CharSequence fullLabel, 435 CharSequence description, @StringRes int request, @StringRes int requestDetail, 436 @StringRes int backgroundRequest, @StringRes int backgroundRequestDetail, 437 @StringRes int upgradeRequest, @StringRes int upgradeRequestDetail, 438 String iconPkg, int iconResId, UserHandle userHandle, boolean delayChanges, 439 @NonNull AppOpsManager appOpsManager) { 440 int targetSDK = packageInfo.applicationInfo.targetSdkVersion; 441 442 mContext = context; 443 mUserHandle = userHandle; 444 mPackageManager = mContext.getPackageManager(); 445 mPackageInfo = packageInfo; 446 mAppSupportsRuntimePermissions = targetSDK > Build.VERSION_CODES.LOLLIPOP_MR1; 447 mIsEphemeralApp = packageInfo.applicationInfo.isInstantApp(); 448 mAppOps = appOpsManager; 449 mActivityManager = context.getSystemService(ActivityManager.class); 450 mDeclaringPackage = declaringPackage; 451 mName = name; 452 mLabel = label; 453 mFullLabel = fullLabel; 454 mDescription = description; 455 mCollator = Collator.getInstance( 456 context.getResources().getConfiguration().getLocales().get(0)); 457 mRequest = request; 458 mRequestDetail = requestDetail; 459 mBackgroundRequest = backgroundRequest; 460 mBackgroundRequestDetail = backgroundRequestDetail; 461 mUpgradeRequest = upgradeRequest; 462 mUpgradeRequestDetail = upgradeRequestDetail; 463 mDelayChanges = delayChanges; 464 if (iconResId != 0) { 465 mIconPkg = iconPkg; 466 mIconResId = iconResId; 467 } else { 468 mIconPkg = context.getPackageName(); 469 mIconResId = R.drawable.ic_perm_device_info; 470 } 471 472 mIsNonIsolatedStorage = targetSDK < Build.VERSION_CODES.P 473 || (targetSDK < Build.VERSION_CODES.R 474 && mAppOps.unsafeCheckOpNoThrow(OPSTR_LEGACY_STORAGE, 475 packageInfo.applicationInfo.uid, packageInfo.packageName) == MODE_ALLOWED); 476 } 477 478 public boolean doesSupportRuntimePermissions() { 479 return mAppSupportsRuntimePermissions; 480 } 481 482 public boolean isGrantingAllowed() { 483 return (!mIsEphemeralApp || mContainsEphemeralPermission) 484 && (mAppSupportsRuntimePermissions || mContainsPreRuntimePermission); 485 } 486 487 public boolean isReviewRequired() { 488 if (mAppSupportsRuntimePermissions) { 489 return false; 490 } 491 final int permissionCount = mPermissions.size(); 492 for (int i = 0; i < permissionCount; i++) { 493 Permission permission = mPermissions.valueAt(i); 494 if (permission.isReviewRequired()) { 495 return true; 496 } 497 } 498 return false; 499 } 500 501 /** 502 * Are any of the permissions in this group user sensitive. 503 * 504 * @return {@code true} if any of the permissions in the group is user sensitive. 505 */ 506 public boolean isUserSensitive() { 507 final int permissionCount = mPermissions.size(); 508 for (int i = 0; i < permissionCount; i++) { 509 Permission permission = mPermissions.valueAt(i); 510 if (permission.isUserSensitive()) { 511 return true; 512 } 513 } 514 return false; 515 } 516 517 public void unsetReviewRequired() { 518 final int permissionCount = mPermissions.size(); 519 for (int i = 0; i < permissionCount; i++) { 520 Permission permission = mPermissions.valueAt(i); 521 if (permission.isReviewRequired()) { 522 permission.unsetReviewRequired(); 523 } 524 } 525 526 if (!mDelayChanges) { 527 persistChanges(false); 528 } 529 } 530 531 public boolean hasGrantedByDefaultPermission() { 532 final int permissionCount = mPermissions.size(); 533 for (int i = 0; i < permissionCount; i++) { 534 Permission permission = mPermissions.valueAt(i); 535 if (permission.isGrantedByDefault()) { 536 return true; 537 } 538 } 539 return false; 540 } 541 542 public PackageInfo getApp() { 543 return mPackageInfo; 544 } 545 546 public String getName() { 547 return mName; 548 } 549 550 public String getDeclaringPackage() { 551 return mDeclaringPackage; 552 } 553 554 public String getIconPkg() { 555 return mIconPkg; 556 } 557 558 public int getIconResId() { 559 return mIconResId; 560 } 561 562 public CharSequence getLabel() { 563 return mLabel; 564 } 565 566 /** 567 * Get the full un-ellipsized label of the permission group. 568 * 569 * @return the full label of the group. 570 */ 571 public CharSequence getFullLabel() { 572 return mFullLabel; 573 } 574 575 /** 576 * @hide 577 * @return The resource Id of the request string. 578 */ 579 public @StringRes int getRequest() { 580 return mRequest; 581 } 582 583 /** 584 * Extract the (subtitle) message explaining to the user that the permission is only granted to 585 * the apps running in the foreground. 586 * 587 * @param info The package item info to extract the message from 588 * 589 * @return the message or 0 if unset 590 */ 591 private static @StringRes int getRequestDetail(PackageItemInfo info) { 592 return Utils.getRequestDetail(info.name); 593 } 594 595 /** 596 * Get the (subtitle) message explaining to the user that the permission is only granted to 597 * the apps running in the foreground. 598 * 599 * @return the message or 0 if unset 600 */ 601 public @StringRes int getRequestDetail() { 602 return mRequestDetail; 603 } 604 605 /** 606 * Extract the title of the dialog explaining to the user that the permission is granted while 607 * the app is in background and in foreground. 608 * 609 * @param info The package item info to extract the message from 610 * 611 * @return the message or 0 if unset 612 */ 613 private static @StringRes int getBackgroundRequest(PackageItemInfo info) { 614 return Utils.getBackgroundRequest(info.name); 615 } 616 617 /** 618 * Get the title of the dialog explaining to the user that the permission is granted while 619 * the app is in background and in foreground. 620 * 621 * @return the message or 0 if unset 622 */ 623 public @StringRes int getBackgroundRequest() { 624 return mBackgroundRequest; 625 } 626 627 /** 628 * Extract the (subtitle) message explaining to the user that the she/he is about to allow the 629 * app to have background access. 630 * 631 * @param info The package item info to extract the message from 632 * 633 * @return the message or 0 if unset 634 */ 635 private static @StringRes int getBackgroundRequestDetail(PackageItemInfo info) { 636 return Utils.getBackgroundRequestDetail(info.name); 637 } 638 639 /** 640 * Get the (subtitle) message explaining to the user that the she/he is about to allow the 641 * app to have background access. 642 * 643 * @return the message or 0 if unset 644 */ 645 public @StringRes int getBackgroundRequestDetail() { 646 return mBackgroundRequestDetail; 647 } 648 649 /** 650 * Extract the title of the dialog explaining to the user that the permission, which was 651 * previously only granted for foreground, is granted while the app is in background and in 652 * foreground. 653 * 654 * @param info The package item info to extract the message from 655 * 656 * @return the message or 0 if unset 657 */ 658 private static @StringRes int getUpgradeRequest(PackageItemInfo info) { 659 return Utils.getUpgradeRequest(info.name); 660 } 661 662 /** 663 * Get the title of the dialog explaining to the user that the permission, which was 664 * previously only granted for foreground, is granted while the app is in background and in 665 * foreground. 666 * 667 * @return the message or 0 if unset 668 */ 669 public @StringRes int getUpgradeRequest() { 670 return mUpgradeRequest; 671 } 672 673 /** 674 * Extract the (subtitle) message explaining to the user that the she/he is about to allow the 675 * app to have background access while currently having foreground only. 676 * 677 * @param info The package item info to extract the message from 678 * 679 * @return the message or 0 if unset 680 */ 681 private static @StringRes int getUpgradeRequestDetail(PackageItemInfo info) { 682 return Utils.getUpgradeRequestDetail(info.name); 683 } 684 685 /** 686 * Get the (subtitle) message explaining to the user that the she/he is about to allow the 687 * app to have background access while currently having foreground only. 688 * 689 * @return the message or 0 if unset 690 */ 691 public @StringRes int getUpgradeRequestDetail() { 692 return mUpgradeRequestDetail; 693 } 694 695 public CharSequence getDescription() { 696 return mDescription; 697 } 698 699 public UserHandle getUser() { 700 return mUserHandle; 701 } 702 703 public boolean hasPermission(String permission) { 704 return mPermissions.get(permission) != null; 705 } 706 707 /** 708 * Return a permission if in this group. 709 * 710 * @param permissionName The name of the permission 711 * 712 * @return The permission 713 */ 714 public @Nullable Permission getPermission(@NonNull String permissionName) { 715 return mPermissions.get(permissionName); 716 } 717 718 public boolean areRuntimePermissionsGranted() { 719 return areRuntimePermissionsGranted(null); 720 } 721 722 public boolean areRuntimePermissionsGranted(String[] filterPermissions) { 723 if (LocationUtils.isLocationGroupAndProvider(mContext, mName, mPackageInfo.packageName)) { 724 return LocationUtils.isLocationEnabled(mContext); 725 } 726 // The permission of the extra location controller package is determined by the status of 727 // the controller package itself. 728 if (LocationUtils.isLocationGroupAndControllerExtraPackage( 729 mContext, mName, mPackageInfo.packageName)) { 730 return LocationUtils.isExtraLocationControllerPackageEnabled(mContext); 731 } 732 final int permissionCount = mPermissions.size(); 733 for (int i = 0; i < permissionCount; i++) { 734 Permission permission = mPermissions.valueAt(i); 735 if (filterPermissions != null 736 && !ArrayUtils.contains(filterPermissions, permission.getName())) { 737 continue; 738 } 739 if (permission.isGrantedIncludingAppOp()) { 740 return true; 741 } 742 } 743 return false; 744 } 745 746 public boolean grantRuntimePermissions(boolean setByTheUser, boolean fixedByTheUser) { 747 return grantRuntimePermissions(setByTheUser, fixedByTheUser, null); 748 } 749 750 /** 751 * Set mode of an app-op if needed. 752 * 753 * @param op The op to set 754 * @param uid The uid the app-op belongs to 755 * @param mode The new mode 756 * 757 * @return {@code true} iff app-op was changed 758 */ 759 private boolean setAppOpMode(@NonNull String op, int uid, int mode) { 760 int currentMode = mAppOps.unsafeCheckOpRaw(op, uid, mPackageInfo.packageName); 761 if (currentMode == mode) { 762 return false; 763 } 764 765 mAppOps.setUidMode(op, uid, mode); 766 return true; 767 } 768 769 /** 770 * Allow the app op for a permission/uid. 771 * 772 * <p>There are three cases: 773 * <dl> 774 * <dt>The permission is not split into foreground/background</dt> 775 * <dd>The app op matching the permission will be set to {@link AppOpsManager#MODE_ALLOWED}</dd> 776 * <dt>The permission is a foreground permission:</dt> 777 * <dd><dl><dt>The background permission permission is granted</dt> 778 * <dd>The app op matching the permission will be set to {@link AppOpsManager#MODE_ALLOWED}</dd> 779 * <dt>The background permission permission is <u>not</u> granted</dt> 780 * <dd>The app op matching the permission will be set to 781 * {@link AppOpsManager#MODE_FOREGROUND}</dd> 782 * </dl></dd> 783 * <dt>The permission is a background permission:</dt> 784 * <dd>All granted foreground permissions for this background permission will be set to 785 * {@link AppOpsManager#MODE_ALLOWED}</dd> 786 * </dl> 787 * 788 * @param permission The permission which has an appOps that should be allowed 789 * @param uid The uid of the process the app op is for 790 * 791 * @return {@code true} iff app-op was changed 792 */ 793 private boolean allowAppOp(Permission permission, int uid) { 794 boolean wasChanged = false; 795 796 if (permission.isBackgroundPermission()) { 797 ArrayList<Permission> foregroundPermissions = permission.getForegroundPermissions(); 798 799 int numForegroundPermissions = foregroundPermissions.size(); 800 for (int i = 0; i < numForegroundPermissions; i++) { 801 Permission foregroundPermission = foregroundPermissions.get(i); 802 if (foregroundPermission.isAppOpAllowed()) { 803 wasChanged |= setAppOpMode(foregroundPermission.getAppOp(), uid, MODE_ALLOWED); 804 } 805 } 806 } else { 807 if (permission.hasBackgroundPermission()) { 808 Permission backgroundPermission = permission.getBackgroundPermission(); 809 810 if (backgroundPermission == null) { 811 // The app requested a permission that has a background permission but it did 812 // not request the background permission, hence it can never get background 813 // access 814 wasChanged = setAppOpMode(permission.getAppOp(), uid, MODE_FOREGROUND); 815 } else { 816 if (backgroundPermission.isAppOpAllowed()) { 817 wasChanged = setAppOpMode(permission.getAppOp(), uid, MODE_ALLOWED); 818 } else { 819 wasChanged = setAppOpMode(permission.getAppOp(), uid, MODE_FOREGROUND); 820 } 821 } 822 } else { 823 wasChanged = setAppOpMode(permission.getAppOp(), uid, MODE_ALLOWED); 824 } 825 } 826 827 return wasChanged; 828 } 829 830 /** 831 * Kills the app the permissions belong to (and all apps sharing the same uid) 832 * 833 * @param reason The reason why the apps are killed 834 */ 835 private void killApp(String reason) { 836 mActivityManager.killUid(mPackageInfo.applicationInfo.uid, reason); 837 } 838 839 /** 840 * Grant permissions of the group. 841 * 842 * <p>This also automatically grants all app ops for permissions that have app ops. 843 * <p>This does <u>only</u> grant permissions in {@link #mPermissions}, i.e. usually not 844 * the background permissions. 845 * 846 * @param setByTheUser If the user has made the decision. This does not unset the flag 847 * @param fixedByTheUser If the user requested that she/he does not want to be asked again 848 * @param filterPermissions If {@code null} all permissions of the group will be granted. 849 * Otherwise only permissions in {@code filterPermissions} will be 850 * granted. 851 * 852 * @return {@code true} iff all permissions of this group could be granted. 853 */ 854 public boolean grantRuntimePermissions(boolean setByTheUser, boolean fixedByTheUser, 855 String[] filterPermissions) { 856 boolean killApp = false; 857 boolean wasAllGranted = true; 858 859 // We toggle permissions only to apps that support runtime 860 // permissions, otherwise we toggle the app op corresponding 861 // to the permission if the permission is granted to the app. 862 for (Permission permission : mPermissions.values()) { 863 if (filterPermissions != null 864 && !ArrayUtils.contains(filterPermissions, permission.getName())) { 865 continue; 866 } 867 868 if (!permission.isGrantingAllowed(mIsEphemeralApp, mAppSupportsRuntimePermissions)) { 869 // Skip unallowed permissions. 870 continue; 871 } 872 873 boolean wasGranted = permission.isGrantedIncludingAppOp(); 874 875 if (mAppSupportsRuntimePermissions) { 876 // Do not touch permissions fixed by the system. 877 if (permission.isSystemFixed()) { 878 wasAllGranted = false; 879 break; 880 } 881 882 // Ensure the permission app op is enabled before the permission grant. 883 if (permission.affectsAppOp() && !permission.isAppOpAllowed()) { 884 permission.setAppOpAllowed(true); 885 } 886 887 // Grant the permission if needed. 888 if (!permission.isGranted()) { 889 permission.setGranted(true); 890 } 891 892 // Update the permission flags. 893 if (!fixedByTheUser) { 894 if (permission.isUserFixed()) { 895 permission.setUserFixed(false); 896 } 897 if (setByTheUser) { 898 if (!permission.isUserSet()) { 899 permission.setUserSet(true); 900 } 901 } 902 } else { 903 if (!permission.isUserFixed()) { 904 permission.setUserFixed(true); 905 } 906 if (permission.isUserSet()) { 907 permission.setUserSet(false); 908 } 909 } 910 } else { 911 // Legacy apps cannot have a not granted permission but just in case. 912 if (!permission.isGranted()) { 913 continue; 914 } 915 916 // If the permissions has no corresponding app op, then it is a 917 // third-party one and we do not offer toggling of such permissions. 918 if (permission.affectsAppOp()) { 919 if (!permission.isAppOpAllowed()) { 920 permission.setAppOpAllowed(true); 921 922 // Legacy apps do not know that they have to retry access to a 923 // resource due to changes in runtime permissions (app ops in this 924 // case). Therefore, we restart them on app op change, so they 925 // can pick up the change. 926 killApp = true; 927 } 928 929 // Mark that the permission is not kept granted only for compatibility. 930 if (permission.isRevokedCompat()) { 931 permission.setRevokedCompat(false); 932 } 933 } 934 935 // Granting a permission explicitly means the user already 936 // reviewed it so clear the review flag on every grant. 937 if (permission.isReviewRequired()) { 938 permission.unsetReviewRequired(); 939 } 940 } 941 942 // If we newly grant background access to the fine location, double-guess the user some 943 // time later if this was really the right choice. 944 if (!wasGranted && permission.isGrantedIncludingAppOp()) { 945 if (permission.getName().equals(ACCESS_FINE_LOCATION)) { 946 Permission bgPerm = permission.getBackgroundPermission(); 947 if (bgPerm != null) { 948 if (bgPerm.isGrantedIncludingAppOp()) { 949 mTriggerLocationAccessCheckOnPersist = true; 950 } 951 } 952 } else if (permission.getName().equals(ACCESS_BACKGROUND_LOCATION)) { 953 ArrayList<Permission> fgPerms = permission.getForegroundPermissions(); 954 if (fgPerms != null) { 955 int numFgPerms = fgPerms.size(); 956 for (int fgPermNum = 0; fgPermNum < numFgPerms; fgPermNum++) { 957 Permission fgPerm = fgPerms.get(fgPermNum); 958 959 if (fgPerm.getName().equals(ACCESS_FINE_LOCATION)) { 960 if (fgPerm.isGrantedIncludingAppOp()) { 961 mTriggerLocationAccessCheckOnPersist = true; 962 } 963 964 break; 965 } 966 } 967 } 968 } 969 } 970 } 971 972 if (!mDelayChanges) { 973 persistChanges(false); 974 975 if (killApp) { 976 killApp(KILL_REASON_APP_OP_CHANGE); 977 } 978 } 979 980 return wasAllGranted; 981 } 982 983 public boolean revokeRuntimePermissions(boolean fixedByTheUser) { 984 return revokeRuntimePermissions(fixedByTheUser, null); 985 } 986 987 /** 988 * Disallow the app op for a permission/uid. 989 * 990 * <p>There are three cases: 991 * <dl> 992 * <dt>The permission is not split into foreground/background</dt> 993 * <dd>The app op matching the permission will be set to {@link AppOpsManager#MODE_IGNORED}</dd> 994 * <dt>The permission is a foreground permission:</dt> 995 * <dd>The app op matching the permission will be set to {@link AppOpsManager#MODE_IGNORED}</dd> 996 * <dt>The permission is a background permission:</dt> 997 * <dd>All granted foreground permissions for this background permission will be set to 998 * {@link AppOpsManager#MODE_FOREGROUND}</dd> 999 * </dl> 1000 * 1001 * @param permission The permission which has an appOps that should be disallowed 1002 * @param uid The uid of the process the app op if for 1003 * 1004 * @return {@code true} iff app-op was changed 1005 */ 1006 private boolean disallowAppOp(Permission permission, int uid) { 1007 boolean wasChanged = false; 1008 1009 if (permission.isBackgroundPermission()) { 1010 ArrayList<Permission> foregroundPermissions = permission.getForegroundPermissions(); 1011 1012 int numForegroundPermissions = foregroundPermissions.size(); 1013 for (int i = 0; i < numForegroundPermissions; i++) { 1014 Permission foregroundPermission = foregroundPermissions.get(i); 1015 if (foregroundPermission.isAppOpAllowed()) { 1016 wasChanged |= setAppOpMode(foregroundPermission.getAppOp(), uid, 1017 MODE_FOREGROUND); 1018 } 1019 } 1020 } else { 1021 wasChanged = setAppOpMode(permission.getAppOp(), uid, MODE_IGNORED); 1022 } 1023 1024 return wasChanged; 1025 } 1026 1027 /** 1028 * Revoke permissions of the group. 1029 * 1030 * <p>This also disallows all app ops for permissions that have app ops. 1031 * <p>This does <u>only</u> revoke permissions in {@link #mPermissions}, i.e. usually not 1032 * the background permissions. 1033 * 1034 * @param fixedByTheUser If the user requested that she/he does not want to be asked again 1035 * @param filterPermissions If {@code null} all permissions of the group will be revoked. 1036 * Otherwise only permissions in {@code filterPermissions} will be 1037 * revoked. 1038 * 1039 * @return {@code true} iff all permissions of this group could be revoked. 1040 */ 1041 public boolean revokeRuntimePermissions(boolean fixedByTheUser, String[] filterPermissions) { 1042 boolean killApp = false; 1043 boolean wasAllRevoked = true; 1044 1045 // We toggle permissions only to apps that support runtime 1046 // permissions, otherwise we toggle the app op corresponding 1047 // to the permission if the permission is granted to the app. 1048 for (Permission permission : mPermissions.values()) { 1049 if (filterPermissions != null 1050 && !ArrayUtils.contains(filterPermissions, permission.getName())) { 1051 continue; 1052 } 1053 1054 // Do not touch permissions fixed by the system. 1055 if (permission.isSystemFixed()) { 1056 wasAllRevoked = false; 1057 break; 1058 } 1059 1060 if (mAppSupportsRuntimePermissions) { 1061 // Revoke the permission if needed. 1062 if (permission.isGranted()) { 1063 permission.setGranted(false); 1064 } 1065 1066 // Update the permission flags. 1067 if (fixedByTheUser) { 1068 // Take a note that the user fixed the permission. 1069 if (permission.isUserSet() || !permission.isUserFixed()) { 1070 permission.setUserSet(false); 1071 permission.setUserFixed(true); 1072 } 1073 } else { 1074 if (!permission.isUserSet() || permission.isUserFixed()) { 1075 permission.setUserSet(true); 1076 permission.setUserFixed(false); 1077 } 1078 } 1079 1080 if (permission.affectsAppOp()) { 1081 permission.setAppOpAllowed(false); 1082 } 1083 } else { 1084 // Legacy apps cannot have a non-granted permission but just in case. 1085 if (!permission.isGranted()) { 1086 continue; 1087 } 1088 1089 // If the permission has no corresponding app op, then it is a 1090 // third-party one and we do not offer toggling of such permissions. 1091 if (permission.affectsAppOp()) { 1092 if (permission.isAppOpAllowed()) { 1093 permission.setAppOpAllowed(false); 1094 1095 // Disabling an app op may put the app in a situation in which it 1096 // has a handle to state it shouldn't have, so we have to kill the 1097 // app. This matches the revoke runtime permission behavior. 1098 killApp = true; 1099 } 1100 1101 // Mark that the permission is kept granted only for compatibility. 1102 if (!permission.isRevokedCompat()) { 1103 permission.setRevokedCompat(true); 1104 } 1105 } 1106 } 1107 } 1108 1109 if (!mDelayChanges) { 1110 persistChanges(false); 1111 1112 if (killApp) { 1113 killApp(KILL_REASON_APP_OP_CHANGE); 1114 } 1115 } 1116 1117 return wasAllRevoked; 1118 } 1119 1120 /** 1121 * Mark permissions in this group as policy fixed. 1122 * 1123 * @param filterPermissions The permissions to mark 1124 */ 1125 public void setPolicyFixed(@NonNull String[] filterPermissions) { 1126 for (String permissionName : filterPermissions) { 1127 Permission permission = mPermissions.get(permissionName); 1128 1129 if (permission != null) { 1130 permission.setPolicyFixed(true); 1131 } 1132 } 1133 1134 if (!mDelayChanges) { 1135 persistChanges(false); 1136 } 1137 } 1138 1139 /** 1140 * Set the user-fixed flag for all permissions in this group. 1141 * 1142 * @param isUsedFixed if the flag should be set or not 1143 */ 1144 public void setUserFixed(boolean isUsedFixed) { 1145 final int permissionCount = mPermissions.size(); 1146 for (int i = 0; i < permissionCount; i++) { 1147 Permission permission = mPermissions.valueAt(i); 1148 permission.setUserFixed(isUsedFixed); 1149 } 1150 1151 if (!mDelayChanges) { 1152 persistChanges(false); 1153 } 1154 } 1155 1156 /** 1157 * Set the one-time flag for all permissions in this group. 1158 * 1159 * @param isOneTime if the flag should be set or not 1160 */ 1161 public void setOneTime(boolean isOneTime) { 1162 final int permissionCount = mPermissions.size(); 1163 for (int i = 0; i < permissionCount; i++) { 1164 Permission permission = mPermissions.valueAt(i); 1165 if (!permission.isBackgroundPermission()) { 1166 permission.setOneTime(isOneTime); 1167 } 1168 } 1169 1170 if (!mDelayChanges) { 1171 persistChanges(false); 1172 } 1173 } 1174 1175 /** 1176 * Set the user-set flag for all permissions in this group. 1177 * 1178 * @param isUserSet if the flag should be set or not 1179 */ 1180 public void setUserSet(boolean isUserSet) { 1181 final int permissionCount = mPermissions.size(); 1182 for (int i = 0; i < permissionCount; i++) { 1183 Permission permission = mPermissions.valueAt(i); 1184 permission.setUserSet(isUserSet); 1185 } 1186 1187 if (!mDelayChanges) { 1188 persistChanges(false); 1189 } 1190 } 1191 1192 public ArrayList<Permission> getPermissions() { 1193 return new ArrayList<>(mPermissions.values()); 1194 } 1195 1196 /** 1197 * @return An {@link AppPermissionGroup}-object that contains all background permissions for 1198 * this group. 1199 */ 1200 public AppPermissionGroup getBackgroundPermissions() { 1201 return mBackgroundPermissions; 1202 } 1203 1204 /** 1205 * @return {@code true} iff the app request at least one permission in this group that has a 1206 * background permission. It is possible that the app does not request the matching background 1207 * permission and hence will only ever get foreground access, never background access. 1208 */ 1209 public boolean hasPermissionWithBackgroundMode() { 1210 return mHasPermissionWithBackgroundMode; 1211 } 1212 1213 /** 1214 * Is the group a storage permission group that is referring to an app that does not have 1215 * isolated storage 1216 * 1217 * @return {@code true} iff this is a storage group on an app that does not have isolated 1218 * storage 1219 */ 1220 public boolean isNonIsolatedStorage() { 1221 return mIsNonIsolatedStorage; 1222 } 1223 1224 /** 1225 * Whether this is group that contains all the background permission for regular permission 1226 * group. 1227 * 1228 * @return {@code true} iff this is a background permission group. 1229 * 1230 * @see #getBackgroundPermissions() 1231 */ 1232 public boolean isBackgroundGroup() { 1233 return mPermissions.valueAt(0).isBackgroundPermission(); 1234 } 1235 1236 /** 1237 * Whether this group supports one-time permissions 1238 * @return {@code true} iff this group supports one-time permissions 1239 */ 1240 public boolean supportsOneTimeGrant() { 1241 return Utils.supportsOneTimeGrant(getName()); 1242 } 1243 1244 public int getFlags() { 1245 int flags = 0; 1246 final int permissionCount = mPermissions.size(); 1247 for (int i = 0; i < permissionCount; i++) { 1248 Permission permission = mPermissions.valueAt(i); 1249 flags |= permission.getFlags(); 1250 } 1251 return flags; 1252 } 1253 1254 public boolean isUserFixed() { 1255 final int permissionCount = mPermissions.size(); 1256 for (int i = 0; i < permissionCount; i++) { 1257 Permission permission = mPermissions.valueAt(i); 1258 if (permission.isUserFixed()) { 1259 return true; 1260 } 1261 } 1262 return false; 1263 } 1264 1265 public boolean isPolicyFixed() { 1266 final int permissionCount = mPermissions.size(); 1267 for (int i = 0; i < permissionCount; i++) { 1268 Permission permission = mPermissions.valueAt(i); 1269 if (permission.isPolicyFixed()) { 1270 return true; 1271 } 1272 } 1273 return false; 1274 } 1275 1276 public boolean isUserSet() { 1277 final int permissionCount = mPermissions.size(); 1278 for (int i = 0; i < permissionCount; i++) { 1279 Permission permission = mPermissions.valueAt(i); 1280 if (permission.isUserSet()) { 1281 return true; 1282 } 1283 } 1284 return false; 1285 } 1286 1287 public boolean isSystemFixed() { 1288 final int permissionCount = mPermissions.size(); 1289 for (int i = 0; i < permissionCount; i++) { 1290 Permission permission = mPermissions.valueAt(i); 1291 if (permission.isSystemFixed()) { 1292 return true; 1293 } 1294 } 1295 return false; 1296 } 1297 1298 /** 1299 * @return Whether any of the permissions in this group is one-time 1300 */ 1301 public boolean isOneTime() { 1302 final int permissionCount = mPermissions.size(); 1303 for (int i = 0; i < permissionCount; i++) { 1304 Permission permission = mPermissions.valueAt(i); 1305 if (permission.isOneTime()) { 1306 return true; 1307 } 1308 } 1309 return false; 1310 } 1311 1312 @Override 1313 public int compareTo(AppPermissionGroup another) { 1314 final int result = mCollator.compare(mLabel.toString(), another.mLabel.toString()); 1315 if (result == 0) { 1316 // Unbadged before badged. 1317 return mPackageInfo.applicationInfo.uid 1318 - another.mPackageInfo.applicationInfo.uid; 1319 } 1320 return result; 1321 } 1322 1323 @Override 1324 public boolean equals(Object o) { 1325 if (!(o instanceof AppPermissionGroup)) { 1326 return false; 1327 } 1328 1329 AppPermissionGroup other = (AppPermissionGroup) o; 1330 1331 boolean equal = mName.equals(other.mName) 1332 && mPackageInfo.packageName.equals(other.mPackageInfo.packageName) 1333 && mUserHandle.equals(other.mUserHandle) 1334 && mPermissions.equals(other.mPermissions); 1335 if (!equal) { 1336 return false; 1337 } 1338 1339 if (mBackgroundPermissions != null && other.getBackgroundPermissions() != null) { 1340 return mBackgroundPermissions.getPermissions().equals( 1341 other.getBackgroundPermissions().getPermissions()); 1342 } 1343 return mBackgroundPermissions == other.getBackgroundPermissions(); 1344 } 1345 1346 @Override 1347 public int hashCode() { 1348 ArrayList<Permission> backgroundPermissions = new ArrayList<>(); 1349 if (mBackgroundPermissions != null) { 1350 backgroundPermissions = mBackgroundPermissions.getPermissions(); 1351 } 1352 return Objects.hash(mName, mPackageInfo.packageName, mUserHandle, mPermissions, 1353 backgroundPermissions); 1354 } 1355 1356 @Override 1357 public String toString() { 1358 StringBuilder builder = new StringBuilder(); 1359 builder.append(getClass().getSimpleName()); 1360 builder.append("{name=").append(mName); 1361 if (mBackgroundPermissions != null) { 1362 builder.append(", <has background permissions>}"); 1363 } 1364 if (!mPermissions.isEmpty()) { 1365 builder.append(", <has permissions>}"); 1366 } else { 1367 builder.append('}'); 1368 } 1369 return builder.toString(); 1370 } 1371 1372 private void addPermission(Permission permission) { 1373 mPermissions.put(permission.getName(), permission); 1374 if (permission.isEphemeral()) { 1375 mContainsEphemeralPermission = true; 1376 } 1377 if (!permission.isRuntimeOnly()) { 1378 mContainsPreRuntimePermission = true; 1379 } 1380 } 1381 1382 /** 1383 * If the changes to this group were delayed, persist them to the platform. 1384 * 1385 * @param mayKillBecauseOfAppOpsChange If the app these permissions belong to may be killed if 1386 * app ops change. If this is set to {@code false} the 1387 * caller has to make sure to kill the app if needed. 1388 */ 1389 public void persistChanges(boolean mayKillBecauseOfAppOpsChange) { 1390 persistChanges(mayKillBecauseOfAppOpsChange, null); 1391 } 1392 1393 1394 /** 1395 * If the changes to this group were delayed, persist them to the platform. 1396 * 1397 * @param mayKillBecauseOfAppOpsChange If the app these permissions belong to may be killed if 1398 * app ops change. If this is set to {@code false} the 1399 * caller has to make sure to kill the app if needed. 1400 * @param revokeReason If any permissions are getting revoked, the reason for revoking them. 1401 */ 1402 public void persistChanges(boolean mayKillBecauseOfAppOpsChange, String revokeReason) { 1403 int uid = mPackageInfo.applicationInfo.uid; 1404 1405 int numPermissions = mPermissions.size(); 1406 boolean shouldKillApp = false; 1407 1408 for (int i = 0; i < numPermissions; i++) { 1409 Permission permission = mPermissions.valueAt(i); 1410 1411 if (!permission.isSystemFixed()) { 1412 if (permission.isGranted()) { 1413 mPackageManager.grantRuntimePermission(mPackageInfo.packageName, 1414 permission.getName(), mUserHandle); 1415 } else { 1416 boolean isCurrentlyGranted = mContext.checkPermission(permission.getName(), -1, 1417 uid) == PERMISSION_GRANTED; 1418 1419 if (isCurrentlyGranted) { 1420 if (revokeReason == null) { 1421 mPackageManager.revokeRuntimePermission(mPackageInfo.packageName, 1422 permission.getName(), mUserHandle); 1423 } else { 1424 mPackageManager.revokeRuntimePermission(mPackageInfo.packageName, 1425 permission.getName(), mUserHandle, revokeReason); 1426 } 1427 } 1428 } 1429 } 1430 1431 int flags = (permission.isUserSet() ? PackageManager.FLAG_PERMISSION_USER_SET : 0) 1432 | (permission.isUserFixed() ? PackageManager.FLAG_PERMISSION_USER_FIXED : 0) 1433 | (permission.isRevokedCompat() 1434 ? PackageManager.FLAG_PERMISSION_REVOKED_COMPAT : 0) 1435 | (permission.isPolicyFixed() ? PackageManager.FLAG_PERMISSION_POLICY_FIXED : 0) 1436 | (permission.isReviewRequired() 1437 ? PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED : 0) 1438 | (permission.isOneTime() ? PackageManager.FLAG_PERMISSION_ONE_TIME : 0); 1439 1440 mPackageManager.updatePermissionFlags(permission.getName(), 1441 mPackageInfo.packageName, 1442 PackageManager.FLAG_PERMISSION_USER_SET 1443 | PackageManager.FLAG_PERMISSION_USER_FIXED 1444 | PackageManager.FLAG_PERMISSION_REVOKED_COMPAT 1445 | PackageManager.FLAG_PERMISSION_POLICY_FIXED 1446 | (permission.isReviewRequired() 1447 ? 0 : PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) 1448 | PackageManager.FLAG_PERMISSION_ONE_TIME 1449 | PackageManager.FLAG_PERMISSION_AUTO_REVOKED, // clear auto revoke 1450 flags, mUserHandle); 1451 1452 if (permission.affectsAppOp()) { 1453 if (!permission.isSystemFixed()) { 1454 // Enabling/Disabling an app op may put the app in a situation in which it has 1455 // a handle to state it shouldn't have, so we have to kill the app. This matches 1456 // the revoke runtime permission behavior. 1457 if (permission.isAppOpAllowed()) { 1458 boolean wasChanged = allowAppOp(permission, uid); 1459 shouldKillApp |= wasChanged && !mAppSupportsRuntimePermissions; 1460 } else { 1461 shouldKillApp |= disallowAppOp(permission, uid); 1462 } 1463 } 1464 } 1465 } 1466 1467 if (mayKillBecauseOfAppOpsChange && shouldKillApp) { 1468 killApp(KILL_REASON_APP_OP_CHANGE); 1469 } 1470 1471 if (mTriggerLocationAccessCheckOnPersist) { 1472 new LocationAccessCheck(mContext, null).checkLocationAccessSoon(); 1473 mTriggerLocationAccessCheckOnPersist = false; 1474 } 1475 1476 String packageName = mPackageInfo.packageName; 1477 if (isOneTime() && areRuntimePermissionsGranted()) { 1478 mContext.getSystemService(PermissionManager.class) 1479 .startOneTimePermissionSession(packageName, 1480 Utils.getOneTimePermissionsTimeout(), 1481 ONE_TIME_PACKAGE_IMPORTANCE_LEVEL_TO_RESET_TIMER, 1482 ONE_TIME_PACKAGE_IMPORTANCE_LEVEL_TO_KEEP_SESSION_ALIVE); 1483 } else if (!Utils.hasOneTimePermissions(mContext, packageName)) { 1484 mContext.getSystemService(PermissionManager.class) 1485 .stopOneTimePermissionSession(packageName); 1486 } 1487 } 1488 1489 /** 1490 * Check if permission group contains a runtime permission that split from an installed 1491 * permission and the split happened in an Android version higher than app's targetSdk. 1492 * 1493 * @return {@code true} if there is such permission, {@code false} otherwise 1494 */ 1495 public boolean hasInstallToRuntimeSplit() { 1496 PermissionManager permissionManager = 1497 (PermissionManager) mContext.getSystemService(PermissionManager.class); 1498 1499 int numSplitPerms = permissionManager.getSplitPermissions().size(); 1500 for (int splitPermNum = 0; splitPermNum < numSplitPerms; splitPermNum++) { 1501 PermissionManager.SplitPermissionInfo spi = 1502 permissionManager.getSplitPermissions().get(splitPermNum); 1503 String splitPerm = spi.getSplitPermission(); 1504 1505 PermissionInfo pi; 1506 try { 1507 pi = mPackageManager.getPermissionInfo(splitPerm, 0); 1508 } catch (NameNotFoundException e) { 1509 Log.w(LOG_TAG, "No such permission: " + splitPerm, e); 1510 continue; 1511 } 1512 1513 // Skip if split permission is not "install" permission. 1514 if (pi.getProtection() != pi.PROTECTION_NORMAL) { 1515 continue; 1516 } 1517 1518 List<String> newPerms = spi.getNewPermissions(); 1519 int numNewPerms = newPerms.size(); 1520 for (int newPermNum = 0; newPermNum < numNewPerms; newPermNum++) { 1521 String newPerm = newPerms.get(newPermNum); 1522 1523 if (!hasPermission(newPerm)) { 1524 continue; 1525 } 1526 1527 try { 1528 pi = mPackageManager.getPermissionInfo(newPerm, 0); 1529 } catch (NameNotFoundException e) { 1530 Log.w(LOG_TAG, "No such permission: " + newPerm, e); 1531 continue; 1532 } 1533 1534 // Skip if new permission is not "runtime" permission. 1535 if (pi.getProtection() != pi.PROTECTION_DANGEROUS) { 1536 continue; 1537 } 1538 1539 if (mPackageInfo.applicationInfo.targetSdkVersion < spi.getTargetSdk()) { 1540 return true; 1541 } 1542 } 1543 } 1544 return false; 1545 } 1546 } 1547