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 * Check whether this role is available. 258 * 259 * @param user the user to check for 260 * @param context the {@code Context} to retrieve system services 261 * 262 * @return whether this role is available. 263 */ isAvailableAsUser(@onNull UserHandle user, @NonNull Context context)264 public boolean isAvailableAsUser(@NonNull UserHandle user, @NonNull Context context) { 265 if (mBehavior != null) { 266 return mBehavior.isAvailableAsUser(this, user, context); 267 } 268 return true; 269 } 270 271 /** 272 * Check whether this role is available, for current user. 273 * 274 * @param context the {@code Context} to retrieve system services 275 * 276 * @return whether this role is available. 277 */ isAvailable(@onNull Context context)278 public boolean isAvailable(@NonNull Context context) { 279 return isAvailableAsUser(Process.myUserHandle(), context); 280 } 281 282 /** 283 * Get the default holders of this role, which will be added when the role is added for the 284 * first time. 285 * 286 * @param context the {@code Context} to retrieve system services 287 * 288 * @return the list of package names of the default holders 289 */ 290 @NonNull getDefaultHolders(@onNull Context context)291 public List<String> getDefaultHolders(@NonNull Context context) { 292 if (mBehavior != null) { 293 return mBehavior.getDefaultHolders(this, context); 294 } 295 return Collections.emptyList(); 296 } 297 298 /** 299 * Get the fallback holder of this role, which will be added whenever there are no role holders. 300 * <p> 301 * Should return {@code null} if this role {@link #mShowNone shows a "None" item}. 302 * 303 * @param context the {@code Context} to retrieve system services 304 * 305 * @return the package name of the fallback holder, or {@code null} if none 306 */ 307 @Nullable getFallbackHolder(@onNull Context context)308 public String getFallbackHolder(@NonNull Context context) { 309 if (mBehavior != null && !isNoneHolderSelected(context)) { 310 return mBehavior.getFallbackHolder(this, context); 311 } 312 return null; 313 } 314 315 /** 316 * Check whether this role should be visible to user. 317 * 318 * @param user the user to check for 319 * @param context the {@code Context} to retrieve system services 320 * 321 * @return whether this role should be visible to user 322 */ isVisibleAsUser(@onNull UserHandle user, @NonNull Context context)323 public boolean isVisibleAsUser(@NonNull UserHandle user, @NonNull Context context) { 324 if (mBehavior != null) { 325 return mBehavior.isVisibleAsUser(this, user, context); 326 } 327 return true; 328 } 329 330 /** 331 * Check whether this role should be visible to user, for current user. 332 * 333 * @param context the {@code Context} to retrieve system services 334 * 335 * @return whether this role should be visible to user. 336 */ isVisible(@onNull Context context)337 public boolean isVisible(@NonNull Context context) { 338 return isVisibleAsUser(Process.myUserHandle(), context); 339 } 340 341 /** 342 * Get the {@link Intent} to manage this role, or {@code null} to use the default UI. 343 * 344 * @param user the user to manage this role for 345 * @param context the {@code Context} to retrieve system services 346 * 347 * @return the {@link Intent} to manage this role, or {@code null} to use the default UI. 348 */ 349 @Nullable getManageIntentAsUser(@onNull UserHandle user, @NonNull Context context)350 public Intent getManageIntentAsUser(@NonNull UserHandle user, @NonNull Context context) { 351 if (mBehavior != null) { 352 return mBehavior.getManageIntentAsUser(this, user, context); 353 } 354 return null; 355 } 356 357 /** 358 * Prepare a {@link Preference} for this role. 359 * 360 * @param preference the {@link Preference} for this role 361 * @param user the user for this role 362 * @param context the {@code Context} to retrieve system services 363 */ preparePreferenceAsUser(@onNull TwoTargetPreference preference, @NonNull UserHandle user, @NonNull Context context)364 public void preparePreferenceAsUser(@NonNull TwoTargetPreference preference, 365 @NonNull UserHandle user, @NonNull Context context) { 366 if (mBehavior != null) { 367 mBehavior.preparePreferenceAsUser(this, preference, user, context); 368 } 369 } 370 371 /** 372 * Prepare a {@link Preference} for an application. 373 * 374 * @param preference the {@link Preference} for the application 375 * @param applicationInfo the {@link ApplicationInfo} for the application 376 * @param user the user for the application 377 * @param context the {@code Context} to retrieve system services 378 */ prepareApplicationPreferenceAsUser(@onNull Preference preference, @NonNull ApplicationInfo applicationInfo, @NonNull UserHandle user, @NonNull Context context)379 public void prepareApplicationPreferenceAsUser(@NonNull Preference preference, 380 @NonNull ApplicationInfo applicationInfo, @NonNull UserHandle user, 381 @NonNull Context context) { 382 if (mBehavior != null) { 383 mBehavior.prepareApplicationPreferenceAsUser(this, preference, applicationInfo, user, 384 context); 385 } 386 } 387 388 /** 389 * Get the confirmation message for adding an application as a holder of this role. 390 * 391 * @param packageName the package name of the application to get confirmation message for 392 * @param context the {@code Context} to retrieve system services 393 * 394 * @return the confirmation message, or {@code null} if no confirmation is needed 395 */ 396 @Nullable getConfirmationMessage(@onNull String packageName, @NonNull Context context)397 public CharSequence getConfirmationMessage(@NonNull String packageName, 398 @NonNull Context context) { 399 if (mBehavior != null) { 400 return mBehavior.getConfirmationMessage(this, packageName, context); 401 } 402 return null; 403 } 404 405 /** 406 * Check whether a package is qualified for this role, i.e. whether it contains all the required 407 * components (plus meeting some other general restrictions). 408 * 409 * @param packageName the package name to check for 410 * @param context the {@code Context} to retrieve system services 411 * 412 * @return whether the package is qualified for a role 413 */ isPackageQualified(@onNull String packageName, @NonNull Context context)414 public boolean isPackageQualified(@NonNull String packageName, @NonNull Context context) { 415 if (!isPackageMinimallyQualifiedAsUser(packageName, Process.myUserHandle(), context)) { 416 return false; 417 } 418 419 if (mBehavior != null) { 420 Boolean isPackageQualified = mBehavior.isPackageQualified(this, packageName, context); 421 if (isPackageQualified != null) { 422 return isPackageQualified; 423 } 424 } 425 426 int requiredComponentsSize = mRequiredComponents.size(); 427 for (int i = 0; i < requiredComponentsSize; i++) { 428 RequiredComponent requiredComponent = mRequiredComponents.get(i); 429 if (requiredComponent.getQualifyingComponentForPackage(packageName, context) == null) { 430 Log.w(LOG_TAG, packageName + " not qualified for " + mName 431 + " due to missing " + requiredComponent); 432 return false; 433 } 434 } 435 436 return true; 437 } 438 439 /** 440 * Get the list of packages that are qualified for this role, i.e. packages containing all the 441 * required components (plus meeting some other general restrictions). 442 * 443 * @param user the user to get the qualifying packages. 444 * @param context the {@code Context} to retrieve system services 445 * 446 * @return the list of packages that are qualified for this role 447 */ 448 @NonNull getQualifyingPackagesAsUser(@onNull UserHandle user, @NonNull Context context)449 public List<String> getQualifyingPackagesAsUser(@NonNull UserHandle user, 450 @NonNull Context context) { 451 List<String> qualifyingPackages = null; 452 453 if (mBehavior != null) { 454 qualifyingPackages = mBehavior.getQualifyingPackagesAsUser(this, user, context); 455 } 456 457 if (qualifyingPackages == null) { 458 ArrayMap<String, Integer> packageComponentCountMap = new ArrayMap<>(); 459 int requiredComponentsSize = mRequiredComponents.size(); 460 for (int requiredComponentsIndex = 0; requiredComponentsIndex < requiredComponentsSize; 461 requiredComponentsIndex++) { 462 RequiredComponent requiredComponent = mRequiredComponents.get( 463 requiredComponentsIndex); 464 465 // This returns at most one component per package. 466 List<ComponentName> qualifyingComponents = 467 requiredComponent.getQualifyingComponentsAsUser(user, context); 468 int qualifyingComponentsSize = qualifyingComponents.size(); 469 for (int qualifyingComponentsIndex = 0; 470 qualifyingComponentsIndex < qualifyingComponentsSize; 471 ++qualifyingComponentsIndex) { 472 ComponentName componentName = qualifyingComponents.get( 473 qualifyingComponentsIndex); 474 475 String packageName = componentName.getPackageName(); 476 Integer componentCount = packageComponentCountMap.get(packageName); 477 packageComponentCountMap.put(packageName, componentCount == null ? 1 478 : componentCount + 1); 479 } 480 } 481 482 qualifyingPackages = new ArrayList<>(); 483 int packageComponentCountMapSize = packageComponentCountMap.size(); 484 for (int i = 0; i < packageComponentCountMapSize; i++) { 485 int componentCount = packageComponentCountMap.valueAt(i); 486 487 if (componentCount != requiredComponentsSize) { 488 continue; 489 } 490 String packageName = packageComponentCountMap.keyAt(i); 491 qualifyingPackages.add(packageName); 492 } 493 } 494 495 int qualifyingPackagesSize = qualifyingPackages.size(); 496 for (int i = 0; i < qualifyingPackagesSize; ) { 497 String packageName = qualifyingPackages.get(i); 498 499 if (!isPackageMinimallyQualifiedAsUser(packageName, user, context)) { 500 qualifyingPackages.remove(i); 501 qualifyingPackagesSize--; 502 } else { 503 i++; 504 } 505 } 506 507 return qualifyingPackages; 508 } 509 isPackageMinimallyQualifiedAsUser( @onNull String packageName, @NonNull UserHandle user, @NonNull Context context)510 private boolean isPackageMinimallyQualifiedAsUser( 511 @NonNull String packageName, @NonNull UserHandle user, @NonNull Context context) { 512 if (Objects.equals(packageName, PACKAGE_NAME_ANDROID_SYSTEM)) { 513 return false; 514 } 515 516 ApplicationInfo applicationInfo = PackageUtils.getApplicationInfoAsUser(packageName, user, 517 context); 518 if (applicationInfo == null) { 519 Log.w(LOG_TAG, "Cannot get ApplicationInfo for package: " + packageName + ", user: " 520 + user.getIdentifier()); 521 return false; 522 } 523 524 if (mSystemOnly && (applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { 525 return false; 526 } 527 528 if (!applicationInfo.enabled) { 529 return false; 530 } 531 532 if (applicationInfo.isInstantApp()) { 533 return false; 534 } 535 536 PackageManager userPackageManager = UserUtils.getUserContext(context, user) 537 .getPackageManager(); 538 if (!userPackageManager.getDeclaredSharedLibraries(packageName, 0).isEmpty()) { 539 return false; 540 } 541 542 return true; 543 } 544 545 /** 546 * Grant this role to an application. 547 * 548 * @param packageName the package name of the application to be granted this role to 549 * @param dontKillApp whether this application should not be killed despite changes 550 * @param overrideUserSetAndFixedPermissions whether to override user set and fixed flags on 551 * permissions 552 * @param context the {@code Context} to retrieve system services 553 */ grant(@onNull String packageName, boolean dontKillApp, boolean overrideUserSetAndFixedPermissions, @NonNull Context context)554 public void grant(@NonNull String packageName, boolean dontKillApp, 555 boolean overrideUserSetAndFixedPermissions, @NonNull Context context) { 556 boolean permissionOrAppOpChanged = Permissions.grant(packageName, mPermissions, true, 557 overrideUserSetAndFixedPermissions, true, false, false, context); 558 559 int appOpsSize = mAppOps.size(); 560 for (int i = 0; i < appOpsSize; i++) { 561 AppOp appOp = mAppOps.get(i); 562 appOp.grant(packageName, context); 563 } 564 565 int preferredActivitiesSize = mPreferredActivities.size(); 566 for (int i = 0; i < preferredActivitiesSize; i++) { 567 PreferredActivity preferredActivity = mPreferredActivities.get(i); 568 preferredActivity.configure(packageName, context); 569 } 570 571 if (mBehavior != null) { 572 mBehavior.grant(this, packageName, context); 573 } 574 575 if (!dontKillApp && permissionOrAppOpChanged && !Permissions.isRuntimePermissionsSupported( 576 packageName, context)) { 577 killApp(packageName, context); 578 } 579 } 580 581 /** 582 * Revoke this role from an application. 583 * 584 * @param packageName the package name of the application to be granted this role to 585 * @param dontKillApp whether this application should not be killed despite changes 586 * @param overrideSystemFixedPermissions whether system-fixed permissions can be revoked 587 * @param context the {@code Context} to retrieve system services 588 */ revoke(@onNull String packageName, boolean dontKillApp, boolean overrideSystemFixedPermissions, @NonNull Context context)589 public void revoke(@NonNull String packageName, boolean dontKillApp, 590 boolean overrideSystemFixedPermissions, @NonNull Context context) { 591 RoleManager roleManager = context.getSystemService(RoleManager.class); 592 List<String> otherRoleNames = roleManager.getHeldRolesFromController(packageName); 593 otherRoleNames.remove(mName); 594 595 List<String> permissionsToRevoke = new ArrayList<>(mPermissions); 596 ArrayMap<String, Role> roles = Roles.get(context); 597 int otherRoleNamesSize = otherRoleNames.size(); 598 for (int i = 0; i < otherRoleNamesSize; i++) { 599 String roleName = otherRoleNames.get(i); 600 Role role = roles.get(roleName); 601 permissionsToRevoke.removeAll(role.getPermissions()); 602 } 603 boolean permissionOrAppOpChanged = Permissions.revoke(packageName, permissionsToRevoke, 604 true, false, overrideSystemFixedPermissions, context); 605 606 List<AppOp> appOpsToRevoke = new ArrayList<>(mAppOps); 607 for (int i = 0; i < otherRoleNamesSize; i++) { 608 String roleName = otherRoleNames.get(i); 609 Role role = roles.get(roleName); 610 appOpsToRevoke.removeAll(role.getAppOps()); 611 } 612 int appOpsSize = appOpsToRevoke.size(); 613 for (int i = 0; i < appOpsSize; i++) { 614 AppOp appOp = appOpsToRevoke.get(i); 615 appOp.revoke(packageName, context); 616 } 617 618 // TODO: Revoke preferred activities? But this is unnecessary for most roles using it as 619 // they have fallback holders. Moreover, clearing the preferred activity might result in 620 // other system components listening to preferred activity change get notified for the 621 // wrong thing when we are removing a exclusive role holder for adding another. 622 623 if (mBehavior != null) { 624 mBehavior.revoke(this, packageName, context); 625 } 626 627 if (!dontKillApp && permissionOrAppOpChanged) { 628 killApp(packageName, context); 629 } 630 } 631 killApp(@onNull String packageName, @NonNull Context context)632 private void killApp(@NonNull String packageName, @NonNull Context context) { 633 if (DEBUG) { 634 Log.i(LOG_TAG, "Killing " + packageName + " due to " 635 + Thread.currentThread().getStackTrace()[3].getMethodName() 636 + "(" + mName + ")"); 637 } 638 ApplicationInfo applicationInfo = PackageUtils.getApplicationInfo(packageName, context); 639 if (applicationInfo == null) { 640 Log.w(LOG_TAG, "Cannot get ApplicationInfo for package: " + packageName); 641 return; 642 } 643 ActivityManager activityManager = context.getSystemService(ActivityManager.class); 644 activityManager.killUid(applicationInfo.uid, "Permission or app op changed"); 645 } 646 647 /** 648 * Check whether the "none" role holder is selected. 649 * 650 * @param context the {@code Context} to retrieve system services 651 * 652 * @return whether the "none" role holder is selected 653 */ isNoneHolderSelected(@onNull Context context)654 private boolean isNoneHolderSelected(@NonNull Context context) { 655 return Utils.getDeviceProtectedSharedPreferences(context).getBoolean( 656 Constants.IS_NONE_ROLE_HOLDER_SELECTED_KEY + mName, false); 657 } 658 659 /** 660 * Callback when a role holder (other than "none") was added. 661 * 662 * @param packageName the package name of the role holder 663 * @param user the user for the role 664 * @param context the {@code Context} to retrieve system services 665 */ onHolderAddedAsUser(@onNull String packageName, @NonNull UserHandle user, @NonNull Context context)666 public void onHolderAddedAsUser(@NonNull String packageName, @NonNull UserHandle user, 667 @NonNull Context context) { 668 Utils.getDeviceProtectedSharedPreferences(UserUtils.getUserContext(context, user)).edit() 669 .remove(Constants.IS_NONE_ROLE_HOLDER_SELECTED_KEY + mName) 670 .apply(); 671 } 672 673 /** 674 * Callback when a role holder (other than "none") was selected in the UI and added 675 * successfully. 676 * 677 * @param packageName the package name of the role holder 678 * @param user the user for the role 679 * @param context the {@code Context} to retrieve system services 680 */ onHolderSelectedAsUser(@onNull String packageName, @NonNull UserHandle user, @NonNull Context context)681 public void onHolderSelectedAsUser(@NonNull String packageName, @NonNull UserHandle user, 682 @NonNull Context context) { 683 if (mBehavior != null) { 684 mBehavior.onHolderSelectedAsUser(this, packageName, user, context); 685 } 686 } 687 688 /** 689 * Callback when a role holder changed. 690 * 691 * @param user the user for the role 692 * @param context the {@code Context} to retrieve system services 693 */ onHolderChangedAsUser(@onNull UserHandle user, @NonNull Context context)694 public void onHolderChangedAsUser(@NonNull UserHandle user, 695 @NonNull Context context) { 696 if (mBehavior != null) { 697 mBehavior.onHolderChangedAsUser(this, user, context); 698 } 699 } 700 701 /** 702 * Callback when the "none" role holder was selected in the UI. 703 * 704 * @param user the user for the role 705 * @param context the {@code Context} to retrieve system services 706 */ onNoneHolderSelectedAsUser(@onNull UserHandle user, @NonNull Context context)707 public void onNoneHolderSelectedAsUser(@NonNull UserHandle user, @NonNull Context context) { 708 Utils.getDeviceProtectedSharedPreferences(UserUtils.getUserContext(context, user)).edit() 709 .putBoolean(Constants.IS_NONE_ROLE_HOLDER_SELECTED_KEY + mName, true) 710 .apply(); 711 } 712 713 @Override toString()714 public String toString() { 715 return "Role{" 716 + "mName='" + mName + '\'' 717 + ", mBehavior=" + mBehavior 718 + ", mExclusive=" + mExclusive 719 + ", mLabelResource=" + mLabelResource 720 + ", mShowNone=" + mShowNone 721 + ", mSystemOnly=" + mSystemOnly 722 + ", mRequiredComponents=" + mRequiredComponents 723 + ", mPermissions=" + mPermissions 724 + ", mAppOps=" + mAppOps 725 + ", mPreferredActivities=" + mPreferredActivities 726 + '}'; 727 } 728 729 @Override equals(Object object)730 public boolean equals(Object object) { 731 if (this == object) { 732 return true; 733 } 734 if (object == null || getClass() != object.getClass()) { 735 return false; 736 } 737 Role that = (Role) object; 738 return mExclusive == that.mExclusive 739 && mLabelResource == that.mLabelResource 740 && mShowNone == that.mShowNone 741 && mSystemOnly == that.mSystemOnly 742 && mName.equals(that.mName) 743 && Objects.equals(mBehavior, that.mBehavior) 744 && mRequiredComponents.equals(that.mRequiredComponents) 745 && mPermissions.equals(that.mPermissions) 746 && mAppOps.equals(that.mAppOps) 747 && mPreferredActivities.equals(that.mPreferredActivities); 748 } 749 750 @Override hashCode()751 public int hashCode() { 752 return Objects.hash(mName, mBehavior, mExclusive, mLabelResource, mShowNone, mSystemOnly, 753 mRequiredComponents, mPermissions, mAppOps, mPreferredActivities); 754 } 755 } 756