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