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