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