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