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