• 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.role.controller.model;
18 
19 import android.app.ActivityManager;
20 import android.app.admin.DevicePolicyManager;
21 import android.app.ecm.EnhancedConfirmationManager;
22 import android.app.role.RoleManager;
23 import android.content.ComponentName;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.pm.ApplicationInfo;
27 import android.content.pm.PackageManager;
28 import android.content.pm.SharedLibraryInfo;
29 import android.content.res.Resources;
30 import android.os.Build;
31 import android.os.UserHandle;
32 import android.os.UserManager;
33 import android.permission.flags.Flags;
34 import android.provider.Settings;
35 import android.text.TextUtils;
36 import android.util.ArrayMap;
37 import android.util.ArraySet;
38 import android.util.Log;
39 import android.util.SparseBooleanArray;
40 
41 import androidx.annotation.IntDef;
42 import androidx.annotation.NonNull;
43 import androidx.annotation.Nullable;
44 import androidx.annotation.StringRes;
45 import androidx.annotation.VisibleForTesting;
46 
47 import com.android.modules.utils.build.SdkLevel;
48 import com.android.role.controller.util.CollectionUtils;
49 import com.android.role.controller.util.IntentCompat;
50 import com.android.role.controller.util.PackageUtils;
51 import com.android.role.controller.util.RoleFlags;
52 import com.android.role.controller.util.RoleManagerCompat;
53 import com.android.role.controller.util.SignedPackageUtils;
54 import com.android.role.controller.util.UserUtils;
55 
56 import java.lang.annotation.Retention;
57 import java.lang.annotation.RetentionPolicy;
58 import java.util.ArrayList;
59 import java.util.Collections;
60 import java.util.List;
61 import java.util.Objects;
62 import java.util.function.Supplier;
63 
64 /**
65  * Specifies a role and its properties.
66  * <p>
67  * A role is a unique name within the system associated with certain privileges. There can be
68  * multiple applications qualifying for a role, but only a subset of them can become role holders.
69  * To qualify for a role, an application must meet certain requirements, including defining certain
70  * components in its manifest. Then the application will need user consent to become the role
71  * holder.
72  * <p>
73  * Upon becoming a role holder, the application may be granted certain permissions, have certain
74  * app ops set to certain modes and certain {@code Activity} components configured as preferred for
75  * certain {@code Intent} actions. When an application loses its role, these privileges will also be
76  * revoked.
77  *
78  * @see android.app.role.RoleManager
79  */
80 public class Role {
81 
82     private static final String LOG_TAG = Role.class.getSimpleName();
83 
84     private static final boolean DEBUG = false;
85 
86     private static final String PACKAGE_NAME_ANDROID_SYSTEM = "android";
87 
88     @Retention(RetentionPolicy.SOURCE)
89     @IntDef({
90             EXCLUSIVITY_NONE,
91             EXCLUSIVITY_USER,
92             EXCLUSIVITY_PROFILE_GROUP
93     })
94     public @interface Exclusivity {}
95 
96     /**
97      * Does not enforce any exclusivity, which means multiple apps may hold this role in a user.
98      */
99     public static final int EXCLUSIVITY_NONE = 0;
100 
101     /** Enforces exclusivity within one user. */
102     public static final int EXCLUSIVITY_USER = 1;
103 
104     /**
105      * Enforces exclusivity across all users (including profile users) in the same profile group.
106      */
107     public static final int EXCLUSIVITY_PROFILE_GROUP = 2;
108 
109     /** Set of valid exclusivity values. */
110     private static final SparseBooleanArray sExclusivityValues = new SparseBooleanArray();
111     static {
sExclusivityValues.put(EXCLUSIVITY_NONE, true)112         sExclusivityValues.put(EXCLUSIVITY_NONE, true);
sExclusivityValues.put(EXCLUSIVITY_USER, true)113         sExclusivityValues.put(EXCLUSIVITY_USER, true);
sExclusivityValues.put(EXCLUSIVITY_PROFILE_GROUP, RoleFlags.isProfileGroupExclusivityAvailable())114         sExclusivityValues.put(EXCLUSIVITY_PROFILE_GROUP,
115                 RoleFlags.isProfileGroupExclusivityAvailable());
116     }
117 
118     /**
119      * The name of this role. Must be unique.
120      */
121     @NonNull
122     private final String mName;
123 
124     /**
125      * Whether this role allows bypassing role holder qualification.
126      */
127     private final boolean mAllowBypassingQualification;
128 
129     /**
130      * The behavior of this role.
131      */
132     @Nullable
133     private final RoleBehavior mBehavior;
134 
135     @Nullable
136     private final String mDefaultHoldersResourceName;
137 
138     /**
139      * The string resource for the description of this role.
140      */
141     @StringRes
142     private final int mDescriptionResource;
143 
144     /**
145      * The exclusivity of this role, i.e. whether this role allows multiple holders, or allows at
146      * most one holder within a user or a profile group.
147      */
148     private final int mExclusivity;
149 
150     /**
151      * Whether this role should fall back to the default holder.
152      */
153     private final boolean mFallBackToDefaultHolder;
154 
155     /**
156      * The feature flag for this app op to be granted, or {@code null} if none.
157      */
158     @Nullable
159     private final Supplier<Boolean> mFeatureFlag;
160 
161     /**
162      * The string resource for the label of this role.
163      */
164     @StringRes
165     private final int mLabelResource;
166 
167     /**
168      * The maximum SDK version for this role to be available.
169      */
170     private final int mMaxSdkVersion;
171 
172     /**
173      * The minimum SDK version for this role to be available.
174      */
175     private final int mMinSdkVersion;
176 
177     /**
178      * Whether this role should only grant privileges when a role holder is actively added.
179      */
180     private final boolean mOnlyGrantWhenAdded;
181 
182     /**
183      * Whether this role should override user's choice about privileges when granting.
184      */
185     private final boolean mOverrideUserWhenGranting;
186 
187     /**
188      * The string resource for the request description of this role, shown below the selected app in
189      * the request role dialog.
190      */
191     @StringRes
192     private final int mRequestDescriptionResource;
193 
194     /**
195      * The string resource for the request title of this role, shown as the title of the request
196      * role dialog.
197      */
198     @StringRes
199     private final int mRequestTitleResource;
200 
201     /**
202      * Whether this role is requestable by applications with
203      * {@link android.app.role.RoleManager#createRequestRoleIntent(String)}.
204      */
205     private final boolean mRequestable;
206 
207     /**
208      * The string resource for search keywords of this role, in addition to the label of this role,
209      * if it's non-zero.
210      */
211     @StringRes
212     private final int mSearchKeywordsResource;
213 
214     /**
215      * The string resource for the short label of this role, currently used when in a list of roles.
216      */
217     @StringRes
218     private final int mShortLabelResource;
219 
220     /**
221      * Whether the UI for this role will show the "None" item. Only valid if this role is
222      * {@link #isExclusive()}, and {@link #getFallbackHolder(Context)} should
223      * also return empty to allow actually selecting "None".
224      */
225     private final boolean mShowNone;
226 
227     /**
228      * Whether this role is static, i.e. the role will always be assigned to its default holders.
229      */
230     private final boolean mStatic;
231 
232     /**
233      * Whether this role only accepts system apps as its holders.
234      */
235     private final boolean mSystemOnly;
236 
237     /**
238      * Whether this role is visible to user.
239      */
240     private final boolean mVisible;
241 
242     /**
243      * The required components for an application to qualify for this role.
244      */
245     @NonNull
246     private final List<RequiredComponent> mRequiredComponents;
247 
248     /**
249      * The permissions to be granted by this role.
250      */
251     @NonNull
252     private final List<Permission> mPermissions;
253 
254     /**
255      * The app op permissions to be granted by this role.
256      */
257     @NonNull
258     private final List<Permission> mAppOpPermissions;
259 
260     /**
261      * The app ops to be set to allowed by this role.
262      */
263     @NonNull
264     private final List<AppOp> mAppOps;
265 
266     /**
267      * The set of preferred {@code Activity} configurations to be configured by this role.
268      */
269     @NonNull
270     private final List<PreferredActivity> mPreferredActivities;
271 
272     @Nullable
273     private final String mUiBehaviorName;
274 
Role(@onNull String name, boolean allowBypassingQualification, @Nullable RoleBehavior behavior, @Nullable String defaultHoldersResourceName, @StringRes int descriptionResource, @Exclusivity int exclusivity, boolean fallBackToDefaultHolder, @Nullable Supplier<Boolean> featureFlag, @StringRes int labelResource, int maxSdkVersion, int minSdkVersion, boolean onlyGrantWhenAdded, boolean overrideUserWhenGranting, @StringRes int requestDescriptionResource, @StringRes int requestTitleResource, boolean requestable, @StringRes int searchKeywordsResource, @StringRes int shortLabelResource, boolean showNone, boolean statik, boolean systemOnly, boolean visible, @NonNull List<RequiredComponent> requiredComponents, @NonNull List<Permission> permissions, @NonNull List<Permission> appOpPermissions, @NonNull List<AppOp> appOps, @NonNull List<PreferredActivity> preferredActivities, @Nullable String uiBehaviorName)275     public Role(@NonNull String name, boolean allowBypassingQualification,
276             @Nullable RoleBehavior behavior, @Nullable String defaultHoldersResourceName,
277             @StringRes int descriptionResource, @Exclusivity int exclusivity,
278             boolean fallBackToDefaultHolder, @Nullable Supplier<Boolean> featureFlag,
279             @StringRes int labelResource, int maxSdkVersion, int minSdkVersion,
280             boolean onlyGrantWhenAdded, boolean overrideUserWhenGranting,
281             @StringRes int requestDescriptionResource, @StringRes int requestTitleResource,
282             boolean requestable, @StringRes int searchKeywordsResource,
283             @StringRes int shortLabelResource, boolean showNone, boolean statik, boolean systemOnly,
284             boolean visible, @NonNull List<RequiredComponent> requiredComponents,
285             @NonNull List<Permission> permissions, @NonNull List<Permission> appOpPermissions,
286             @NonNull List<AppOp> appOps, @NonNull List<PreferredActivity> preferredActivities,
287             @Nullable String uiBehaviorName) {
288         mName = name;
289         mAllowBypassingQualification = allowBypassingQualification;
290         mBehavior = behavior;
291         mDefaultHoldersResourceName = defaultHoldersResourceName;
292         mDescriptionResource = descriptionResource;
293         mExclusivity = exclusivity;
294         mFallBackToDefaultHolder = fallBackToDefaultHolder;
295         mFeatureFlag = featureFlag;
296         mLabelResource = labelResource;
297         mMaxSdkVersion = maxSdkVersion;
298         mMinSdkVersion = minSdkVersion;
299         mOnlyGrantWhenAdded = onlyGrantWhenAdded;
300         mOverrideUserWhenGranting = overrideUserWhenGranting;
301         mRequestDescriptionResource = requestDescriptionResource;
302         mRequestTitleResource = requestTitleResource;
303         mRequestable = requestable;
304         mSearchKeywordsResource = searchKeywordsResource;
305         mShortLabelResource = shortLabelResource;
306         mShowNone = showNone;
307         mStatic = statik;
308         mSystemOnly = systemOnly;
309         mVisible = visible;
310         mRequiredComponents = requiredComponents;
311         mPermissions = permissions;
312         mAppOpPermissions = appOpPermissions;
313         mAppOps = appOps;
314         mPreferredActivities = preferredActivities;
315         mUiBehaviorName = uiBehaviorName;
316     }
317 
318     @NonNull
getName()319     public String getName() {
320         return mName;
321     }
322 
323     @Nullable
getBehavior()324     public RoleBehavior getBehavior() {
325         return mBehavior;
326     }
327 
328     @StringRes
getDescriptionResource()329     public int getDescriptionResource() {
330         return mDescriptionResource;
331     }
332 
isExclusive()333     public boolean isExclusive() {
334         return  getExclusivity() != EXCLUSIVITY_NONE;
335     }
336 
337     @Exclusivity
getExclusivity()338     public int getExclusivity() {
339         if (com.android.permission.flags.Flags.crossUserRoleEnabled() && mBehavior != null) {
340             Integer exclusivity = mBehavior.getExclusivity();
341             if (exclusivity != null) {
342                 if (!sExclusivityValues.get(exclusivity)) {
343                     throw new IllegalArgumentException(
344                         "Role " + mName + " has invalid exclusivity: "
345                             + exclusivity);
346                 }
347                 if (mShowNone && exclusivity == EXCLUSIVITY_NONE) {
348                     throw new IllegalArgumentException(
349                         "Role " + mName + " cannot be non-exclusive when showNone is true: "
350                             + exclusivity);
351                 }
352                 if (!mPreferredActivities.isEmpty() && exclusivity == EXCLUSIVITY_PROFILE_GROUP) {
353                     throw new IllegalArgumentException(
354                         "Role " + mName + " cannot have preferred activities when exclusivity is "
355                             + "profileGroup");
356                 }
357                 return exclusivity;
358             }
359         }
360         return mExclusivity;
361     }
362 
363     @Nullable
getFeatureFlag()364     public Supplier<Boolean> getFeatureFlag() {
365         return mFeatureFlag;
366     }
367 
368     @StringRes
getLabelResource()369     public int getLabelResource() {
370         return mLabelResource;
371     }
372 
373     @StringRes
getRequestDescriptionResource()374     public int getRequestDescriptionResource() {
375         return mRequestDescriptionResource;
376     }
377 
378     @StringRes
getRequestTitleResource()379     public int getRequestTitleResource() {
380         return mRequestTitleResource;
381     }
382 
isRequestable()383     public boolean isRequestable() {
384         return mRequestable;
385     }
386 
387     @StringRes
getSearchKeywordsResource()388     public int getSearchKeywordsResource() {
389         return mSearchKeywordsResource;
390     }
391 
392     @StringRes
getShortLabelResource()393     public int getShortLabelResource() {
394         return mShortLabelResource;
395     }
396 
397     /**
398      * @see #mOnlyGrantWhenAdded
399      */
shouldOnlyGrantWhenAdded()400     public boolean shouldOnlyGrantWhenAdded() {
401         return mOnlyGrantWhenAdded;
402     }
403 
404     /**
405      * @see #mOverrideUserWhenGranting
406      */
shouldOverrideUserWhenGranting()407     public boolean shouldOverrideUserWhenGranting() {
408         return mOverrideUserWhenGranting;
409     }
410 
411     /**
412      * @see #mShowNone
413      */
shouldShowNone()414     public boolean shouldShowNone() {
415         return mShowNone;
416     }
417 
isVisible()418     public boolean isVisible() {
419         return mVisible;
420     }
421 
422     @NonNull
getRequiredComponents()423     public List<RequiredComponent> getRequiredComponents() {
424         return mRequiredComponents;
425     }
426 
427     @NonNull
getPermissions()428     public List<Permission> getPermissions() {
429         return mPermissions;
430     }
431 
432     @NonNull
getAppOpPermissions()433     public List<Permission> getAppOpPermissions() {
434         return mAppOpPermissions;
435     }
436 
437     @NonNull
getAppOps()438     public List<AppOp> getAppOps() {
439         return mAppOps;
440     }
441 
442     @NonNull
getPreferredActivities()443     public List<PreferredActivity> getPreferredActivities() {
444         return mPreferredActivities;
445     }
446 
447     @Nullable
getUiBehaviorName()448     public String getUiBehaviorName() {
449         return mUiBehaviorName;
450     }
451 
452     /**
453      * Callback when this role is added to the system for the first time.
454      *
455      * @param user the user to add the role for
456      * @param context the {@code Context} to retrieve system services
457      */
onRoleAddedAsUser(@onNull UserHandle user, @NonNull Context context)458     public void onRoleAddedAsUser(@NonNull UserHandle user, @NonNull Context context) {
459         if (mBehavior != null) {
460             mBehavior.onRoleAddedAsUser(this, user, context);
461         }
462     }
463 
464     /**
465      * Check whether this role is available.
466      *
467      * @param user the user to check for
468      * @param context the {@code Context} to retrieve system services
469      *
470      * @return whether this role is available.
471      */
isAvailableAsUser(@onNull UserHandle user, @NonNull Context context)472     public boolean isAvailableAsUser(@NonNull UserHandle user, @NonNull Context context) {
473         if (!isAvailableByFeatureFlagAndSdkVersion()) {
474             return false;
475         }
476         if (getExclusivity() == EXCLUSIVITY_PROFILE_GROUP) {
477             if (UserUtils.isPrivateProfile(user, context)) {
478                 return false;
479             }
480 
481             // A profile group exclusive role may only be available in a profile when it's
482             // available in the profile parent.
483             UserHandle profileParent = UserUtils.getProfileParentOrSelf(user, context);
484             if (!Objects.equals(user, profileParent)
485                     && !isAvailableAsUser(profileParent, context)) {
486                 return false;
487             }
488         }
489         if (mBehavior != null) {
490             return mBehavior.isAvailableAsUser(this, user, context);
491         }
492         return true;
493     }
494 
495     /**
496      * Check whether this role is available based on SDK version.
497      *
498      * @return whether this role is available based on SDK version
499      */
500     @VisibleForTesting
isAvailableByFeatureFlagAndSdkVersion()501     public boolean isAvailableByFeatureFlagAndSdkVersion() {
502         if (mFeatureFlag != null && !mFeatureFlag.get()) {
503             return false;
504         }
505         return (Build.VERSION.SDK_INT >= mMinSdkVersion
506                 // Workaround to match the value 36 for B in roles.xml before SDK finalization.
507                 || (mMinSdkVersion == 36 && SdkLevel.isAtLeastB()))
508                 && Build.VERSION.SDK_INT <= mMaxSdkVersion;
509     }
510 
511     /**
512      * Check whether this role is static, which may change due to bypassing qualification.
513      *
514      * @param context the {@code Context} to retrieve system services
515      * @return whether this role is static
516      */
isStatic(@onNull Context context)517     public boolean isStatic(@NonNull Context context) {
518         return mStatic && !isBypassingQualification(context);
519     }
520 
521     /**
522      * Get the default holders of this role, which will be added when the role is added for the
523      * first time.
524      *
525      * @param user the user of the role
526      * @param context the {@code Context} to retrieve system services
527      * @return the list of package names of the default holders
528      */
529     @NonNull
getDefaultHoldersAsUser(@onNull UserHandle user, @NonNull Context context)530     public List<String> getDefaultHoldersAsUser(@NonNull UserHandle user,
531             @NonNull Context context) {
532         // Do not allow default role holder for non-active user if the role is exclusive to profile
533         // group
534         if (isNonActiveUserForProfileGroupExclusiveRole(user, context)) {
535             return Collections.emptyList();
536         }
537 
538         if (mBehavior != null) {
539             List<String> defaultHolders = mBehavior.getDefaultHoldersAsUser(this, user, context);
540             if (defaultHolders != null) {
541                 return defaultHolders;
542             }
543         }
544 
545         if (mDefaultHoldersResourceName == null) {
546             return Collections.emptyList();
547         }
548 
549         Resources resources = context.getResources();
550         int resourceId = resources.getIdentifier(mDefaultHoldersResourceName, "string", "android");
551         if (resourceId == 0) {
552             Log.w(LOG_TAG, "Cannot find resource for default holder: "
553                     + mDefaultHoldersResourceName);
554             return Collections.emptyList();
555         }
556 
557         String defaultHolders;
558         try {
559             defaultHolders = resources.getString(resourceId);
560         } catch (Resources.NotFoundException e) {
561             Log.w(LOG_TAG, "Cannot get resource for default holder: " + mDefaultHoldersResourceName,
562                     e);
563             return Collections.emptyList();
564         }
565         if (TextUtils.isEmpty(defaultHolders)) {
566             return Collections.emptyList();
567         }
568 
569         if (isExclusive()) {
570             return CollectionUtils.singletonOrEmpty(
571                     SignedPackageUtils.getPackageNameAsUser(defaultHolders, user, context));
572         } else {
573             return SignedPackageUtils.getPackageNamesAsUser(defaultHolders, user, context);
574         }
575     }
576 
577     /**
578      * Get the fallback holder of this role, which will be added whenever there are no role holders.
579      * <p>
580      * Should return {@code null} if this role {@link #mShowNone shows a "None" item}.
581      *
582      * @param user the user of the role
583      * @param context the {@code Context} to retrieve system services
584      * @return the package name of the fallback holder, or {@code null} if none
585      */
586     @Nullable
getFallbackHolderAsUser(@onNull UserHandle user, @NonNull Context context)587     public String getFallbackHolderAsUser(@NonNull UserHandle user, @NonNull Context context) {
588         if (!RoleManagerCompat.isRoleFallbackEnabledAsUser(this, user, context)) {
589             return null;
590         }
591         // Do not fall back for non-active user if the role is exclusive to profile group
592         if (isNonActiveUserForProfileGroupExclusiveRole(user, context)) {
593             return null;
594         }
595         if (mFallBackToDefaultHolder) {
596             return CollectionUtils.firstOrNull(getDefaultHoldersAsUser(user, context));
597         }
598         if (mBehavior != null) {
599             return mBehavior.getFallbackHolderAsUser(this, user, context);
600         }
601         return null;
602     }
603 
isNonActiveUserForProfileGroupExclusiveRole(@onNull UserHandle user, @NonNull Context context)604     private boolean isNonActiveUserForProfileGroupExclusiveRole(@NonNull UserHandle user,
605             @NonNull Context context) {
606         if (RoleFlags.isProfileGroupExclusivityAvailable()
607                 && getExclusivity() == Role.EXCLUSIVITY_PROFILE_GROUP) {
608             Context userContext = UserUtils.getUserContext(context, user);
609             RoleManager userRoleManager = userContext.getSystemService(RoleManager.class);
610             return !Objects.equals(userRoleManager.getActiveUserForRole(mName), user);
611         }
612         return false;
613     }
614 
615     /**
616      * Check whether this role is allowed to bypass qualification, if enabled globally.
617      *
618      * @param context the {@code Context} to retrieve system services
619      *
620      * @return whether this role is allowed to bypass qualification
621      */
shouldAllowBypassingQualification(@onNull Context context)622     public boolean shouldAllowBypassingQualification(@NonNull Context context) {
623         if (mBehavior != null) {
624             Boolean allowBypassingQualification = mBehavior.shouldAllowBypassingQualification(this,
625                     context);
626             if (allowBypassingQualification != null) {
627                 return allowBypassingQualification;
628             }
629         }
630         return mAllowBypassingQualification;
631     }
632 
isBypassingQualification(@onNull Context context)633     private boolean isBypassingQualification(@NonNull Context context) {
634         RoleManager roleManager = context.getSystemService(RoleManager.class);
635         return shouldAllowBypassingQualification(context)
636                 && RoleManagerCompat.isBypassingRoleQualification(roleManager);
637     }
638 
639     /**
640      * Check whether a package is qualified for this role, i.e. whether it contains all the required
641      * components (plus meeting some other general restrictions).
642      *
643      * @param packageName the package name to check for
644      * @param user the user to check for
645      * @param context the {@code Context} to retrieve system services
646      *
647      * @return whether the package is qualified for a role
648      */
isPackageQualifiedAsUser(@onNull String packageName, @NonNull UserHandle user, @NonNull Context context)649     public boolean isPackageQualifiedAsUser(@NonNull String packageName, @NonNull UserHandle user,
650             @NonNull Context context) {
651         if (isBypassingQualification(context)) {
652             return true;
653         }
654 
655         ApplicationInfo applicationInfo = PackageUtils.getApplicationInfoAsUser(packageName, user,
656                 context);
657         if (applicationInfo == null) {
658             Log.w(LOG_TAG, "Cannot get ApplicationInfo for package: " + packageName);
659             return false;
660         }
661         if (!isPackageMinimallyQualifiedAsUser(applicationInfo, user, context)) {
662             return false;
663         }
664 
665         if (mBehavior != null) {
666             Boolean isPackageQualified = mBehavior.isPackageQualifiedAsUser(this, packageName,
667                     user, context);
668             if (isPackageQualified != null) {
669                 return isPackageQualified;
670             }
671         }
672 
673         int requiredComponentsSize = mRequiredComponents.size();
674         for (int i = 0; i < requiredComponentsSize; i++) {
675             RequiredComponent requiredComponent = mRequiredComponents.get(i);
676 
677             if (!requiredComponent.isRequired(applicationInfo)) {
678                 continue;
679             }
680 
681             if (requiredComponent.getQualifyingComponentForPackageAsUser(packageName, user, context)
682                     == null) {
683                 Log.i(LOG_TAG, packageName + " not qualified for " + mName
684                         + " due to missing " + requiredComponent);
685                 return false;
686             }
687         }
688 
689         if (mStatic && !getDefaultHoldersAsUser(user, context).contains(packageName)) {
690             return false;
691         }
692 
693         return true;
694     }
695 
696     /**
697      * Get the list of packages that are qualified for this role, i.e. packages containing all the
698      * required components (plus meeting some other general restrictions).
699      *
700      * @param user the user to get the qualifying packages.
701      * @param context the {@code Context} to retrieve system services
702      *
703      * @return the list of packages that are qualified for this role
704      */
705     @NonNull
getQualifyingPackagesAsUser(@onNull UserHandle user, @NonNull Context context)706     public List<String> getQualifyingPackagesAsUser(@NonNull UserHandle user,
707             @NonNull Context context) {
708         List<String> qualifyingPackages = null;
709 
710         if (mBehavior != null) {
711             qualifyingPackages = mBehavior.getQualifyingPackagesAsUser(this, user, context);
712         }
713 
714         ArrayMap<String, ApplicationInfo> packageApplicationInfoMap = new ArrayMap<>();
715         if (qualifyingPackages == null) {
716             ArrayMap<String, ArraySet<RequiredComponent>> packageRequiredComponentsMap =
717                     new ArrayMap<>();
718             int requiredComponentsSize = mRequiredComponents.size();
719             for (int requiredComponentsIndex = 0; requiredComponentsIndex < requiredComponentsSize;
720                     requiredComponentsIndex++) {
721                 RequiredComponent requiredComponent = mRequiredComponents.get(
722                         requiredComponentsIndex);
723 
724                 if (!requiredComponent.isAvailable()) {
725                     continue;
726                 }
727 
728                 // This returns at most one component per package.
729                 List<ComponentName> qualifyingComponents =
730                         requiredComponent.getQualifyingComponentsAsUser(user, context);
731                 int qualifyingComponentsSize = qualifyingComponents.size();
732                 for (int qualifyingComponentsIndex = 0;
733                         qualifyingComponentsIndex < qualifyingComponentsSize;
734                         ++qualifyingComponentsIndex) {
735                     ComponentName componentName = qualifyingComponents.get(
736                             qualifyingComponentsIndex);
737 
738                     String packageName = componentName.getPackageName();
739                     ArraySet<RequiredComponent> packageRequiredComponents =
740                             packageRequiredComponentsMap.get(packageName);
741                     if (packageRequiredComponents == null) {
742                         packageRequiredComponents = new ArraySet<>();
743                         packageRequiredComponentsMap.put(packageName, packageRequiredComponents);
744                     }
745                     packageRequiredComponents.add(requiredComponent);
746                 }
747             }
748 
749             qualifyingPackages = new ArrayList<>();
750             int packageRequiredComponentsMapSize = packageRequiredComponentsMap.size();
751             for (int packageRequiredComponentsMapIndex = 0;
752                     packageRequiredComponentsMapIndex < packageRequiredComponentsMapSize;
753                     packageRequiredComponentsMapIndex++) {
754                 String packageName = packageRequiredComponentsMap.keyAt(
755                         packageRequiredComponentsMapIndex);
756                 ArraySet<RequiredComponent> packageRequiredComponents =
757                         packageRequiredComponentsMap.valueAt(packageRequiredComponentsMapIndex);
758 
759                 ApplicationInfo applicationInfo = packageApplicationInfoMap.get(packageName);
760                 if (applicationInfo == null) {
761                     applicationInfo = PackageUtils.getApplicationInfoAsUser(packageName, user,
762                             context);
763                     if (applicationInfo == null) {
764                         Log.w(LOG_TAG, "Cannot get ApplicationInfo for package: " + packageName
765                                 + ", user: " + user.getIdentifier());
766                         continue;
767                     }
768                     packageApplicationInfoMap.put(packageName, applicationInfo);
769                 }
770 
771                 boolean hasAllRequiredComponents = true;
772                 for (int requiredComponentsIndex = 0;
773                         requiredComponentsIndex < requiredComponentsSize;
774                         requiredComponentsIndex++) {
775                     RequiredComponent requiredComponent = mRequiredComponents.get(
776                             requiredComponentsIndex);
777 
778                     if (!requiredComponent.isRequired(applicationInfo)) {
779                         continue;
780                     }
781 
782                     if (!packageRequiredComponents.contains(requiredComponent)) {
783                         hasAllRequiredComponents = false;
784                         break;
785                     }
786                 }
787 
788                 if (hasAllRequiredComponents) {
789                     qualifyingPackages.add(packageName);
790                 }
791             }
792         }
793 
794         int qualifyingPackagesSize = qualifyingPackages.size();
795         for (int i = 0; i < qualifyingPackagesSize; ) {
796             String packageName = qualifyingPackages.get(i);
797 
798             ApplicationInfo applicationInfo = packageApplicationInfoMap.get(packageName);
799             if (applicationInfo == null) {
800                 applicationInfo = PackageUtils.getApplicationInfoAsUser(packageName, user,
801                         context);
802                 if (applicationInfo == null) {
803                     Log.w(LOG_TAG, "Cannot get ApplicationInfo for package: " + packageName
804                             + ", user: " + user.getIdentifier());
805                     continue;
806                 }
807                 packageApplicationInfoMap.put(packageName, applicationInfo);
808             }
809 
810             if (!isPackageMinimallyQualifiedAsUser(applicationInfo, user, context)) {
811                 qualifyingPackages.remove(i);
812                 qualifyingPackagesSize--;
813             } else {
814                 i++;
815             }
816         }
817 
818         return qualifyingPackages;
819     }
820 
isPackageMinimallyQualifiedAsUser(@onNull ApplicationInfo applicationInfo, @NonNull UserHandle user, @NonNull Context context)821     private boolean isPackageMinimallyQualifiedAsUser(@NonNull ApplicationInfo applicationInfo,
822                                                       @NonNull UserHandle user,
823                                                       @NonNull Context context) {
824         String packageName = applicationInfo.packageName;
825         if (Objects.equals(packageName, PACKAGE_NAME_ANDROID_SYSTEM)) {
826             return false;
827         }
828 
829         if (mSystemOnly && (applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
830             return false;
831         }
832 
833         if (!applicationInfo.enabled) {
834             return false;
835         }
836 
837         if (applicationInfo.isInstantApp()) {
838             return false;
839         }
840 
841         PackageManager userPackageManager = UserUtils.getUserContext(context, user)
842                 .getPackageManager();
843         List<SharedLibraryInfo> declaredLibraries = userPackageManager.getDeclaredSharedLibraries(
844                 packageName, 0);
845         final int libCount = declaredLibraries.size();
846         for (int i = 0; i < libCount; i++) {
847             SharedLibraryInfo sharedLibrary = declaredLibraries.get(i);
848             if (sharedLibrary.getType() != SharedLibraryInfo.TYPE_DYNAMIC) {
849                 return false;
850             }
851         }
852 
853         return true;
854     }
855 
856     /**
857      * Grant this role to an application.
858      *
859      * @param packageName the package name of the application to be granted this role to
860      * @param dontKillApp whether this application should not be killed despite changes
861      * @param overrideUser whether to override user when granting privileges
862      * @param user the user of the application
863      * @param context the {@code Context} to retrieve system services
864      */
grantAsUser(@onNull String packageName, boolean dontKillApp, boolean overrideUser, @NonNull UserHandle user, @NonNull Context context)865     public void grantAsUser(@NonNull String packageName, boolean dontKillApp,
866             boolean overrideUser, @NonNull UserHandle user, @NonNull Context context) {
867         boolean permissionOrAppOpChanged = Permissions.grantAsUser(packageName,
868                 Permissions.filterBySdkVersionAsUser(mPermissions, user, context),
869                 SdkLevel.isAtLeastS() ? !mSystemOnly : true, overrideUser, true, false, false,
870                 user, context);
871 
872         List<String> appOpPermissionsToGrant =
873                 Permissions.filterBySdkVersionAsUser(mAppOpPermissions, user, context);
874         int appOpPermissionsSize = appOpPermissionsToGrant.size();
875         for (int i = 0; i < appOpPermissionsSize; i++) {
876             String appOpPermission = appOpPermissionsToGrant.get(i);
877             AppOpPermissions.grantAsUser(packageName, appOpPermission, overrideUser, user, context);
878         }
879 
880         int appOpsSize = mAppOps.size();
881         for (int i = 0; i < appOpsSize; i++) {
882             AppOp appOp = mAppOps.get(i);
883             appOp.grantAsUser(packageName, user, context);
884         }
885 
886         int preferredActivitiesSize = mPreferredActivities.size();
887         for (int i = 0; i < preferredActivitiesSize; i++) {
888             PreferredActivity preferredActivity = mPreferredActivities.get(i);
889             preferredActivity.configureAsUser(packageName, user, context);
890         }
891 
892         if (mBehavior != null) {
893             mBehavior.grantAsUser(this, packageName, user, context);
894         }
895 
896         if (!dontKillApp && permissionOrAppOpChanged
897                 && !Permissions.isRuntimePermissionsSupportedAsUser(packageName, user, context)) {
898             killAppAsUser(packageName, user, context);
899         }
900     }
901 
902     /**
903      * Revoke this role from an application.
904      *
905      * @param packageName the package name of the application to be granted this role to
906      * @param dontKillApp whether this application should not be killed despite changes
907      * @param overrideSystemFixedPermissions whether system-fixed permissions can be revoked
908      * @param user the user of the role
909      * @param context the {@code Context} to retrieve system services
910      */
revokeAsUser(@onNull String packageName, boolean dontKillApp, boolean overrideSystemFixedPermissions, @NonNull UserHandle user, @NonNull Context context)911     public void revokeAsUser(@NonNull String packageName, boolean dontKillApp,
912             boolean overrideSystemFixedPermissions, @NonNull UserHandle user,
913             @NonNull Context context) {
914         Context userContext = UserUtils.getUserContext(context, user);
915         RoleManager userRoleManager = userContext.getSystemService(RoleManager.class);
916         List<String> otherRoleNames = userRoleManager.getHeldRolesFromController(packageName);
917         otherRoleNames.remove(mName);
918 
919         List<String> permissionsToRevoke =
920                 Permissions.filterBySdkVersionAsUser(mPermissions, user, context);
921         ArrayMap<String, Role> roles = Roles.get(context);
922         int otherRoleNamesSize = otherRoleNames.size();
923         for (int i = 0; i < otherRoleNamesSize; i++) {
924             String roleName = otherRoleNames.get(i);
925             Role role = roles.get(roleName);
926             permissionsToRevoke.removeAll(
927                     Permissions.filterBySdkVersionAsUser(role.mPermissions, user, context));
928         }
929 
930         boolean permissionOrAppOpChanged = Permissions.revokeAsUser(packageName,
931                 permissionsToRevoke, true, false, overrideSystemFixedPermissions, user, context);
932 
933         List<String> appOpPermissionsToRevoke = Permissions.filterBySdkVersionAsUser(
934                 mAppOpPermissions, user, context);
935         for (int i = 0; i < otherRoleNamesSize; i++) {
936             String roleName = otherRoleNames.get(i);
937             Role role = roles.get(roleName);
938             appOpPermissionsToRevoke.removeAll(
939                     Permissions.filterBySdkVersionAsUser(role.mAppOpPermissions, user, context));
940         }
941         int appOpPermissionsSize = appOpPermissionsToRevoke.size();
942         for (int i = 0; i < appOpPermissionsSize; i++) {
943             String appOpPermission = appOpPermissionsToRevoke.get(i);
944             AppOpPermissions.revokeAsUser(packageName, appOpPermission, user, context);
945         }
946 
947         List<AppOp> appOpsToRevoke = new ArrayList<>(mAppOps);
948         for (int i = 0; i < otherRoleNamesSize; i++) {
949             String roleName = otherRoleNames.get(i);
950             Role role = roles.get(roleName);
951             appOpsToRevoke.removeAll(role.mAppOps);
952         }
953         int appOpsSize = appOpsToRevoke.size();
954         for (int i = 0; i < appOpsSize; i++) {
955             AppOp appOp = appOpsToRevoke.get(i);
956             appOp.revokeAsUser(packageName, user, context);
957         }
958 
959         // TODO: Revoke preferred activities? But this is unnecessary for most roles using it as
960         //  they have fallback holders. Moreover, clearing the preferred activity might result in
961         //  other system components listening to preferred activity change get notified for the
962         //  wrong thing when we are removing a exclusive role holder for adding another.
963 
964         if (mBehavior != null) {
965             mBehavior.revokeAsUser(this, packageName, user, context);
966         }
967 
968         if (!dontKillApp && permissionOrAppOpChanged) {
969             killAppAsUser(packageName, user, context);
970         }
971     }
972 
killAppAsUser(@onNull String packageName, @NonNull UserHandle user, @NonNull Context context)973     private void killAppAsUser(@NonNull String packageName, @NonNull UserHandle user,
974             @NonNull Context context) {
975         if (DEBUG) {
976             Log.i(LOG_TAG, "Killing " + packageName + " due to "
977                     + Thread.currentThread().getStackTrace()[3].getMethodName()
978                     + "(" + mName + ")");
979         }
980         ApplicationInfo applicationInfo = PackageUtils.getApplicationInfoAsUser(packageName, user,
981                 context);
982         if (applicationInfo == null) {
983             Log.w(LOG_TAG, "Cannot get ApplicationInfo for package: " + packageName);
984             return;
985         }
986         ActivityManager activityManager = context.getSystemService(ActivityManager.class);
987         activityManager.killUid(applicationInfo.uid, "Permission or app op changed");
988     }
989 
990     /**
991      * Callback when a role holder (other than "none") was added.
992      *
993      * @param packageName the package name of the role holder
994      * @param user the user for the role
995      * @param context the {@code Context} to retrieve system services
996      */
onHolderAddedAsUser(@onNull String packageName, @NonNull UserHandle user, @NonNull Context context)997     public void onHolderAddedAsUser(@NonNull String packageName, @NonNull UserHandle user,
998             @NonNull Context context) {
999         if (RoleFlags.isProfileGroupExclusivityAvailable()
1000                 && com.android.permission.flags.Flags.crossUserRoleUxBugfixEnabled()
1001                 && getExclusivity() == Role.EXCLUSIVITY_PROFILE_GROUP) {
1002             UserHandle profileParent = UserUtils.getProfileParentOrSelf(user, context);
1003             RoleManagerCompat.setRoleFallbackEnabledAsUser(this, true, profileParent, context);
1004         } else {
1005             RoleManagerCompat.setRoleFallbackEnabledAsUser(this, true, user, context);
1006         }
1007     }
1008 
1009     /**
1010      * Callback when a role holder (other than "none") was selected in the UI and added
1011      * successfully.
1012      *
1013      * @param packageName the package name of the role holder
1014      * @param user the user for the role
1015      * @param context the {@code Context} to retrieve system services
1016      */
onHolderSelectedAsUser(@onNull String packageName, @NonNull UserHandle user, @NonNull Context context)1017     public void onHolderSelectedAsUser(@NonNull String packageName, @NonNull UserHandle user,
1018             @NonNull Context context) {
1019         if (mBehavior != null) {
1020             mBehavior.onHolderSelectedAsUser(this, packageName, user, context);
1021         }
1022     }
1023 
1024     /**
1025      * Callback when a role holder changed.
1026      *
1027      * @param user the user for the role
1028      * @param context the {@code Context} to retrieve system services
1029      */
onHolderChangedAsUser(@onNull UserHandle user, @NonNull Context context)1030     public void onHolderChangedAsUser(@NonNull UserHandle user,
1031             @NonNull Context context) {
1032         if (mBehavior != null) {
1033             mBehavior.onHolderChangedAsUser(this, user, context);
1034         }
1035     }
1036 
1037     /**
1038      * Callback when the "none" role holder was selected in the UI.
1039      *
1040      * @param user the user for the role
1041      * @param context the {@code Context} to retrieve system services
1042      */
onNoneHolderSelectedAsUser(@onNull UserHandle user, @NonNull Context context)1043     public void onNoneHolderSelectedAsUser(@NonNull UserHandle user, @NonNull Context context) {
1044         RoleManagerCompat.setRoleFallbackEnabledAsUser(this, false, user, context);
1045         if (RoleFlags.isProfileGroupExclusivityAvailable()
1046                 && getExclusivity() == Role.EXCLUSIVITY_PROFILE_GROUP) {
1047             RoleManager roleManager = context.getSystemService(RoleManager.class);
1048             roleManager.setActiveUserForRole(mName, user, 0);
1049         }
1050     }
1051 
1052     /**
1053      * Check whether this role should be visible to user.
1054      *
1055      * @param user the user to check for
1056      * @param context the {@code Context} to retrieve system services
1057      *
1058      * @return whether this role should be visible to user
1059      */
isVisibleAsUser(@onNull UserHandle user, @NonNull Context context)1060     public boolean isVisibleAsUser(@NonNull UserHandle user, @NonNull Context context) {
1061         if (mBehavior != null) {
1062             Boolean isVisibleAsUser = mBehavior.isVisibleAsUser(this, user, context);
1063             if (isVisibleAsUser != null) {
1064                 if (isVisibleAsUser && mStatic) {
1065                     throw new IllegalArgumentException("static=\"true\" is invalid for a visible "
1066                             + "role: " + mName);
1067                 }
1068                 if (isVisibleAsUser && (mDescriptionResource == 0
1069                         || mLabelResource == 0
1070                         || mShortLabelResource == 0)) {
1071                     throw new IllegalArgumentException("description, label, and shortLabel are "
1072                             + "required for a visible role: " + mName);
1073                 }
1074                 return isVisibleAsUser;
1075             }
1076         }
1077         return isVisible();
1078     }
1079 
1080     /**
1081      * Check whether a qualifying application should be visible to user.
1082      *
1083      * @param applicationInfo the {@link ApplicationInfo} for the application
1084      * @param user the user for the application
1085      * @param context the {@code Context} to retrieve system services
1086      *
1087      * @return whether the qualifying application should be visible to user
1088      */
isApplicationVisibleAsUser(@onNull ApplicationInfo applicationInfo, @NonNull UserHandle user, @NonNull Context context)1089     public boolean isApplicationVisibleAsUser(@NonNull ApplicationInfo applicationInfo,
1090             @NonNull UserHandle user,  @NonNull Context context) {
1091         RoleBehavior behavior = getBehavior();
1092         if (behavior == null) {
1093             return true;
1094         }
1095         return behavior.isApplicationVisibleAsUser(this, applicationInfo, user, context);
1096     }
1097 
1098     /**
1099      * Check whether this role is restricted and return the {@code Intent} for the restriction if it
1100      * is.
1101      * <p>
1102      * If a role is restricted, it is implied that all applications are restricted for the role as
1103      * well.
1104      *
1105      * @param user the user to check for
1106      * @param context the {@code Context} to retrieve system services
1107      *
1108      * @return the {@code Intent} for the restriction if this role is restricted, or {@code null}
1109      *         otherwise.
1110      */
1111     @Nullable
getRestrictionIntentAsUser(@onNull UserHandle user, @NonNull Context context)1112     public Intent getRestrictionIntentAsUser(@NonNull UserHandle user, @NonNull Context context) {
1113         if (SdkLevel.isAtLeastU() && isExclusive()) {
1114             boolean crossUserRoleUxBugfixEnabled =
1115                     com.android.permission.flags.Flags.crossUserRoleUxBugfixEnabled();
1116             if (crossUserRoleUxBugfixEnabled && getExclusivity() == EXCLUSIVITY_PROFILE_GROUP) {
1117                 DevicePolicyManager devicePolicyManager =
1118                         context.getSystemService(DevicePolicyManager.class);
1119                 if (!devicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile()) {
1120                     // For profileGroup exclusive roles users on BYOD are free to choose personal or
1121                     // work profile app regardless of DISALLOW_CONFIG_DEFAULT_APPS
1122                     return null;
1123                 }
1124             }
1125 
1126             // Otherwise if role is profileGroup exclusive check DISALLOW_CONFIG_DEFAULT_APPS for
1127             // all users
1128             List<UserHandle> profiles =
1129                     (crossUserRoleUxBugfixEnabled && getExclusivity() == EXCLUSIVITY_PROFILE_GROUP)
1130                             ? UserUtils.getUserProfiles(user, context, true)
1131                             : List.of(user);
1132             final int profilesSize = profiles.size();
1133             for (int i = 0; i < profilesSize; i++) {
1134                 UserHandle profile = profiles.get(i);
1135                 UserManager userManager = context.getSystemService(UserManager.class);
1136                 if (userManager.hasUserRestrictionForUser(
1137                         UserManager.DISALLOW_CONFIG_DEFAULT_APPS, profile)) {
1138                     return new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS)
1139                             .putExtra(
1140                                     DevicePolicyManager.EXTRA_RESTRICTION,
1141                                     UserManager.DISALLOW_CONFIG_DEFAULT_APPS)
1142                             .putExtra(Intent.EXTRA_USER, profile)
1143                             .putExtra(IntentCompat.EXTRA_USER_ID, profile.getIdentifier());
1144                 }
1145             }
1146         }
1147         return null;
1148     }
1149 
1150     /**
1151      * Check whether an application is restricted for this role and return the {@code Intent} for
1152      * the restriction if it is.
1153      * <p>
1154      * If a role is restricted, it is implied that all applications are restricted for the role as
1155      * well.
1156      *
1157      * @param applicationInfo the {@link ApplicationInfo} for the application
1158      * @param user the user to check for
1159      * @param context the {@code Context} to retrieve system services
1160      *
1161      * @return the {@code Intent} for the restriction if the application is restricted for this
1162      *         role, or {@code null} otherwise.
1163      */
1164     @Nullable
getApplicationRestrictionIntentAsUser(@onNull ApplicationInfo applicationInfo, @NonNull UserHandle user, @NonNull Context context)1165     public Intent getApplicationRestrictionIntentAsUser(@NonNull ApplicationInfo applicationInfo,
1166             @NonNull UserHandle user, @NonNull Context context) {
1167         if (SdkLevel.isAtLeastV() && Flags.enhancedConfirmationModeApisEnabled()) {
1168             Context userContext = UserUtils.getUserContext(context, user);
1169             EnhancedConfirmationManager userEnhancedConfirmationManager =
1170                     userContext.getSystemService(EnhancedConfirmationManager.class);
1171             String packageName = applicationInfo.packageName;
1172             boolean isRestricted;
1173             try {
1174                 isRestricted = userEnhancedConfirmationManager.isRestricted(packageName, mName);
1175             } catch (PackageManager.NameNotFoundException e) {
1176                 Log.w(LOG_TAG, "Cannot check enhanced confirmation restriction for package: "
1177                         + packageName, e);
1178                 isRestricted = false;
1179             }
1180             if (isRestricted) {
1181                 try {
1182                     return userEnhancedConfirmationManager.createRestrictedSettingDialogIntent(
1183                             packageName, mName);
1184                 } catch (PackageManager.NameNotFoundException e) {
1185                     Log.w(LOG_TAG, "Cannot create enhanced confirmation restriction intent for"
1186                             + " package: " + packageName, e);
1187                 }
1188             }
1189         }
1190         return getRestrictionIntentAsUser(user, context);
1191     }
1192 
1193     @Override
toString()1194     public String toString() {
1195         return "Role{"
1196                 + "mName='" + mName + '\''
1197                 + ", mAllowBypassingQualification=" + mAllowBypassingQualification
1198                 + ", mBehavior=" + mBehavior
1199                 + ", mDefaultHoldersResourceName=" + mDefaultHoldersResourceName
1200                 + ", mDescriptionResource=" + mDescriptionResource
1201                 + ", mExclusivity=" + mExclusivity
1202                 + ", mFallBackToDefaultHolder=" + mFallBackToDefaultHolder
1203                 + ", mFeatureFlag=" + mFeatureFlag
1204                 + ", mLabelResource=" + mLabelResource
1205                 + ", mMaxSdkVersion=" + mMaxSdkVersion
1206                 + ", mMinSdkVersion=" + mMinSdkVersion
1207                 + ", mOnlyGrantWhenAdded=" + mOnlyGrantWhenAdded
1208                 + ", mOverrideUserWhenGranting=" + mOverrideUserWhenGranting
1209                 + ", mRequestDescriptionResource=" + mRequestDescriptionResource
1210                 + ", mRequestTitleResource=" + mRequestTitleResource
1211                 + ", mRequestable=" + mRequestable
1212                 + ", mSearchKeywordsResource=" + mSearchKeywordsResource
1213                 + ", mShortLabelResource=" + mShortLabelResource
1214                 + ", mShowNone=" + mShowNone
1215                 + ", mStatic=" + mStatic
1216                 + ", mSystemOnly=" + mSystemOnly
1217                 + ", mVisible=" + mVisible
1218                 + ", mRequiredComponents=" + mRequiredComponents
1219                 + ", mPermissions=" + mPermissions
1220                 + ", mAppOpPermissions=" + mAppOpPermissions
1221                 + ", mAppOps=" + mAppOps
1222                 + ", mPreferredActivities=" + mPreferredActivities
1223                 + ", mUiBehaviorName=" + mUiBehaviorName
1224                 + '}';
1225     }
1226 }
1227