1 /* 2 * Copyright (C) 2018 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.role.model; 18 19 import android.app.ActivityManager; 20 import android.app.role.RoleManager; 21 import android.content.ComponentName; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.pm.ApplicationInfo; 25 import android.content.pm.PackageManager; 26 import android.content.res.Resources; 27 import android.os.Process; 28 import android.os.UserHandle; 29 import android.text.TextUtils; 30 import android.util.ArrayMap; 31 import android.util.Log; 32 33 import androidx.annotation.NonNull; 34 import androidx.annotation.Nullable; 35 import androidx.annotation.StringRes; 36 import androidx.preference.Preference; 37 38 import com.android.permissioncontroller.Constants; 39 import com.android.permissioncontroller.permission.utils.CollectionUtils; 40 import com.android.permissioncontroller.permission.utils.Utils; 41 import com.android.permissioncontroller.role.ui.TwoTargetPreference; 42 import com.android.permissioncontroller.role.utils.PackageUtils; 43 import com.android.permissioncontroller.role.utils.UserUtils; 44 45 import java.util.ArrayList; 46 import java.util.Collections; 47 import java.util.List; 48 import java.util.Objects; 49 50 /** 51 * Specifies a role and its properties. 52 * <p> 53 * A role is a unique name within the system associated with certain privileges. There can be 54 * multiple applications qualifying for a role, but only a subset of them can become role holders. 55 * To qualify for a role, an application must meet certain requirements, including defining certain 56 * components in its manifest. Then the application will need user consent to become the role 57 * holder. 58 * <p> 59 * Upon becoming a role holder, the application may be granted certain permissions, have certain 60 * app ops set to certain modes and certain {@code Activity} components configured as preferred for 61 * certain {@code Intent} actions. When an application loses its role, these privileges will also be 62 * revoked. 63 * 64 * @see android.app.role.RoleManager 65 */ 66 public class Role { 67 68 private static final String LOG_TAG = Role.class.getSimpleName(); 69 70 private static final boolean DEBUG = false; 71 72 private static final String PACKAGE_NAME_ANDROID_SYSTEM = "android"; 73 74 private static final String PACKAGE_NAME_SEPARATOR = ";"; 75 76 /** 77 * The name of this role. Must be unique. 78 */ 79 @NonNull 80 private final String mName; 81 82 /** 83 * The behavior of this role. 84 */ 85 @Nullable 86 private final RoleBehavior mBehavior; 87 88 @Nullable 89 private final String mDefaultHoldersResourceName; 90 91 /** 92 * The string resource for the description of this role. 93 */ 94 @StringRes 95 private final int mDescriptionResource; 96 97 /** 98 * Whether this role is exclusive, i.e. allows at most one holder. 99 */ 100 private final boolean mExclusive; 101 102 /** 103 * Whether this role should fall back to the default holder. 104 */ 105 private final boolean mFallBackToDefaultHolder; 106 107 /** 108 * The string resource for the label of this role. 109 */ 110 @StringRes 111 private final int mLabelResource; 112 113 /** 114 * The string resource for the request description of this role, shown below the selected app in 115 * the request role dialog. 116 */ 117 @StringRes 118 private final int mRequestDescriptionResource; 119 120 /** 121 * The string resource for the request title of this role, shown as the title of the request 122 * role dialog. 123 */ 124 @StringRes 125 private final int mRequestTitleResource; 126 127 /** 128 * Whether this role is requestable by applications with 129 * {@link android.app.role.RoleManager#createRequestRoleIntent(String)}. 130 */ 131 private final boolean mRequestable; 132 133 /** 134 * The string resource for search keywords of this role, in addition to the label of this role, 135 * if it's non-zero. 136 */ 137 @StringRes 138 private final int mSearchKeywordsResource; 139 140 /** 141 * The string resource for the short label of this role, currently used when in a list of roles. 142 */ 143 @StringRes 144 private final int mShortLabelResource; 145 146 /** 147 * Whether the UI for this role will show the "None" item. Only valid if this role is 148 * {@link #mExclusive exclusive}, and {@link #getFallbackHolder(Context)} should also return 149 * empty to allow actually selecting "None". 150 */ 151 private final boolean mShowNone; 152 153 /** 154 * Whether this role only accepts system apps as its holders. 155 */ 156 private final boolean mSystemOnly; 157 158 /** 159 * Whether this role is visible to user. 160 */ 161 private final boolean mVisible; 162 163 /** 164 * The required components for an application to qualify for this role. 165 */ 166 @NonNull 167 private final List<RequiredComponent> mRequiredComponents; 168 169 /** 170 * The permissions to be granted by this role. 171 */ 172 @NonNull 173 private final List<String> mPermissions; 174 175 /** 176 * The app op permissions to be granted by this role. 177 */ 178 @NonNull 179 private final List<String> mAppOpPermissions; 180 181 /** 182 * The app ops to be set to allowed by this role. 183 */ 184 @NonNull 185 private final List<AppOp> mAppOps; 186 187 /** 188 * The set of preferred {@code Activity} configurations to be configured by this role. 189 */ 190 @NonNull 191 private final List<PreferredActivity> mPreferredActivities; 192 Role(@onNull String name, @Nullable RoleBehavior behavior, @Nullable String defaultHoldersResourceName, @StringRes int descriptionResource, boolean exclusive, boolean fallBackToDefaultHolder, @StringRes int labelResource, @StringRes int requestDescriptionResource, @StringRes int requestTitleResource, boolean requestable, @StringRes int searchKeywordsResource, @StringRes int shortLabelResource, boolean showNone, boolean systemOnly, boolean visible, @NonNull List<RequiredComponent> requiredComponents, @NonNull List<String> permissions, @NonNull List<String> appOpPermissions, @NonNull List<AppOp> appOps, @NonNull List<PreferredActivity> preferredActivities)193 public Role(@NonNull String name, @Nullable RoleBehavior behavior, 194 @Nullable String defaultHoldersResourceName, @StringRes int descriptionResource, 195 boolean exclusive, boolean fallBackToDefaultHolder, @StringRes int labelResource, 196 @StringRes int requestDescriptionResource, @StringRes int requestTitleResource, 197 boolean requestable, @StringRes int searchKeywordsResource, 198 @StringRes int shortLabelResource, boolean showNone, boolean systemOnly, 199 boolean visible, @NonNull List<RequiredComponent> requiredComponents, 200 @NonNull List<String> permissions, @NonNull List<String> appOpPermissions, 201 @NonNull List<AppOp> appOps, @NonNull List<PreferredActivity> preferredActivities) { 202 mName = name; 203 mBehavior = behavior; 204 mDefaultHoldersResourceName = defaultHoldersResourceName; 205 mDescriptionResource = descriptionResource; 206 mExclusive = exclusive; 207 mFallBackToDefaultHolder = fallBackToDefaultHolder; 208 mLabelResource = labelResource; 209 mRequestDescriptionResource = requestDescriptionResource; 210 mRequestTitleResource = requestTitleResource; 211 mRequestable = requestable; 212 mSearchKeywordsResource = searchKeywordsResource; 213 mShortLabelResource = shortLabelResource; 214 mShowNone = showNone; 215 mSystemOnly = systemOnly; 216 mVisible = visible; 217 mRequiredComponents = requiredComponents; 218 mPermissions = permissions; 219 mAppOpPermissions = appOpPermissions; 220 mAppOps = appOps; 221 mPreferredActivities = preferredActivities; 222 } 223 224 @NonNull getName()225 public String getName() { 226 return mName; 227 } 228 229 @Nullable getBehavior()230 public RoleBehavior getBehavior() { 231 return mBehavior; 232 } 233 234 @StringRes getDescriptionResource()235 public int getDescriptionResource() { 236 return mDescriptionResource; 237 } 238 isExclusive()239 public boolean isExclusive() { 240 return mExclusive; 241 } 242 243 @StringRes getLabelResource()244 public int getLabelResource() { 245 return mLabelResource; 246 } 247 248 @StringRes getRequestDescriptionResource()249 public int getRequestDescriptionResource() { 250 return mRequestDescriptionResource; 251 } 252 253 @StringRes getRequestTitleResource()254 public int getRequestTitleResource() { 255 return mRequestTitleResource; 256 } 257 isRequestable()258 public boolean isRequestable() { 259 return mRequestable; 260 } 261 262 @StringRes getSearchKeywordsResource()263 public int getSearchKeywordsResource() { 264 return mSearchKeywordsResource; 265 } 266 267 @StringRes getShortLabelResource()268 public int getShortLabelResource() { 269 return mShortLabelResource; 270 } 271 272 /** 273 * @see #mShowNone 274 */ shouldShowNone()275 public boolean shouldShowNone() { 276 return mShowNone; 277 } 278 isVisible()279 public boolean isVisible() { 280 return mVisible; 281 } 282 283 @NonNull getRequiredComponents()284 public List<RequiredComponent> getRequiredComponents() { 285 return mRequiredComponents; 286 } 287 288 @NonNull getPermissions()289 public List<String> getPermissions() { 290 return mPermissions; 291 } 292 293 @NonNull getAppOpPermissions()294 public List<String> getAppOpPermissions() { 295 return mAppOpPermissions; 296 } 297 298 @NonNull getAppOps()299 public List<AppOp> getAppOps() { 300 return mAppOps; 301 } 302 303 @NonNull getPreferredActivities()304 public List<PreferredActivity> getPreferredActivities() { 305 return mPreferredActivities; 306 } 307 308 /** 309 * Callback when this role is added to the system for the first time. 310 * 311 * @param context the {@code Context} to retrieve system services 312 */ onRoleAdded(@onNull Context context)313 public void onRoleAdded(@NonNull Context context) { 314 if (mBehavior != null) { 315 mBehavior.onRoleAdded(this, context); 316 } 317 } 318 319 /** 320 * Check whether this role is available. 321 * 322 * @param user the user to check for 323 * @param context the {@code Context} to retrieve system services 324 * 325 * @return whether this role is available. 326 */ isAvailableAsUser(@onNull UserHandle user, @NonNull Context context)327 public boolean isAvailableAsUser(@NonNull UserHandle user, @NonNull Context context) { 328 if (mBehavior != null) { 329 return mBehavior.isAvailableAsUser(this, user, context); 330 } 331 return true; 332 } 333 334 /** 335 * Check whether this role is available, for current user. 336 * 337 * @param context the {@code Context} to retrieve system services 338 * 339 * @return whether this role is available. 340 */ isAvailable(@onNull Context context)341 public boolean isAvailable(@NonNull Context context) { 342 return isAvailableAsUser(Process.myUserHandle(), context); 343 } 344 345 /** 346 * Get the default holders of this role, which will be added when the role is added for the 347 * first time. 348 * 349 * @param context the {@code Context} to retrieve system services 350 * 351 * @return the list of package names of the default holders 352 */ 353 @NonNull getDefaultHolders(@onNull Context context)354 public List<String> getDefaultHolders(@NonNull Context context) { 355 if (mDefaultHoldersResourceName == null) { 356 if (mBehavior != null) { 357 return mBehavior.getDefaultHolders(this, context); 358 } 359 return Collections.emptyList(); 360 } 361 362 Resources resources = context.getResources(); 363 int resourceId = resources.getIdentifier(mDefaultHoldersResourceName, "string", "android"); 364 if (resourceId == 0) { 365 Log.w(LOG_TAG, "Cannot find resource for default holder: " 366 + mDefaultHoldersResourceName); 367 return Collections.emptyList(); 368 } 369 370 String resourceValue; 371 try { 372 resourceValue = resources.getString(resourceId); 373 } catch (Resources.NotFoundException e) { 374 Log.w(LOG_TAG, "Cannot get resource for default holder: " + mDefaultHoldersResourceName, 375 e); 376 return Collections.emptyList(); 377 } 378 if (TextUtils.isEmpty(resourceValue)) { 379 return Collections.emptyList(); 380 } 381 382 if (isExclusive()) { 383 if (!isDefaultHolderQualified(resourceValue, context)) { 384 return Collections.emptyList(); 385 } 386 return Collections.singletonList(resourceValue); 387 } else { 388 String[] resourcePackageNames = resourceValue.split(PACKAGE_NAME_SEPARATOR); 389 List<String> packageNames = new ArrayList<>(); 390 for (String packageName : resourcePackageNames) { 391 if (isDefaultHolderQualified(packageName, context)) { 392 packageNames.add(packageName); 393 } 394 } 395 return packageNames; 396 } 397 } 398 isDefaultHolderQualified(@onNull String packageName, @NonNull Context context)399 private boolean isDefaultHolderQualified(@NonNull String packageName, 400 @NonNull Context context) { 401 ApplicationInfo applicationInfo = PackageUtils.getApplicationInfo(packageName, context); 402 if (applicationInfo == null) { 403 Log.w(LOG_TAG, "Cannot get ApplicationInfo for default holder: " + packageName); 404 return false; 405 } 406 407 if ((applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { 408 Log.w(LOG_TAG, "Default holder is not a system app: " + packageName); 409 return false; 410 } 411 412 return true; 413 } 414 415 /** 416 * Get the fallback holder of this role, which will be added whenever there are no role holders. 417 * <p> 418 * Should return {@code null} if this role {@link #mShowNone shows a "None" item}. 419 * 420 * @param context the {@code Context} to retrieve system services 421 * 422 * @return the package name of the fallback holder, or {@code null} if none 423 */ 424 @Nullable getFallbackHolder(@onNull Context context)425 public String getFallbackHolder(@NonNull Context context) { 426 if (isNoneHolderSelected(context)) { 427 return null; 428 } 429 if (mFallBackToDefaultHolder) { 430 return CollectionUtils.firstOrNull(getDefaultHolders(context)); 431 } 432 if (mBehavior != null) { 433 return mBehavior.getFallbackHolder(this, context); 434 } 435 return null; 436 } 437 438 /** 439 * Check whether this role should be visible to user. 440 * 441 * @param user the user to check for 442 * @param context the {@code Context} to retrieve system services 443 * 444 * @return whether this role should be visible to user 445 */ isVisibleAsUser(@onNull UserHandle user, @NonNull Context context)446 public boolean isVisibleAsUser(@NonNull UserHandle user, @NonNull Context context) { 447 return mVisible && (mBehavior == null || mBehavior.isVisibleAsUser(this, user, context)); 448 } 449 450 /** 451 * Check whether this role should be visible to user, for current user. 452 * 453 * @param context the {@code Context} to retrieve system services 454 * 455 * @return whether this role should be visible to user. 456 */ isVisible(@onNull Context context)457 public boolean isVisible(@NonNull Context context) { 458 return isVisibleAsUser(Process.myUserHandle(), context); 459 } 460 461 /** 462 * Get the {@link Intent} to manage this role, or {@code null} to use the default UI. 463 * 464 * @param user the user to manage this role for 465 * @param context the {@code Context} to retrieve system services 466 * 467 * @return the {@link Intent} to manage this role, or {@code null} to use the default UI. 468 */ 469 @Nullable getManageIntentAsUser(@onNull UserHandle user, @NonNull Context context)470 public Intent getManageIntentAsUser(@NonNull UserHandle user, @NonNull Context context) { 471 if (mBehavior != null) { 472 return mBehavior.getManageIntentAsUser(this, user, context); 473 } 474 return null; 475 } 476 477 /** 478 * Prepare a {@link Preference} for this role. 479 * 480 * @param preference the {@link Preference} for this role 481 * @param user the user for this role 482 * @param context the {@code Context} to retrieve system services 483 */ preparePreferenceAsUser(@onNull TwoTargetPreference preference, @NonNull UserHandle user, @NonNull Context context)484 public void preparePreferenceAsUser(@NonNull TwoTargetPreference preference, 485 @NonNull UserHandle user, @NonNull Context context) { 486 if (mBehavior != null) { 487 mBehavior.preparePreferenceAsUser(this, preference, user, context); 488 } 489 } 490 491 /** 492 * Check whether a qualifying application should be visible to user. 493 * 494 * @param applicationInfo the {@link ApplicationInfo} for the application 495 * @param user the user for the application 496 * @param context the {@code Context} to retrieve system services 497 * 498 * @return whether the qualifying application should be visible to user 499 */ isApplicationVisibleAsUser(@onNull ApplicationInfo applicationInfo, @NonNull UserHandle user, @NonNull Context context)500 public boolean isApplicationVisibleAsUser(@NonNull ApplicationInfo applicationInfo, 501 @NonNull UserHandle user, @NonNull Context context) { 502 if (mBehavior != null) { 503 return mBehavior.isApplicationVisibleAsUser(this, applicationInfo, user, context); 504 } 505 return true; 506 } 507 508 /** 509 * Prepare a {@link Preference} for an application. 510 * 511 * @param preference the {@link Preference} for the application 512 * @param applicationInfo the {@link ApplicationInfo} for the application 513 * @param user the user for the application 514 * @param context the {@code Context} to retrieve system services 515 */ prepareApplicationPreferenceAsUser(@onNull Preference preference, @NonNull ApplicationInfo applicationInfo, @NonNull UserHandle user, @NonNull Context context)516 public void prepareApplicationPreferenceAsUser(@NonNull Preference preference, 517 @NonNull ApplicationInfo applicationInfo, @NonNull UserHandle user, 518 @NonNull Context context) { 519 if (mBehavior != null) { 520 mBehavior.prepareApplicationPreferenceAsUser(this, preference, applicationInfo, user, 521 context); 522 } 523 } 524 525 /** 526 * Get the confirmation message for adding an application as a holder of this role. 527 * 528 * @param packageName the package name of the application to get confirmation message for 529 * @param context the {@code Context} to retrieve system services 530 * 531 * @return the confirmation message, or {@code null} if no confirmation is needed 532 */ 533 @Nullable getConfirmationMessage(@onNull String packageName, @NonNull Context context)534 public CharSequence getConfirmationMessage(@NonNull String packageName, 535 @NonNull Context context) { 536 if (mBehavior != null) { 537 return mBehavior.getConfirmationMessage(this, packageName, context); 538 } 539 return null; 540 } 541 542 /** 543 * Check whether a package is qualified for this role, i.e. whether it contains all the required 544 * components (plus meeting some other general restrictions). 545 * 546 * @param packageName the package name to check for 547 * @param context the {@code Context} to retrieve system services 548 * 549 * @return whether the package is qualified for a role 550 */ isPackageQualified(@onNull String packageName, @NonNull Context context)551 public boolean isPackageQualified(@NonNull String packageName, @NonNull Context context) { 552 if (!isPackageMinimallyQualifiedAsUser(packageName, Process.myUserHandle(), context)) { 553 return false; 554 } 555 556 if (mBehavior != null) { 557 Boolean isPackageQualified = mBehavior.isPackageQualified(this, packageName, context); 558 if (isPackageQualified != null) { 559 return isPackageQualified; 560 } 561 } 562 563 int requiredComponentsSize = mRequiredComponents.size(); 564 for (int i = 0; i < requiredComponentsSize; i++) { 565 RequiredComponent requiredComponent = mRequiredComponents.get(i); 566 if (requiredComponent.getQualifyingComponentForPackage(packageName, context) == null) { 567 Log.w(LOG_TAG, packageName + " not qualified for " + mName 568 + " due to missing " + requiredComponent); 569 return false; 570 } 571 } 572 573 return true; 574 } 575 576 /** 577 * Get the list of packages that are qualified for this role, i.e. packages containing all the 578 * required components (plus meeting some other general restrictions). 579 * 580 * @param user the user to get the qualifying packages. 581 * @param context the {@code Context} to retrieve system services 582 * 583 * @return the list of packages that are qualified for this role 584 */ 585 @NonNull getQualifyingPackagesAsUser(@onNull UserHandle user, @NonNull Context context)586 public List<String> getQualifyingPackagesAsUser(@NonNull UserHandle user, 587 @NonNull Context context) { 588 List<String> qualifyingPackages = null; 589 590 if (mBehavior != null) { 591 qualifyingPackages = mBehavior.getQualifyingPackagesAsUser(this, user, context); 592 } 593 594 if (qualifyingPackages == null) { 595 ArrayMap<String, Integer> packageComponentCountMap = new ArrayMap<>(); 596 int requiredComponentsSize = mRequiredComponents.size(); 597 for (int requiredComponentsIndex = 0; requiredComponentsIndex < requiredComponentsSize; 598 requiredComponentsIndex++) { 599 RequiredComponent requiredComponent = mRequiredComponents.get( 600 requiredComponentsIndex); 601 602 // This returns at most one component per package. 603 List<ComponentName> qualifyingComponents = 604 requiredComponent.getQualifyingComponentsAsUser(user, context); 605 int qualifyingComponentsSize = qualifyingComponents.size(); 606 for (int qualifyingComponentsIndex = 0; 607 qualifyingComponentsIndex < qualifyingComponentsSize; 608 ++qualifyingComponentsIndex) { 609 ComponentName componentName = qualifyingComponents.get( 610 qualifyingComponentsIndex); 611 612 String packageName = componentName.getPackageName(); 613 Integer componentCount = packageComponentCountMap.get(packageName); 614 packageComponentCountMap.put(packageName, componentCount == null ? 1 615 : componentCount + 1); 616 } 617 } 618 619 qualifyingPackages = new ArrayList<>(); 620 int packageComponentCountMapSize = packageComponentCountMap.size(); 621 for (int i = 0; i < packageComponentCountMapSize; i++) { 622 int componentCount = packageComponentCountMap.valueAt(i); 623 624 if (componentCount != requiredComponentsSize) { 625 continue; 626 } 627 String packageName = packageComponentCountMap.keyAt(i); 628 qualifyingPackages.add(packageName); 629 } 630 } 631 632 int qualifyingPackagesSize = qualifyingPackages.size(); 633 for (int i = 0; i < qualifyingPackagesSize; ) { 634 String packageName = qualifyingPackages.get(i); 635 636 if (!isPackageMinimallyQualifiedAsUser(packageName, user, context)) { 637 qualifyingPackages.remove(i); 638 qualifyingPackagesSize--; 639 } else { 640 i++; 641 } 642 } 643 644 return qualifyingPackages; 645 } 646 isPackageMinimallyQualifiedAsUser( @onNull String packageName, @NonNull UserHandle user, @NonNull Context context)647 private boolean isPackageMinimallyQualifiedAsUser( 648 @NonNull String packageName, @NonNull UserHandle user, @NonNull Context context) { 649 if (Objects.equals(packageName, PACKAGE_NAME_ANDROID_SYSTEM)) { 650 return false; 651 } 652 653 ApplicationInfo applicationInfo = PackageUtils.getApplicationInfoAsUser(packageName, user, 654 context); 655 if (applicationInfo == null) { 656 Log.w(LOG_TAG, "Cannot get ApplicationInfo for package: " + packageName + ", user: " 657 + user.getIdentifier()); 658 return false; 659 } 660 661 if (mSystemOnly && (applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { 662 return false; 663 } 664 665 if (!applicationInfo.enabled) { 666 return false; 667 } 668 669 if (applicationInfo.isInstantApp()) { 670 return false; 671 } 672 673 PackageManager userPackageManager = UserUtils.getUserContext(context, user) 674 .getPackageManager(); 675 if (!userPackageManager.getDeclaredSharedLibraries(packageName, 0).isEmpty()) { 676 return false; 677 } 678 679 return true; 680 } 681 682 /** 683 * Grant this role to an application. 684 * 685 * @param packageName the package name of the application to be granted this role to 686 * @param dontKillApp whether this application should not be killed despite changes 687 * @param overrideUserSetAndFixedPermissions whether to override user set and fixed flags on 688 * permissions 689 * @param context the {@code Context} to retrieve system services 690 */ grant(@onNull String packageName, boolean dontKillApp, boolean overrideUserSetAndFixedPermissions, @NonNull Context context)691 public void grant(@NonNull String packageName, boolean dontKillApp, 692 boolean overrideUserSetAndFixedPermissions, @NonNull Context context) { 693 boolean permissionOrAppOpChanged = Permissions.grant(packageName, mPermissions, true, 694 overrideUserSetAndFixedPermissions, true, false, false, context); 695 696 int appOpPermissionsSize = mAppOpPermissions.size(); 697 for (int i = 0; i < appOpPermissionsSize; i++) { 698 String appOpPermissions = mAppOpPermissions.get(i); 699 AppOpPermissions.grant(packageName, appOpPermissions, context); 700 } 701 702 int appOpsSize = mAppOps.size(); 703 for (int i = 0; i < appOpsSize; i++) { 704 AppOp appOp = mAppOps.get(i); 705 appOp.grant(packageName, context); 706 } 707 708 int preferredActivitiesSize = mPreferredActivities.size(); 709 for (int i = 0; i < preferredActivitiesSize; i++) { 710 PreferredActivity preferredActivity = mPreferredActivities.get(i); 711 preferredActivity.configure(packageName, context); 712 } 713 714 if (mBehavior != null) { 715 mBehavior.grant(this, packageName, context); 716 } 717 718 if (!dontKillApp && permissionOrAppOpChanged && !Permissions.isRuntimePermissionsSupported( 719 packageName, context)) { 720 killApp(packageName, context); 721 } 722 } 723 724 /** 725 * Revoke this role from an application. 726 * 727 * @param packageName the package name of the application to be granted this role to 728 * @param dontKillApp whether this application should not be killed despite changes 729 * @param overrideSystemFixedPermissions whether system-fixed permissions can be revoked 730 * @param context the {@code Context} to retrieve system services 731 */ revoke(@onNull String packageName, boolean dontKillApp, boolean overrideSystemFixedPermissions, @NonNull Context context)732 public void revoke(@NonNull String packageName, boolean dontKillApp, 733 boolean overrideSystemFixedPermissions, @NonNull Context context) { 734 RoleManager roleManager = context.getSystemService(RoleManager.class); 735 List<String> otherRoleNames = roleManager.getHeldRolesFromController(packageName); 736 otherRoleNames.remove(mName); 737 738 List<String> permissionsToRevoke = new ArrayList<>(mPermissions); 739 ArrayMap<String, Role> roles = Roles.get(context); 740 int otherRoleNamesSize = otherRoleNames.size(); 741 for (int i = 0; i < otherRoleNamesSize; i++) { 742 String roleName = otherRoleNames.get(i); 743 Role role = roles.get(roleName); 744 permissionsToRevoke.removeAll(role.mPermissions); 745 } 746 boolean permissionOrAppOpChanged = Permissions.revoke(packageName, permissionsToRevoke, 747 true, false, overrideSystemFixedPermissions, context); 748 749 List<String> appOpPermissionsToRevoke = new ArrayList<>(mAppOpPermissions); 750 for (int i = 0; i < otherRoleNamesSize; i++) { 751 String roleName = otherRoleNames.get(i); 752 Role role = roles.get(roleName); 753 appOpPermissionsToRevoke.removeAll(role.mAppOpPermissions); 754 } 755 int appOpPermissionsSize = appOpPermissionsToRevoke.size(); 756 for (int i = 0; i < appOpPermissionsSize; i++) { 757 String appOpPermission = appOpPermissionsToRevoke.get(i); 758 AppOpPermissions.revoke(packageName, appOpPermission, context); 759 } 760 761 List<AppOp> appOpsToRevoke = new ArrayList<>(mAppOps); 762 for (int i = 0; i < otherRoleNamesSize; i++) { 763 String roleName = otherRoleNames.get(i); 764 Role role = roles.get(roleName); 765 appOpsToRevoke.removeAll(role.mAppOps); 766 } 767 int appOpsSize = appOpsToRevoke.size(); 768 for (int i = 0; i < appOpsSize; i++) { 769 AppOp appOp = appOpsToRevoke.get(i); 770 appOp.revoke(packageName, context); 771 } 772 773 // TODO: Revoke preferred activities? But this is unnecessary for most roles using it as 774 // they have fallback holders. Moreover, clearing the preferred activity might result in 775 // other system components listening to preferred activity change get notified for the 776 // wrong thing when we are removing a exclusive role holder for adding another. 777 778 if (mBehavior != null) { 779 mBehavior.revoke(this, packageName, context); 780 } 781 782 if (!dontKillApp && permissionOrAppOpChanged) { 783 killApp(packageName, context); 784 } 785 } 786 killApp(@onNull String packageName, @NonNull Context context)787 private void killApp(@NonNull String packageName, @NonNull Context context) { 788 if (DEBUG) { 789 Log.i(LOG_TAG, "Killing " + packageName + " due to " 790 + Thread.currentThread().getStackTrace()[3].getMethodName() 791 + "(" + mName + ")"); 792 } 793 ApplicationInfo applicationInfo = PackageUtils.getApplicationInfo(packageName, context); 794 if (applicationInfo == null) { 795 Log.w(LOG_TAG, "Cannot get ApplicationInfo for package: " + packageName); 796 return; 797 } 798 ActivityManager activityManager = context.getSystemService(ActivityManager.class); 799 activityManager.killUid(applicationInfo.uid, "Permission or app op changed"); 800 } 801 802 /** 803 * Check whether the "none" role holder is selected. 804 * 805 * @param context the {@code Context} to retrieve system services 806 * 807 * @return whether the "none" role holder is selected 808 */ isNoneHolderSelected(@onNull Context context)809 private boolean isNoneHolderSelected(@NonNull Context context) { 810 return Utils.getDeviceProtectedSharedPreferences(context).getBoolean( 811 Constants.IS_NONE_ROLE_HOLDER_SELECTED_KEY + mName, false); 812 } 813 814 /** 815 * Callback when a role holder (other than "none") was added. 816 * 817 * @param packageName the package name of the role holder 818 * @param user the user for the role 819 * @param context the {@code Context} to retrieve system services 820 */ onHolderAddedAsUser(@onNull String packageName, @NonNull UserHandle user, @NonNull Context context)821 public void onHolderAddedAsUser(@NonNull String packageName, @NonNull UserHandle user, 822 @NonNull Context context) { 823 Utils.getDeviceProtectedSharedPreferences(UserUtils.getUserContext(context, user)).edit() 824 .remove(Constants.IS_NONE_ROLE_HOLDER_SELECTED_KEY + mName) 825 .apply(); 826 } 827 828 /** 829 * Callback when a role holder (other than "none") was selected in the UI and added 830 * successfully. 831 * 832 * @param packageName the package name of the role holder 833 * @param user the user for the role 834 * @param context the {@code Context} to retrieve system services 835 */ onHolderSelectedAsUser(@onNull String packageName, @NonNull UserHandle user, @NonNull Context context)836 public void onHolderSelectedAsUser(@NonNull String packageName, @NonNull UserHandle user, 837 @NonNull Context context) { 838 if (mBehavior != null) { 839 mBehavior.onHolderSelectedAsUser(this, packageName, user, context); 840 } 841 } 842 843 /** 844 * Callback when a role holder changed. 845 * 846 * @param user the user for the role 847 * @param context the {@code Context} to retrieve system services 848 */ onHolderChangedAsUser(@onNull UserHandle user, @NonNull Context context)849 public void onHolderChangedAsUser(@NonNull UserHandle user, 850 @NonNull Context context) { 851 if (mBehavior != null) { 852 mBehavior.onHolderChangedAsUser(this, user, context); 853 } 854 } 855 856 /** 857 * Callback when the "none" role holder was selected in the UI. 858 * 859 * @param user the user for the role 860 * @param context the {@code Context} to retrieve system services 861 */ onNoneHolderSelectedAsUser(@onNull UserHandle user, @NonNull Context context)862 public void onNoneHolderSelectedAsUser(@NonNull UserHandle user, @NonNull Context context) { 863 Utils.getDeviceProtectedSharedPreferences(UserUtils.getUserContext(context, user)).edit() 864 .putBoolean(Constants.IS_NONE_ROLE_HOLDER_SELECTED_KEY + mName, true) 865 .apply(); 866 } 867 868 @Override toString()869 public String toString() { 870 return "Role{" 871 + "mName='" + mName + '\'' 872 + ", mBehavior=" + mBehavior 873 + ", mDefaultHoldersResourceName=" + mDefaultHoldersResourceName 874 + ", mDescriptionResource=" + mDescriptionResource 875 + ", mExclusive=" + mExclusive 876 + ", mFallBackToDefaultHolder=" + mFallBackToDefaultHolder 877 + ", mLabelResource=" + mLabelResource 878 + ", mRequestDescriptionResource=" + mRequestDescriptionResource 879 + ", mRequestTitleResource=" + mRequestTitleResource 880 + ", mRequestable=" + mRequestable 881 + ", mSearchKeywordsResource=" + mSearchKeywordsResource 882 + ", mShortLabelResource=" + mShortLabelResource 883 + ", mShowNone=" + mShowNone 884 + ", mSystemOnly=" + mSystemOnly 885 + ", mVisible=" + mVisible 886 + ", mRequiredComponents=" + mRequiredComponents 887 + ", mPermissions=" + mPermissions 888 + ", mAppOpPermissions=" + mAppOpPermissions 889 + ", mAppOps=" + mAppOps 890 + ", mPreferredActivities=" + mPreferredActivities 891 + '}'; 892 } 893 } 894