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