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