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