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