• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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