• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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.permission.model;
18 
19 import static android.Manifest.permission.ACCESS_BACKGROUND_LOCATION;
20 import static android.Manifest.permission.ACCESS_FINE_LOCATION;
21 import static android.Manifest.permission.POST_NOTIFICATIONS;
22 import static android.app.AppOpsManager.MODE_ALLOWED;
23 import static android.app.AppOpsManager.MODE_FOREGROUND;
24 import static android.app.AppOpsManager.MODE_IGNORED;
25 import static android.app.AppOpsManager.OPSTR_LEGACY_STORAGE;
26 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
27 
28 import android.Manifest;
29 import android.app.ActivityManager;
30 import android.app.AppOpsManager;
31 import android.app.Application;
32 import android.content.Context;
33 import android.content.pm.PackageInfo;
34 import android.content.pm.PackageItemInfo;
35 import android.content.pm.PackageManager;
36 import android.content.pm.PackageManager.NameNotFoundException;
37 import android.content.pm.PermissionGroupInfo;
38 import android.content.pm.PermissionInfo;
39 import android.os.Binder;
40 import android.os.Build;
41 import android.os.UserHandle;
42 import android.permission.PermissionManager;
43 import android.text.TextUtils;
44 import android.util.ArrayMap;
45 import android.util.Log;
46 
47 import androidx.annotation.NonNull;
48 import androidx.annotation.Nullable;
49 import androidx.annotation.StringRes;
50 
51 import com.android.modules.utils.build.SdkLevel;
52 import com.android.permissioncontroller.PermissionControllerApplication;
53 import com.android.permissioncontroller.R;
54 import com.android.permissioncontroller.permission.service.LocationAccessCheck;
55 import com.android.permissioncontroller.permission.utils.ArrayUtils;
56 import com.android.permissioncontroller.permission.utils.KotlinUtils;
57 import com.android.permissioncontroller.permission.utils.LocationUtils;
58 import com.android.permissioncontroller.permission.utils.SoftRestrictedPermissionPolicy;
59 import com.android.permissioncontroller.permission.utils.Utils;
60 
61 import java.text.Collator;
62 import java.util.ArrayList;
63 import java.util.List;
64 import java.util.Objects;
65 import java.util.Set;
66 
67 /**
68  * All permissions of a permission group that are requested by an app.
69  *
70  * <p>Some permissions only grant access to the protected resource while the app is running in the
71  * foreground. These permissions are considered "split" into this foreground and a matching
72  * "background" permission.
73  *
74  * <p>All background permissions of the group are not in the main group and will not be affected
75  * by operations on the group. The background permissions can be found in the {@link
76  * #getBackgroundPermissions() background permissions group}.
77  */
78 public final class AppPermissionGroup implements Comparable<AppPermissionGroup> {
79     private static final String LOG_TAG = AppPermissionGroup.class.getSimpleName();
80     private static final String PLATFORM_PACKAGE_NAME = "android";
81 
82     private static final String KILL_REASON_APP_OP_CHANGE = "Permission related app op changed";
83 
84     /**
85      * Importance level to define the threshold for whether a package is in a state which resets the
86      * timer on its one-time permission session
87      */
88     private static final int ONE_TIME_PACKAGE_IMPORTANCE_LEVEL_TO_RESET_TIMER =
89             ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
90 
91     /**
92      * Importance level to define the threshold for whether a package is in a state which keeps its
93      * one-time permission session alive after the timer ends
94      */
95     private static final int ONE_TIME_PACKAGE_IMPORTANCE_LEVEL_TO_KEEP_SESSION_ALIVE =
96             ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
97 
98     private final Context mContext;
99     private final UserHandle mUserHandle;
100     private final PackageManager mPackageManager;
101     private final AppOpsManager mAppOps;
102     private final ActivityManager mActivityManager;
103     private final Collator mCollator;
104 
105     private final PackageInfo mPackageInfo;
106     private final String mName;
107     private final String mDeclaringPackage;
108     private final CharSequence mLabel;
109     private final CharSequence mFullLabel;
110     private final @StringRes int mRequest;
111     private final @StringRes int mRequestDetail;
112     private final @StringRes int mBackgroundRequest;
113     private final @StringRes int mBackgroundRequestDetail;
114     private final @StringRes int mUpgradeRequest;
115     private final @StringRes int mUpgradeRequestDetail;
116     private final CharSequence mDescription;
117     private final ArrayMap<String, Permission> mPermissions = new ArrayMap<>();
118     private final String mIconPkg;
119     private final int mIconResId;
120 
121     /** Delay changes until {@link #persistChanges} is called */
122     private final boolean mDelayChanges;
123 
124     /**
125      * Some permissions are split into foreground and background permission. All non-split and
126      * foreground permissions are in {@link #mPermissions}, all background permissions are in
127      * this field.
128      */
129     private AppPermissionGroup mBackgroundPermissions;
130 
131     private final boolean mAppSupportsRuntimePermissions;
132     private final boolean mIsEphemeralApp;
133     private final boolean mIsNonIsolatedStorage;
134     private boolean mContainsEphemeralPermission;
135     private boolean mContainsPreRuntimePermission;
136 
137     /**
138      * Does this group contain at least one permission that is split into a foreground and
139      * background permission? This does not necessarily mean that the app also requested the
140      * background permission.
141      */
142     private boolean mHasPermissionWithBackgroundMode;
143 
144     /**
145      * Set if {@link LocationAccessCheck#checkLocationAccessSoon()} should be triggered once the
146      * changes are persisted.
147      */
148     private boolean mTriggerLocationAccessCheckOnPersist;
149 
150     private boolean mIsSelfRevoked;
151 
152     /**
153      * Create the app permission group.
154      *
155      * @param context the {@code Context} to retrieve system services.
156      * @param packageInfo package information about the app.
157      * @param permissionName the name of the permission this object represents.
158      * @param delayChanges whether to delay changes until {@link #persistChanges} is called.
159      *
160      * @return the AppPermissionGroup.
161      */
create(Context context, PackageInfo packageInfo, String permissionName, boolean delayChanges)162     public static AppPermissionGroup create(Context context, PackageInfo packageInfo,
163             String permissionName, boolean delayChanges) {
164         PermissionInfo permissionInfo;
165         try {
166             permissionInfo = context.getPackageManager().getPermissionInfo(permissionName, 0);
167         } catch (PackageManager.NameNotFoundException e) {
168             return null;
169         }
170 
171         if ((permissionInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
172                 != PermissionInfo.PROTECTION_DANGEROUS
173                 || (permissionInfo.flags & PermissionInfo.FLAG_INSTALLED) == 0
174                 || (permissionInfo.flags & PermissionInfo.FLAG_REMOVED) != 0) {
175             return null;
176         }
177 
178         String group = Utils.getGroupOfPermission(permissionInfo);
179         PackageItemInfo groupInfo = permissionInfo;
180         if (group != null) {
181             try {
182                 groupInfo = context.getPackageManager().getPermissionGroupInfo(group, 0);
183             } catch (PackageManager.NameNotFoundException e) {
184                 /* ignore */
185             }
186         }
187 
188         List<PermissionInfo> permissionInfos = null;
189         if (groupInfo instanceof PermissionGroupInfo) {
190             try {
191                 permissionInfos = Utils.getPermissionInfosForGroup(context.getPackageManager(),
192                         groupInfo.name);
193             } catch (PackageManager.NameNotFoundException e) {
194                 /* ignore */
195             }
196         }
197 
198         return create(context, packageInfo, groupInfo, permissionInfos, delayChanges);
199     }
200 
201     /**
202      * Create the app permission group.
203      *
204      * @param app the current application
205      * @param packageName the name of the package
206      * @param permissionGroupName the name of the permission group
207      * @param user the user of the package
208      * @param delayChanges whether to delay changes until {@link #persistChanges} is called.
209      *
210      * @return the AppPermissionGroup.
211      */
create(Application app, String packageName, String permissionGroupName, UserHandle user, boolean delayChanges)212     public static AppPermissionGroup create(Application app, String packageName,
213             String permissionGroupName, UserHandle user, boolean delayChanges) {
214         try {
215             PackageInfo packageInfo = Utils.getUserContext(app, user).getPackageManager()
216                     .getPackageInfo(packageName, PackageManager.GET_PERMISSIONS);
217             PackageItemInfo groupInfo = Utils.getGroupInfo(permissionGroupName, app);
218             if (groupInfo == null) {
219                 return null;
220             }
221 
222             List<PermissionInfo> permissionInfos = null;
223             if (groupInfo instanceof PermissionGroupInfo) {
224                 permissionInfos = Utils.getPermissionInfosForGroup(app.getPackageManager(),
225                             groupInfo.name);
226             }
227             return create(app, packageInfo, groupInfo, permissionInfos, delayChanges);
228         } catch (PackageManager.NameNotFoundException e) {
229             return null;
230         }
231     }
232 
233     /**
234      * Create the app permission group.
235      *
236      * @param context the {@code Context} to retrieve system services.
237      * @param packageInfo package information about the app.
238      * @param groupInfo the information about the group created.
239      * @param permissionInfos the information about the permissions belonging to the group.
240      * @param delayChanges whether to delay changes until {@link #persistChanges} is called.
241      *
242      * @return the AppPermissionGroup.
243      */
create(Context context, PackageInfo packageInfo, PackageItemInfo groupInfo, List<PermissionInfo> permissionInfos, boolean delayChanges)244     public static AppPermissionGroup create(Context context, PackageInfo packageInfo,
245             PackageItemInfo groupInfo, List<PermissionInfo> permissionInfos, boolean delayChanges) {
246         PackageManager packageManager = context.getPackageManager();
247         CharSequence groupLabel = groupInfo.loadLabel(packageManager);
248         CharSequence fullGroupLabel = groupInfo.loadSafeLabel(packageManager, 0,
249                 TextUtils.SAFE_STRING_FLAG_TRIM | TextUtils.SAFE_STRING_FLAG_FIRST_LINE);
250         return create(context, packageInfo, groupInfo, permissionInfos, groupLabel,
251                 fullGroupLabel, delayChanges);
252     }
253 
254     /**
255      * Create the app permission group.
256      *
257      * @param context the {@code Context} to retrieve system services.
258      * @param packageInfo package information about the app.
259      * @param groupInfo the information about the group created.
260      * @param permissionInfos the information about the permissions belonging to the group.
261      * @param groupLabel the label of the group.
262      * @param fullGroupLabel the untruncated label of the group.
263      * @param delayChanges whether to delay changes until {@link #persistChanges} is called.
264      *
265      * @return the AppPermissionGroup.
266      */
create(Context context, PackageInfo packageInfo, PackageItemInfo groupInfo, List<PermissionInfo> permissionInfos, CharSequence groupLabel, CharSequence fullGroupLabel, boolean delayChanges)267     public static AppPermissionGroup create(Context context, PackageInfo packageInfo,
268             PackageItemInfo groupInfo, List<PermissionInfo> permissionInfos,
269             CharSequence groupLabel, CharSequence fullGroupLabel, boolean delayChanges) {
270         PackageManager packageManager = context.getPackageManager();
271         UserHandle userHandle = UserHandle.getUserHandleForUid(packageInfo.applicationInfo.uid);
272 
273         if (groupInfo instanceof PermissionInfo) {
274             permissionInfos = new ArrayList<>();
275             permissionInfos.add((PermissionInfo) groupInfo);
276         }
277 
278         if (permissionInfos == null || permissionInfos.isEmpty()) {
279             return null;
280         }
281 
282         AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
283 
284         AppPermissionGroup group = new AppPermissionGroup(context, packageInfo, groupInfo.name,
285                 groupInfo.packageName, groupLabel, fullGroupLabel,
286                 loadGroupDescription(context, groupInfo, packageManager), getRequest(groupInfo),
287                 getRequestDetail(groupInfo), getBackgroundRequest(groupInfo),
288                 getBackgroundRequestDetail(groupInfo), getUpgradeRequest(groupInfo),
289                 getUpgradeRequestDetail(groupInfo), groupInfo.packageName, groupInfo.icon,
290                 userHandle, delayChanges, appOpsManager);
291 
292         final Set<String> exemptedRestrictedPermissions = context.getPackageManager()
293                 .getWhitelistedRestrictedPermissions(packageInfo.packageName,
294                         Utils.FLAGS_PERMISSION_WHITELIST_ALL);
295 
296         // Parse and create permissions reqested by the app
297         ArrayMap<String, Permission> allPermissions = new ArrayMap<>();
298         final int permissionCount = packageInfo.requestedPermissions == null ? 0
299                 : packageInfo.requestedPermissions.length;
300         String packageName = packageInfo.packageName;
301         for (int i = 0; i < permissionCount; i++) {
302             String requestedPermission = packageInfo.requestedPermissions[i];
303 
304             PermissionInfo requestedPermissionInfo = null;
305 
306             for (PermissionInfo permissionInfo : permissionInfos) {
307                 if (requestedPermission.equals(permissionInfo.name)) {
308                     requestedPermissionInfo = permissionInfo;
309                     break;
310                 }
311             }
312 
313             if (requestedPermissionInfo == null) {
314                 continue;
315             }
316 
317             // Collect only runtime permissions.
318             if ((requestedPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
319                     != PermissionInfo.PROTECTION_DANGEROUS) {
320                 continue;
321             }
322 
323             // Don't allow toggling non-platform permission groups for legacy apps via app ops.
324             if (packageInfo.applicationInfo.targetSdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1
325                     && !PLATFORM_PACKAGE_NAME.equals(groupInfo.packageName)) {
326                 continue;
327             }
328 
329             final boolean granted = (packageInfo.requestedPermissionsFlags[i]
330                     & PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0;
331 
332             final String appOp = PLATFORM_PACKAGE_NAME.equals(requestedPermissionInfo.packageName)
333                     ? AppOpsManager.permissionToOp(requestedPermissionInfo.name) : null;
334 
335             final boolean appOpAllowed;
336             if (appOp == null) {
337                 appOpAllowed = false;
338             } else {
339                 int appOpsMode = appOpsManager.unsafeCheckOpRaw(appOp,
340                         packageInfo.applicationInfo.uid, packageName);
341                 appOpAllowed = appOpsMode == MODE_ALLOWED || appOpsMode == MODE_FOREGROUND;
342             }
343 
344             final int flags = packageManager.getPermissionFlags(
345                     requestedPermission, packageName, userHandle);
346 
347             Permission permission = new Permission(requestedPermission, requestedPermissionInfo,
348                     granted, appOp, appOpAllowed, flags);
349 
350             if (requestedPermissionInfo.backgroundPermission != null) {
351                 group.mHasPermissionWithBackgroundMode = true;
352             }
353 
354             allPermissions.put(requestedPermission, permission);
355         }
356 
357         int numPermissions = allPermissions.size();
358         if (numPermissions == 0) {
359             return null;
360         }
361 
362         // Link up foreground and background permissions
363         for (int i = 0; i < allPermissions.size(); i++) {
364             Permission permission = allPermissions.valueAt(i);
365 
366             if (permission.getBackgroundPermissionName() != null) {
367                 Permission backgroundPermission = allPermissions.get(
368                         permission.getBackgroundPermissionName());
369 
370                 if (backgroundPermission != null) {
371                     backgroundPermission.addForegroundPermissions(permission);
372                     permission.setBackgroundPermission(backgroundPermission);
373 
374                     // The background permissions isAppOpAllowed refers to the background state of
375                     // the foregound permission's appOp. Hence we can only set it once we know the
376                     // matching foreground permission.
377                     // @see #allowAppOp
378                     if (context.getSystemService(AppOpsManager.class).unsafeCheckOpRaw(
379                             permission.getAppOp(), packageInfo.applicationInfo.uid,
380                             packageInfo.packageName) == MODE_ALLOWED) {
381                         backgroundPermission.setAppOpAllowed(true);
382                     }
383                 }
384             }
385         }
386 
387         // Add permissions found to this group
388         for (int i = 0; i < numPermissions; i++) {
389             Permission permission = allPermissions.valueAt(i);
390 
391             if ((!permission.isHardRestricted()
392                     || exemptedRestrictedPermissions.contains(permission.getName()))
393                     && (!permission.isSoftRestricted()
394                     || SoftRestrictedPermissionPolicy.shouldShow(packageInfo, permission))) {
395                 if (permission.isBackgroundPermission()) {
396                     if (group.getBackgroundPermissions() == null) {
397                         group.mBackgroundPermissions = new AppPermissionGroup(group.mContext,
398                                 group.getApp(), group.getName(), group.getDeclaringPackage(),
399                                 group.getLabel(), group.getFullLabel(), group.getDescription(),
400                                 group.getRequest(), group.getRequestDetail(),
401                                 group.getBackgroundRequest(), group.getBackgroundRequestDetail(),
402                                 group.getUpgradeRequest(), group.getUpgradeRequestDetail(),
403                                 group.getIconPkg(), group.getIconResId(), group.getUser(),
404                                 delayChanges, appOpsManager);
405                     }
406 
407                     group.getBackgroundPermissions().addPermission(permission);
408                 } else {
409                     group.addPermission(permission);
410                 }
411             }
412         }
413 
414         if (group.getPermissions().isEmpty()) {
415             return null;
416         }
417 
418         return group;
419     }
420 
getRequest(PackageItemInfo group)421     private static @StringRes int getRequest(PackageItemInfo group) {
422         return Utils.getRequest(group.name);
423     }
424 
loadGroupDescription(Context context, PackageItemInfo group, @NonNull PackageManager packageManager)425     private static CharSequence loadGroupDescription(Context context, PackageItemInfo group,
426                                                      @NonNull PackageManager packageManager) {
427         CharSequence description = null;
428         if (group instanceof PermissionGroupInfo) {
429             description = ((PermissionGroupInfo) group).loadDescription(packageManager);
430         } else if (group instanceof PermissionInfo) {
431             description = ((PermissionInfo) group).loadDescription(packageManager);
432         }
433 
434         if (description == null || description.length() <= 0) {
435             description = context.getString(R.string.default_permission_description);
436         }
437 
438         return description;
439     }
440 
AppPermissionGroup(Context context, PackageInfo packageInfo, String name, String declaringPackage, CharSequence label, CharSequence fullLabel, CharSequence description, @StringRes int request, @StringRes int requestDetail, @StringRes int backgroundRequest, @StringRes int backgroundRequestDetail, @StringRes int upgradeRequest, @StringRes int upgradeRequestDetail, String iconPkg, int iconResId, UserHandle userHandle, boolean delayChanges, @NonNull AppOpsManager appOpsManager)441     private AppPermissionGroup(Context context, PackageInfo packageInfo, String name,
442             String declaringPackage, CharSequence label, CharSequence fullLabel,
443             CharSequence description, @StringRes int request, @StringRes int requestDetail,
444             @StringRes int backgroundRequest, @StringRes int backgroundRequestDetail,
445             @StringRes int upgradeRequest, @StringRes int upgradeRequestDetail,
446             String iconPkg, int iconResId, UserHandle userHandle, boolean delayChanges,
447             @NonNull AppOpsManager appOpsManager) {
448         int targetSDK = packageInfo.applicationInfo.targetSdkVersion;
449 
450         mContext = context;
451         mUserHandle = userHandle;
452         mPackageManager = mContext.getPackageManager();
453         mPackageInfo = packageInfo;
454         mAppSupportsRuntimePermissions = targetSDK > Build.VERSION_CODES.LOLLIPOP_MR1;
455         mIsEphemeralApp = packageInfo.applicationInfo.isInstantApp();
456         mAppOps = appOpsManager;
457         mActivityManager = context.getSystemService(ActivityManager.class);
458         mDeclaringPackage = declaringPackage;
459         mName = name;
460         mLabel = label;
461         mFullLabel = fullLabel;
462         mDescription = description;
463         mCollator = Collator.getInstance(
464                 context.getResources().getConfiguration().getLocales().get(0));
465         mRequest = request;
466         mRequestDetail = requestDetail;
467         mBackgroundRequest = backgroundRequest;
468         mBackgroundRequestDetail = backgroundRequestDetail;
469         mUpgradeRequest = upgradeRequest;
470         mUpgradeRequestDetail = upgradeRequestDetail;
471         mDelayChanges = delayChanges;
472         if (iconResId != 0) {
473             mIconPkg = iconPkg;
474             mIconResId = iconResId;
475         } else {
476             mIconPkg = context.getPackageName();
477             mIconResId = R.drawable.ic_perm_device_info;
478         }
479 
480         mIsNonIsolatedStorage = targetSDK < Build.VERSION_CODES.P
481                 || (targetSDK < Build.VERSION_CODES.R
482                 && mAppOps.unsafeCheckOpNoThrow(OPSTR_LEGACY_STORAGE,
483                         packageInfo.applicationInfo.uid, packageInfo.packageName) == MODE_ALLOWED);
484     }
485 
486     public boolean doesSupportRuntimePermissions() {
487         return mAppSupportsRuntimePermissions;
488     }
489 
490     public boolean isGrantingAllowed() {
491         return (!mIsEphemeralApp || mContainsEphemeralPermission)
492                 && (mAppSupportsRuntimePermissions || mContainsPreRuntimePermission);
493     }
494 
495     public boolean isReviewRequired() {
496         if (mAppSupportsRuntimePermissions) {
497             return false;
498         }
499         final int permissionCount = mPermissions.size();
500         for (int i = 0; i < permissionCount; i++) {
501             Permission permission = mPermissions.valueAt(i);
502             if (permission.isReviewRequired()) {
503                 return true;
504             }
505         }
506         return false;
507     }
508 
509     /**
510      * Are any of the permissions in this group user sensitive.
511      *
512      * @return {@code true} if any of the permissions in the group is user sensitive.
513      */
514     public boolean isUserSensitive() {
515         final int permissionCount = mPermissions.size();
516         for (int i = 0; i < permissionCount; i++) {
517             Permission permission = mPermissions.valueAt(i);
518             if (permission.isUserSensitive()) {
519                 return true;
520             }
521         }
522         return false;
523     }
524 
525     public void unsetReviewRequired() {
526         final int permissionCount = mPermissions.size();
527         for (int i = 0; i < permissionCount; i++) {
528             Permission permission = mPermissions.valueAt(i);
529             if (permission.isReviewRequired()) {
530                 permission.unsetReviewRequired();
531             }
532         }
533 
534         if (!mDelayChanges) {
535             persistChanges(false);
536         }
537     }
538 
539     public boolean hasGrantedByDefaultPermission() {
540         final int permissionCount = mPermissions.size();
541         for (int i = 0; i < permissionCount; i++) {
542             Permission permission = mPermissions.valueAt(i);
543             if (permission.isGrantedByDefault()) {
544                 return true;
545             }
546         }
547         return false;
548     }
549 
550     public PackageInfo getApp() {
551         return mPackageInfo;
552     }
553 
554     public String getName() {
555         return mName;
556     }
557 
558     public String getDeclaringPackage() {
559         return mDeclaringPackage;
560     }
561 
562     public String getIconPkg() {
563         return mIconPkg;
564     }
565 
566     public int getIconResId() {
567         return mIconResId;
568     }
569 
570     public CharSequence getLabel() {
571         return mLabel;
572     }
573 
574     /**
575      * Get the full un-ellipsized label of the permission group.
576      *
577      * @return the full label of the group.
578      */
579     public CharSequence getFullLabel() {
580         return mFullLabel;
581     }
582 
583     /**
584      * @hide
585      * @return The resource Id of the request string.
586      */
587     public @StringRes int getRequest() {
588         return mRequest;
589     }
590 
591     /**
592      * Extract the (subtitle) message explaining to the user that the permission is only granted to
593      * the apps running in the foreground.
594      *
595      * @param info The package item info to extract the message from
596      *
597      * @return the message or 0 if unset
598      */
599     private static @StringRes int getRequestDetail(PackageItemInfo info) {
600         return Utils.getRequestDetail(info.name);
601     }
602 
603     /**
604      * Get the (subtitle) message explaining to the user that the permission is only granted to
605      * the apps running in the foreground.
606      *
607      * @return the message or 0 if unset
608      */
609     public @StringRes int getRequestDetail() {
610         return mRequestDetail;
611     }
612 
613     /**
614      * Extract the title of the dialog explaining to the user that the permission is granted while
615      * the app is in background and in foreground.
616      *
617      * @param info The package item info to extract the message from
618      *
619      * @return the message or 0 if unset
620      */
621     private static @StringRes int getBackgroundRequest(PackageItemInfo info) {
622         return Utils.getBackgroundRequest(info.name);
623     }
624 
625     /**
626      * Get the title of the dialog explaining to the user that the permission is granted while
627      * the app is in background and in foreground.
628      *
629      * @return the message or 0 if unset
630      */
631     public @StringRes int getBackgroundRequest() {
632         return mBackgroundRequest;
633     }
634 
635     /**
636      * Extract the (subtitle) message explaining to the user that the she/he is about to allow the
637      * app to have background access.
638      *
639      * @param info The package item info to extract the message from
640      *
641      * @return the message or 0 if unset
642      */
643     private static @StringRes int getBackgroundRequestDetail(PackageItemInfo info) {
644         return Utils.getBackgroundRequestDetail(info.name);
645     }
646 
647     /**
648      * Get the (subtitle) message explaining to the user that the she/he is about to allow the
649      * app to have background access.
650      *
651      * @return the message or 0 if unset
652      */
653     public @StringRes int getBackgroundRequestDetail() {
654         return mBackgroundRequestDetail;
655     }
656 
657     /**
658      * Extract the title of the dialog explaining to the user that the permission, which was
659      * previously only granted for foreground, is granted while the app is in background and in
660      * foreground.
661      *
662      * @param info The package item info to extract the message from
663      *
664      * @return the message or 0 if unset
665      */
666     private static @StringRes int getUpgradeRequest(PackageItemInfo info) {
667         return Utils.getUpgradeRequest(info.name);
668     }
669 
670     /**
671      * Get the title of the dialog explaining to the user that the permission, which was
672      * previously only granted for foreground, is granted while the app is in background and in
673      * foreground.
674      *
675      * @return the message or 0 if unset
676      */
677     public @StringRes int getUpgradeRequest() {
678         return mUpgradeRequest;
679     }
680 
681     /**
682      * Extract the (subtitle) message explaining to the user that the she/he is about to allow the
683      * app to have background access while currently having foreground only.
684      *
685      * @param info The package item info to extract the message from
686      *
687      * @return the message or 0 if unset
688      */
689     private static @StringRes int getUpgradeRequestDetail(PackageItemInfo info) {
690         return Utils.getUpgradeRequestDetail(info.name);
691     }
692 
693     /**
694      * Get the (subtitle) message explaining to the user that the she/he is about to allow the
695      * app to have background access while currently having foreground only.
696      *
697      * @return the message or 0 if unset
698      */
699     public @StringRes int getUpgradeRequestDetail() {
700         return mUpgradeRequestDetail;
701     }
702 
703     public CharSequence getDescription() {
704         return mDescription;
705     }
706 
707     public UserHandle getUser() {
708         return mUserHandle;
709     }
710 
711     public boolean hasPermission(String permission) {
712         return mPermissions.get(permission) != null;
713     }
714 
715     /**
716      * Return a permission if in this group.
717      *
718      * @param permissionName The name of the permission
719      *
720      * @return The permission
721      */
722     public @Nullable Permission getPermission(@NonNull String permissionName) {
723         return mPermissions.get(permissionName);
724     }
725 
726     public boolean areRuntimePermissionsGranted() {
727         return areRuntimePermissionsGranted(null);
728     }
729 
730     public boolean areRuntimePermissionsGranted(String[] filterPermissions) {
731         return areRuntimePermissionsGranted(filterPermissions, false);
732     }
733 
734     /**
735      * @param filterPermissions the permissions to check for, null for all in this group
736      * @param asOneTime add the requirement that at least one of the granted permissions must have
737      *                 the ONE_TIME flag to return true
738      */
739     public boolean areRuntimePermissionsGranted(String[] filterPermissions, boolean asOneTime) {
740         return areRuntimePermissionsGranted(filterPermissions, asOneTime, true);
741     }
742 
743     /**
744      * Returns true if at least one of the permissions in filterPermissions (or the entire
745      * permission group if null) should be considered granted and satisfy the requirements
746      * described by asOneTime and includingAppOp.
747      *
748      * @param filterPermissions the permissions to check for, null for all in this group
749      * @param asOneTime add the requirement that the granted permission must have the ONE_TIME flag
750      * @param includingAppOp add the requirement that if the granted permissions has a
751      *                       corresponding AppOp, it must be allowed.
752      */
753     public boolean areRuntimePermissionsGranted(String[] filterPermissions, boolean asOneTime,
754             boolean includingAppOp) {
755         if (LocationUtils.isLocationGroupAndProvider(mContext, mName, mPackageInfo.packageName)) {
756             return LocationUtils.isLocationEnabled(mContext) && !asOneTime;
757         }
758         // The permission of the extra location controller package is determined by the status of
759         // the controller package itself.
760         if (LocationUtils.isLocationGroupAndControllerExtraPackage(
761                 mContext, mName, mPackageInfo.packageName)) {
762             return LocationUtils.isExtraLocationControllerPackageEnabled(mContext) && !asOneTime;
763         }
764         final int permissionCount = mPermissions.size();
765         for (int i = 0; i < permissionCount; i++) {
766             Permission permission = mPermissions.valueAt(i);
767             if (filterPermissions != null
768                     && !ArrayUtils.contains(filterPermissions, permission.getName())) {
769                 continue;
770             }
771             boolean isGranted = includingAppOp ? permission.isGrantedIncludingAppOp()
772                     : permission.isGranted();
773             if (isGranted && (!asOneTime || permission.isOneTime())) {
774                 return true;
775             }
776         }
777         if (mBackgroundPermissions != null) {
778             // If asOneTime is true and none of the foreground permissions are one-time, but some
779             // background permissions are, then we still want to return true.
780             return mBackgroundPermissions.areRuntimePermissionsGranted(filterPermissions,
781                     asOneTime, includingAppOp);
782         }
783         return false;
784     }
785 
786     public boolean grantRuntimePermissions(boolean setByTheUser, boolean fixedByTheUser) {
787         return grantRuntimePermissions(setByTheUser, fixedByTheUser, null);
788     }
789 
790     /**
791      * Set mode of an app-op if needed.
792      *
793      * @param op The op to set
794      * @param uid The uid the app-op belongs to
795      * @param mode The new mode
796      *
797      * @return {@code true} iff app-op was changed
798      */
799     private boolean setAppOpMode(@NonNull String op, int uid, int mode) {
800         int currentMode = mAppOps.unsafeCheckOpRaw(op, uid, mPackageInfo.packageName);
801         if (currentMode == mode) {
802             return false;
803         }
804 
805         mAppOps.setUidMode(op, uid, mode);
806         return true;
807     }
808 
809     /**
810      * Allow the app op for a permission/uid.
811      *
812      * <p>There are three cases:
813      * <dl>
814      * <dt>The permission is not split into foreground/background</dt>
815      * <dd>The app op matching the permission will be set to {@link AppOpsManager#MODE_ALLOWED}</dd>
816      * <dt>The permission is a foreground permission:</dt>
817      * <dd><dl><dt>The background permission permission is granted</dt>
818      * <dd>The app op matching the permission will be set to {@link AppOpsManager#MODE_ALLOWED}</dd>
819      * <dt>The background permission permission is <u>not</u> granted</dt>
820      * <dd>The app op matching the permission will be set to
821      * {@link AppOpsManager#MODE_FOREGROUND}</dd>
822      * </dl></dd>
823      * <dt>The permission is a background permission:</dt>
824      * <dd>All granted foreground permissions for this background permission will be set to
825      * {@link AppOpsManager#MODE_ALLOWED}</dd>
826      * </dl>
827      *
828      * @param permission The permission which has an appOps that should be allowed
829      * @param uid        The uid of the process the app op is for
830      *
831      * @return {@code true} iff app-op was changed
832      */
833     private boolean allowAppOp(Permission permission, int uid) {
834         boolean wasChanged = false;
835 
836         if (permission.isBackgroundPermission()) {
837             ArrayList<Permission> foregroundPermissions = permission.getForegroundPermissions();
838 
839             int numForegroundPermissions = foregroundPermissions.size();
840             for (int i = 0; i < numForegroundPermissions; i++) {
841                 Permission foregroundPermission = foregroundPermissions.get(i);
842                 if (foregroundPermission.isAppOpAllowed()) {
843                     wasChanged |= setAppOpMode(foregroundPermission.getAppOp(), uid, MODE_ALLOWED);
844                 }
845             }
846         } else {
847             if (permission.hasBackgroundPermission()) {
848                 Permission backgroundPermission = permission.getBackgroundPermission();
849 
850                 if (backgroundPermission == null) {
851                     // The app requested a permission that has a background permission but it did
852                     // not request the background permission, hence it can never get background
853                     // access
854                     wasChanged = setAppOpMode(permission.getAppOp(), uid, MODE_FOREGROUND);
855                 } else {
856                     if (backgroundPermission.isAppOpAllowed()) {
857                         wasChanged = setAppOpMode(permission.getAppOp(), uid, MODE_ALLOWED);
858                     } else {
859                         wasChanged = setAppOpMode(permission.getAppOp(), uid, MODE_FOREGROUND);
860                     }
861                 }
862             } else {
863                 wasChanged = setAppOpMode(permission.getAppOp(), uid, MODE_ALLOWED);
864             }
865         }
866 
867         return wasChanged;
868     }
869 
870     /**
871      * Kills the app the permissions belong to (and all apps sharing the same uid)
872      *
873      * @param reason The reason why the apps are killed
874      */
875     private void killApp(String reason) {
876         if (shouldSkipKillForGroup()) {
877             return;
878         }
879 
880         mActivityManager.killUid(mPackageInfo.applicationInfo.uid, reason);
881     }
882 
883     private boolean shouldSkipKillForGroup() {
884         if (!mName.equals(Manifest.permission_group.NOTIFICATIONS)) {
885             return false;
886         }
887 
888         return KotlinUtils.INSTANCE.shouldSkipKillOnPermDeny(PermissionControllerApplication.get(),
889                 POST_NOTIFICATIONS, mPackageInfo.packageName, mUserHandle);
890     }
891 
892     /**
893      * Grant permissions of the group.
894      *
895      * <p>This also automatically grants all app ops for permissions that have app ops.
896      * <p>This does <u>only</u> grant permissions in {@link #mPermissions}, i.e. usually not
897      * the background permissions.
898      *
899      * @param setByTheUser If the user has made the decision. This does not unset the flag
900      * @param fixedByTheUser If the user requested that she/he does not want to be asked again
901      * @param filterPermissions If {@code null} all permissions of the group will be granted.
902      *                          Otherwise only permissions in {@code filterPermissions} will be
903      *                          granted.
904      *
905      * @return {@code true} iff all permissions of this group could be granted.
906      */
907     public boolean grantRuntimePermissions(boolean setByTheUser, boolean fixedByTheUser,
908             String[] filterPermissions) {
909         boolean killApp = false;
910         boolean wasAllGranted = true;
911 
912         // We toggle permissions only to apps that support runtime
913         // permissions, otherwise we toggle the app op corresponding
914         // to the permission if the permission is granted to the app.
915         for (Permission permission : mPermissions.values()) {
916             if (filterPermissions != null
917                     && !ArrayUtils.contains(filterPermissions, permission.getName())) {
918                 continue;
919             }
920 
921             if (!permission.isGrantingAllowed(mIsEphemeralApp, mAppSupportsRuntimePermissions)) {
922                 // Skip unallowed permissions.
923                 continue;
924             }
925 
926             boolean wasGranted = permission.isGrantedIncludingAppOp();
927 
928             if (mAppSupportsRuntimePermissions) {
929                 // Do not touch permissions fixed by the system.
930                 if (permission.isSystemFixed()) {
931                     wasAllGranted = false;
932                     break;
933                 }
934 
935                 // Ensure the permission app op is enabled before the permission grant.
936                 if (permission.affectsAppOp() && !permission.isAppOpAllowed()) {
937                     permission.setAppOpAllowed(true);
938                 }
939 
940                 // Grant the permission if needed.
941                 if (!permission.isGranted()) {
942                     permission.setGranted(true);
943                 }
944 
945                 // Update the permission flags.
946                 if (!fixedByTheUser) {
947                     if (permission.isUserFixed()) {
948                         permission.setUserFixed(false);
949                     }
950                     if (setByTheUser) {
951                         if (!permission.isUserSet()) {
952                             permission.setUserSet(true);
953                         }
954                     }
955                 } else {
956                     if (!permission.isUserFixed()) {
957                         permission.setUserFixed(true);
958                     }
959                     if (permission.isUserSet()) {
960                         permission.setUserSet(false);
961                     }
962                 }
963                 if (permission.isReviewRequired()) {
964                     permission.unsetReviewRequired();
965                 }
966             } else {
967                 // Legacy apps cannot have a not granted permission but just in case.
968                 if (!permission.isGranted()) {
969                     continue;
970                 }
971 
972                 // If the permissions has no corresponding app op, then it is a
973                 // third-party one and we do not offer toggling of such permissions.
974                 if (permission.affectsAppOp()) {
975                     if (!permission.isAppOpAllowed()) {
976                         permission.setAppOpAllowed(true);
977 
978                         // Legacy apps do not know that they have to retry access to a
979                         // resource due to changes in runtime permissions (app ops in this
980                         // case). Therefore, we restart them on app op change, so they
981                         // can pick up the change.
982                         killApp = true;
983                     }
984 
985                     // Mark that the permission is not kept granted only for compatibility.
986                     if (permission.isRevokedCompat()) {
987                         permission.setRevokedCompat(false);
988                     }
989                 }
990 
991                 // Granting a permission explicitly means the user already
992                 // reviewed it so clear the review flag on every grant.
993                 if (permission.isReviewRequired()) {
994                     permission.unsetReviewRequired();
995                 }
996             }
997 
998             // If we newly grant background access to the fine location, double-guess the user some
999             // time later if this was really the right choice.
1000             if (!wasGranted && permission.isGrantedIncludingAppOp()) {
1001                 if (permission.getName().equals(ACCESS_FINE_LOCATION)) {
1002                     Permission bgPerm = permission.getBackgroundPermission();
1003                     if (bgPerm != null) {
1004                         if (bgPerm.isGrantedIncludingAppOp()) {
1005                             mTriggerLocationAccessCheckOnPersist = true;
1006                         }
1007                     }
1008                 } else if (permission.getName().equals(ACCESS_BACKGROUND_LOCATION)) {
1009                     ArrayList<Permission> fgPerms = permission.getForegroundPermissions();
1010                     if (fgPerms != null) {
1011                         int numFgPerms = fgPerms.size();
1012                         for (int fgPermNum = 0; fgPermNum < numFgPerms; fgPermNum++) {
1013                             Permission fgPerm = fgPerms.get(fgPermNum);
1014 
1015                             if (fgPerm.getName().equals(ACCESS_FINE_LOCATION)) {
1016                                 if (fgPerm.isGrantedIncludingAppOp()) {
1017                                     mTriggerLocationAccessCheckOnPersist = true;
1018                                 }
1019 
1020                                 break;
1021                             }
1022                         }
1023                     }
1024                 }
1025             }
1026         }
1027 
1028         if (!mDelayChanges) {
1029             persistChanges(false);
1030 
1031             if (killApp) {
1032                 killApp(KILL_REASON_APP_OP_CHANGE);
1033             }
1034         }
1035 
1036         return wasAllGranted;
1037     }
1038 
1039     public boolean revokeRuntimePermissions(boolean fixedByTheUser) {
1040         return revokeRuntimePermissions(fixedByTheUser, null);
1041     }
1042 
1043     /**
1044      * Disallow the app op for a permission/uid.
1045      *
1046      * <p>There are three cases:
1047      * <dl>
1048      * <dt>The permission is not split into foreground/background</dt>
1049      * <dd>The app op matching the permission will be set to {@link AppOpsManager#MODE_IGNORED}</dd>
1050      * <dt>The permission is a foreground permission:</dt>
1051      * <dd>The app op matching the permission will be set to {@link AppOpsManager#MODE_IGNORED}</dd>
1052      * <dt>The permission is a background permission:</dt>
1053      * <dd>All granted foreground permissions for this background permission will be set to
1054      * {@link AppOpsManager#MODE_FOREGROUND}</dd>
1055      * </dl>
1056      *
1057      * @param permission The permission which has an appOps that should be disallowed
1058      * @param uid        The uid of the process the app op if for
1059      *
1060      * @return {@code true} iff app-op was changed
1061      */
1062     private boolean disallowAppOp(Permission permission, int uid) {
1063         boolean wasChanged = false;
1064 
1065         if (permission.isBackgroundPermission()) {
1066             ArrayList<Permission> foregroundPermissions = permission.getForegroundPermissions();
1067 
1068             int numForegroundPermissions = foregroundPermissions.size();
1069             for (int i = 0; i < numForegroundPermissions; i++) {
1070                 Permission foregroundPermission = foregroundPermissions.get(i);
1071                 if (foregroundPermission.isAppOpAllowed()) {
1072                     wasChanged |= setAppOpMode(foregroundPermission.getAppOp(), uid,
1073                             MODE_FOREGROUND);
1074                 }
1075             }
1076         } else {
1077             wasChanged = setAppOpMode(permission.getAppOp(), uid, MODE_IGNORED);
1078         }
1079 
1080         return wasChanged;
1081     }
1082 
1083     /**
1084      * Revoke permissions of the group.
1085      *
1086      * <p>This also disallows all app ops for permissions that have app ops.
1087      * <p>This does <u>only</u> revoke permissions in {@link #mPermissions}, i.e. usually not
1088      * the background permissions.
1089      *
1090      * @param fixedByTheUser If the user requested that she/he does not want to be asked again
1091      * @param filterPermissions If {@code null} all permissions of the group will be revoked.
1092      *                          Otherwise only permissions in {@code filterPermissions} will be
1093      *                          revoked.
1094      *
1095      * @return {@code true} iff all permissions of this group could be revoked.
1096      */
1097     public boolean revokeRuntimePermissions(boolean fixedByTheUser, String[] filterPermissions) {
1098         boolean killApp = false;
1099         boolean wasAllRevoked = true;
1100 
1101         // We toggle permissions only to apps that support runtime
1102         // permissions, otherwise we toggle the app op corresponding
1103         // to the permission if the permission is granted to the app.
1104         for (Permission permission : mPermissions.values()) {
1105             if (filterPermissions != null
1106                     && !ArrayUtils.contains(filterPermissions, permission.getName())) {
1107                 continue;
1108             }
1109 
1110             // Do not touch permissions fixed by the system.
1111             if (permission.isSystemFixed()) {
1112                 wasAllRevoked = false;
1113                 break;
1114             }
1115 
1116             if (mAppSupportsRuntimePermissions) {
1117                 // Revoke the permission if needed.
1118                 if (permission.isGranted()) {
1119                     permission.setGranted(false);
1120                 }
1121 
1122                 // Update the permission flags.
1123                 if (fixedByTheUser) {
1124                     // Take a note that the user fixed the permission.
1125                     if (permission.isUserSet() || !permission.isUserFixed()) {
1126                         permission.setUserSet(false);
1127                         permission.setUserFixed(true);
1128                     }
1129                 } else {
1130                     if (!permission.isUserSet() || permission.isUserFixed()) {
1131                         permission.setUserSet(true);
1132                         permission.setUserFixed(false);
1133                     }
1134                 }
1135 
1136                 if (permission.affectsAppOp()) {
1137                     permission.setAppOpAllowed(false);
1138                 }
1139             } else {
1140                 // Legacy apps cannot have a non-granted permission but just in case.
1141                 if (!permission.isGranted()) {
1142                     continue;
1143                 }
1144 
1145                 // If the permission has no corresponding app op, then it is a
1146                 // third-party one and we do not offer toggling of such permissions.
1147                 if (permission.affectsAppOp()) {
1148                     if (permission.isAppOpAllowed()) {
1149                         permission.setAppOpAllowed(false);
1150 
1151                         // Disabling an app op may put the app in a situation in which it
1152                         // has a handle to state it shouldn't have, so we have to kill the
1153                         // app. This matches the revoke runtime permission behavior.
1154                         killApp = true;
1155                     }
1156 
1157                     // Mark that the permission is kept granted only for compatibility.
1158                     if (!permission.isRevokedCompat()) {
1159                         permission.setRevokedCompat(true);
1160                     }
1161                 }
1162             }
1163         }
1164 
1165         if (!mDelayChanges) {
1166             persistChanges(false);
1167 
1168             if (killApp) {
1169                 killApp(KILL_REASON_APP_OP_CHANGE);
1170             }
1171         }
1172 
1173         return wasAllRevoked;
1174     }
1175 
1176     /**
1177      * Mark permissions in this group as policy fixed.
1178      *
1179      * @param filterPermissions The permissions to mark
1180      */
1181     public void setPolicyFixed(@NonNull String[] filterPermissions) {
1182         for (String permissionName : filterPermissions) {
1183             Permission permission = mPermissions.get(permissionName);
1184 
1185             if (permission != null) {
1186                 permission.setPolicyFixed(true);
1187             }
1188         }
1189 
1190         if (!mDelayChanges) {
1191             persistChanges(false);
1192         }
1193     }
1194 
1195     /**
1196      * Set the user-fixed flag for all permissions in this group.
1197      *
1198      * @param isUsedFixed if the flag should be set or not
1199      */
1200     public void setUserFixed(boolean isUsedFixed) {
1201         final int permissionCount = mPermissions.size();
1202         for (int i = 0; i < permissionCount; i++) {
1203             Permission permission = mPermissions.valueAt(i);
1204             permission.setUserFixed(isUsedFixed);
1205         }
1206 
1207         if (!mDelayChanges) {
1208             persistChanges(false);
1209         }
1210     }
1211 
1212     /**
1213      * Mark this group as having been self-revoked.
1214      */
1215     public void setSelfRevoked() {
1216         mIsSelfRevoked = true;
1217     }
1218 
1219     /**
1220      * Set the one-time flag for all permissions in this group.
1221      *
1222      * @param isOneTime if the flag should be set or not
1223      */
1224     public void setOneTime(boolean isOneTime) {
1225         final int permissionCount = mPermissions.size();
1226         for (int i = 0; i < permissionCount; i++) {
1227             Permission permission = mPermissions.valueAt(i);
1228             permission.setOneTime(isOneTime);
1229         }
1230 
1231         if (!mDelayChanges) {
1232             persistChanges(false);
1233         }
1234     }
1235 
1236     /**
1237      * Set the user-set flag for all permissions in this group.
1238      *
1239      * @param isUserSet if the flag should be set or not
1240      */
1241     public void setUserSet(boolean isUserSet) {
1242         final int permissionCount = mPermissions.size();
1243         for (int i = 0; i < permissionCount; i++) {
1244             Permission permission = mPermissions.valueAt(i);
1245             permission.setUserSet(isUserSet);
1246         }
1247 
1248         if (!mDelayChanges) {
1249             persistChanges(false);
1250         }
1251     }
1252 
1253     public ArrayList<Permission> getPermissions() {
1254         return new ArrayList<>(mPermissions.values());
1255     }
1256 
1257     /**
1258      * @return An {@link AppPermissionGroup}-object that contains all background permissions for
1259      * this group.
1260      */
1261     public AppPermissionGroup getBackgroundPermissions() {
1262         return mBackgroundPermissions;
1263     }
1264 
1265     /**
1266      * @return {@code true} iff the app request at least one permission in this group that has a
1267      * background permission. It is possible that the app does not request the matching background
1268      * permission and hence will only ever get foreground access, never background access.
1269      */
1270     public boolean hasPermissionWithBackgroundMode() {
1271         return mHasPermissionWithBackgroundMode;
1272     }
1273 
1274     /**
1275      * Is the group a storage permission group that is referring to an app that does not have
1276      * isolated storage
1277      *
1278      * @return {@code true} iff this is a storage group on an app that does not have isolated
1279      * storage
1280      */
1281     public boolean isNonIsolatedStorage() {
1282         return mIsNonIsolatedStorage;
1283     }
1284 
1285     /**
1286      * Whether this is group that contains all the background permission for regular permission
1287      * group.
1288      *
1289      * @return {@code true} iff this is a background permission group.
1290      *
1291      * @see #getBackgroundPermissions()
1292      */
1293     public boolean isBackgroundGroup() {
1294         return mPermissions.valueAt(0).isBackgroundPermission();
1295     }
1296 
1297     /**
1298      * Whether this group supports one-time permissions
1299      * @return {@code true} iff this group supports one-time permissions
1300      */
1301     public boolean supportsOneTimeGrant() {
1302         return Utils.supportsOneTimeGrant(getName());
1303     }
1304 
1305     public int getFlags() {
1306         int flags = 0;
1307         final int permissionCount = mPermissions.size();
1308         for (int i = 0; i < permissionCount; i++) {
1309             Permission permission = mPermissions.valueAt(i);
1310             flags |= permission.getFlags();
1311         }
1312         return flags;
1313     }
1314 
1315     public boolean isUserFixed() {
1316         final int permissionCount = mPermissions.size();
1317         for (int i = 0; i < permissionCount; i++) {
1318             Permission permission = mPermissions.valueAt(i);
1319             if (permission.isUserFixed()) {
1320                 return true;
1321             }
1322         }
1323         return false;
1324     }
1325 
1326     public boolean isPolicyFixed() {
1327         final int permissionCount = mPermissions.size();
1328         for (int i = 0; i < permissionCount; i++) {
1329             Permission permission = mPermissions.valueAt(i);
1330             if (permission.isPolicyFixed()) {
1331                 return true;
1332             }
1333         }
1334         return false;
1335     }
1336 
1337     public boolean isUserSet() {
1338         final int permissionCount = mPermissions.size();
1339         for (int i = 0; i < permissionCount; i++) {
1340             Permission permission = mPermissions.valueAt(i);
1341             if (permission.isUserSet()) {
1342                 return true;
1343             }
1344         }
1345         return false;
1346     }
1347 
1348     public boolean isSystemFixed() {
1349         final int permissionCount = mPermissions.size();
1350         for (int i = 0; i < permissionCount; i++) {
1351             Permission permission = mPermissions.valueAt(i);
1352             if (permission.isSystemFixed()) {
1353                 return true;
1354             }
1355         }
1356         return false;
1357     }
1358 
1359     /**
1360      * @return Whether any of the permissions in this group is one-time
1361      */
1362     public boolean isOneTime() {
1363         final int permissionCount = mPermissions.size();
1364         for (int i = 0; i < permissionCount; i++) {
1365             Permission permission = mPermissions.valueAt(i);
1366             if (permission.isOneTime()) {
1367                 return true;
1368             }
1369         }
1370         return false;
1371     }
1372 
1373     /**
1374      * @return Whether at least one permission is granted and every granted permission is one-time
1375      */
1376     public boolean isStrictlyOneTime() {
1377         boolean oneTimePermissionFound = false;
1378         final int permissionCount = mPermissions.size();
1379         for (int i = 0; i < permissionCount; i++) {
1380             Permission permission = mPermissions.valueAt(i);
1381             if (permission.isGranted()) {
1382                 if (!permission.isOneTime()) {
1383                     return false;
1384                 }
1385                 oneTimePermissionFound = true;
1386             }
1387         }
1388         return oneTimePermissionFound;
1389     }
1390 
1391     @Override
1392     public int compareTo(AppPermissionGroup another) {
1393         final int result = mCollator.compare(mLabel.toString(), another.mLabel.toString());
1394         if (result == 0) {
1395             // Unbadged before badged.
1396             return mPackageInfo.applicationInfo.uid
1397                     - another.mPackageInfo.applicationInfo.uid;
1398         }
1399         return result;
1400     }
1401 
1402     @Override
1403     public boolean equals(Object o) {
1404         if (!(o instanceof AppPermissionGroup)) {
1405             return false;
1406         }
1407 
1408         AppPermissionGroup other = (AppPermissionGroup) o;
1409 
1410         boolean equal = mName.equals(other.mName)
1411                 && mPackageInfo.packageName.equals(other.mPackageInfo.packageName)
1412                 && mUserHandle.equals(other.mUserHandle)
1413                 && mPermissions.equals(other.mPermissions);
1414         if (!equal) {
1415             return false;
1416         }
1417 
1418         if (mBackgroundPermissions != null && other.getBackgroundPermissions() != null) {
1419             return mBackgroundPermissions.getPermissions().equals(
1420                     other.getBackgroundPermissions().getPermissions());
1421         }
1422         return mBackgroundPermissions == other.getBackgroundPermissions();
1423     }
1424 
1425     @Override
1426     public int hashCode() {
1427         ArrayList<Permission> backgroundPermissions = new ArrayList<>();
1428         if (mBackgroundPermissions != null) {
1429             backgroundPermissions = mBackgroundPermissions.getPermissions();
1430         }
1431         return Objects.hash(mName, mPackageInfo.packageName, mUserHandle, mPermissions,
1432                 backgroundPermissions);
1433     }
1434 
1435     @Override
1436     public String toString() {
1437         StringBuilder builder = new StringBuilder();
1438         builder.append(getClass().getSimpleName());
1439         builder.append("{name=").append(mName);
1440         if (mBackgroundPermissions != null) {
1441             builder.append(", <has background permissions>}");
1442         }
1443         if (!mPermissions.isEmpty()) {
1444             builder.append(", <has permissions>}");
1445         } else {
1446             builder.append('}');
1447         }
1448         return builder.toString();
1449     }
1450 
1451     private void addPermission(Permission permission) {
1452         mPermissions.put(permission.getName(), permission);
1453         if (permission.isEphemeral()) {
1454             mContainsEphemeralPermission = true;
1455         }
1456         if (!permission.isRuntimeOnly()) {
1457             mContainsPreRuntimePermission = true;
1458         }
1459     }
1460 
1461     /**
1462      * If the changes to this group were delayed, persist them to the platform.
1463      *
1464      * @param mayKillBecauseOfAppOpsChange If the app these permissions belong to may be killed if
1465      *                                     app ops change. If this is set to {@code false} the
1466      *                                     caller has to make sure to kill the app if needed.
1467      */
1468     public void persistChanges(boolean mayKillBecauseOfAppOpsChange) {
1469         persistChanges(mayKillBecauseOfAppOpsChange, null, null);
1470     }
1471 
1472     /**
1473      * If the changes to this group were delayed, persist them to the platform.
1474      *
1475      * @param mayKillBecauseOfAppOpsChange If the app these permissions belong to may be killed if
1476      *                                     app ops change. If this is set to {@code false} the
1477      *                                     caller has to make sure to kill the app if needed.
1478      * @param revokeReason If any permissions are getting revoked, the reason for revoking them.
1479      */
1480     public void persistChanges(boolean mayKillBecauseOfAppOpsChange, String revokeReason) {
1481         persistChanges(mayKillBecauseOfAppOpsChange, revokeReason, null);
1482     }
1483 
1484     /**
1485      * If the changes to this group were delayed, persist them to the platform.
1486      *
1487      * @param mayKillBecauseOfAppOpsChange If the app these permissions belong to may be killed if
1488      *                                     app ops change. If this is set to {@code false} the
1489      *                                     caller has to make sure to kill the app if needed.
1490      * @param revokeReason If any permissions are getting revoked, the reason for revoking them.
1491      * @param filterPermissions If provided, only persist state for the given permissions
1492      */
1493     public void persistChanges(boolean mayKillBecauseOfAppOpsChange, String revokeReason,
1494             Set<String> filterPermissions) {
1495         int uid = mPackageInfo.applicationInfo.uid;
1496 
1497         int numPermissions = mPermissions.size();
1498         boolean shouldKillApp = false;
1499 
1500         for (int i = 0; i < numPermissions; i++) {
1501             Permission permission = mPermissions.valueAt(i);
1502 
1503             if (filterPermissions != null && !filterPermissions.contains(permission.getName())) {
1504                 continue;
1505             }
1506 
1507             if (!permission.isSystemFixed()) {
1508                 if (permission.isGranted()) {
1509                     mPackageManager.grantRuntimePermission(mPackageInfo.packageName,
1510                             permission.getName(), mUserHandle);
1511                 } else {
1512                     boolean isCurrentlyGranted = mContext.checkPermission(permission.getName(), -1,
1513                             uid) == PERMISSION_GRANTED;
1514 
1515                     if (isCurrentlyGranted) {
1516                         if (revokeReason == null) {
1517                             mPackageManager.revokeRuntimePermission(mPackageInfo.packageName,
1518                                     permission.getName(), mUserHandle);
1519                         } else {
1520                             mPackageManager.revokeRuntimePermission(mPackageInfo.packageName,
1521                                     permission.getName(), mUserHandle, revokeReason);
1522                         }
1523                     }
1524                 }
1525             }
1526 
1527             int flags = (permission.isUserSet() ? PackageManager.FLAG_PERMISSION_USER_SET : 0)
1528                     | (permission.isUserFixed() ? PackageManager.FLAG_PERMISSION_USER_FIXED : 0)
1529                     | (permission.isRevokedCompat()
1530                     ? PackageManager.FLAG_PERMISSION_REVOKED_COMPAT : 0)
1531                     | (permission.isPolicyFixed() ? PackageManager.FLAG_PERMISSION_POLICY_FIXED : 0)
1532                     | (permission.isReviewRequired()
1533                     ? PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED : 0)
1534                     | (permission.isOneTime() ? PackageManager.FLAG_PERMISSION_ONE_TIME : 0)
1535                     | (permission.isSelectedLocationAccuracy()
1536                     ? PackageManager.FLAG_PERMISSION_SELECTED_LOCATION_ACCURACY : 0);
1537 
1538             mPackageManager.updatePermissionFlags(permission.getName(),
1539                     mPackageInfo.packageName,
1540                     PackageManager.FLAG_PERMISSION_USER_SET
1541                             | PackageManager.FLAG_PERMISSION_USER_FIXED
1542                             | PackageManager.FLAG_PERMISSION_REVOKED_COMPAT
1543                             | PackageManager.FLAG_PERMISSION_POLICY_FIXED
1544                             | (permission.isReviewRequired()
1545                             ? 0 : PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED)
1546                             | PackageManager.FLAG_PERMISSION_ONE_TIME
1547                             | PackageManager.FLAG_PERMISSION_AUTO_REVOKED // clear auto revoke
1548                             | PackageManager.FLAG_PERMISSION_SELECTED_LOCATION_ACCURACY,
1549                     flags, mUserHandle);
1550 
1551             if (permission.affectsAppOp()) {
1552                 if (!permission.isSystemFixed()) {
1553                     // Enabling/Disabling an app op may put the app in a situation in which it has
1554                     // a handle to state it shouldn't have, so we have to kill the app. This matches
1555                     // the revoke runtime permission behavior.
1556                     if (permission.isAppOpAllowed()) {
1557                         boolean wasChanged = allowAppOp(permission, uid);
1558                         shouldKillApp |= wasChanged && !mAppSupportsRuntimePermissions;
1559                     } else {
1560                         shouldKillApp |= disallowAppOp(permission, uid);
1561                     }
1562                 }
1563             }
1564         }
1565 
1566         if (mayKillBecauseOfAppOpsChange && shouldKillApp) {
1567             killApp(KILL_REASON_APP_OP_CHANGE);
1568         }
1569 
1570         if (mTriggerLocationAccessCheckOnPersist) {
1571             new LocationAccessCheck(mContext, null).checkLocationAccessSoon();
1572             mTriggerLocationAccessCheckOnPersist = false;
1573         }
1574 
1575         String packageName = mPackageInfo.packageName;
1576         if (areRuntimePermissionsGranted(null, true, false)) {
1577             // Required to read device config in Utils.getOneTimePermissions*().
1578             final long token = Binder.clearCallingIdentity();
1579             try {
1580                 if (SdkLevel.isAtLeastT()) {
1581                     mContext.getSystemService(PermissionManager.class)
1582                             .startOneTimePermissionSession(packageName,
1583                                     Utils.getOneTimePermissionsTimeout(),
1584                                     Utils.getOneTimePermissionsKilledDelay(mIsSelfRevoked),
1585                                     ONE_TIME_PACKAGE_IMPORTANCE_LEVEL_TO_RESET_TIMER,
1586                                     ONE_TIME_PACKAGE_IMPORTANCE_LEVEL_TO_KEEP_SESSION_ALIVE);
1587                 } else {
1588                     mContext.getSystemService(PermissionManager.class)
1589                             .startOneTimePermissionSession(packageName,
1590                                     Utils.getOneTimePermissionsTimeout(),
1591                                     ONE_TIME_PACKAGE_IMPORTANCE_LEVEL_TO_RESET_TIMER,
1592                                     ONE_TIME_PACKAGE_IMPORTANCE_LEVEL_TO_KEEP_SESSION_ALIVE);
1593                 }
1594             } finally {
1595                 Binder.restoreCallingIdentity(token);
1596             }
1597         } else {
1598             mContext.getSystemService(PermissionManager.class)
1599                     .stopOneTimePermissionSession(packageName);
1600         }
1601     }
1602 
1603     /**
1604      * Check if permission group contains a runtime permission that split from an installed
1605      * permission and the split happened in an Android version higher than app's targetSdk.
1606      *
1607      * @return {@code true} if there is such permission, {@code false} otherwise
1608      */
1609     public boolean hasInstallToRuntimeSplit() {
1610         PermissionManager permissionManager =
1611                 (PermissionManager) mContext.getSystemService(PermissionManager.class);
1612 
1613         int numSplitPerms = permissionManager.getSplitPermissions().size();
1614         for (int splitPermNum = 0; splitPermNum < numSplitPerms; splitPermNum++) {
1615             PermissionManager.SplitPermissionInfo spi =
1616                     permissionManager.getSplitPermissions().get(splitPermNum);
1617             String splitPerm = spi.getSplitPermission();
1618 
1619             PermissionInfo pi;
1620             try {
1621                 pi = mPackageManager.getPermissionInfo(splitPerm, 0);
1622             } catch (NameNotFoundException e) {
1623                 Log.w(LOG_TAG, "No such permission: " + splitPerm, e);
1624                 continue;
1625             }
1626 
1627             // Skip if split permission is not "install" permission.
1628             if (pi.getProtection() != pi.PROTECTION_NORMAL) {
1629                 continue;
1630             }
1631 
1632             List<String> newPerms = spi.getNewPermissions();
1633             int numNewPerms = newPerms.size();
1634             for (int newPermNum = 0; newPermNum < numNewPerms; newPermNum++) {
1635                 String newPerm = newPerms.get(newPermNum);
1636 
1637                 if (!hasPermission(newPerm)) {
1638                     continue;
1639                 }
1640 
1641                 try {
1642                     pi = mPackageManager.getPermissionInfo(newPerm, 0);
1643                 } catch (NameNotFoundException e) {
1644                     Log.w(LOG_TAG, "No such permission: " + newPerm, e);
1645                     continue;
1646                 }
1647 
1648                 // Skip if new permission is not "runtime" permission.
1649                 if (pi.getProtection() != pi.PROTECTION_DANGEROUS) {
1650                     continue;
1651                 }
1652 
1653                 if (mPackageInfo.applicationInfo.targetSdkVersion < spi.getTargetSdk()) {
1654                     return true;
1655                 }
1656             }
1657         }
1658         return false;
1659     }
1660 }
1661