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