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.utils;
18 
19 import static android.Manifest.permission.BIND_SOUND_TRIGGER_DETECTION_SERVICE;
20 import static android.Manifest.permission.CAPTURE_AUDIO_HOTWORD;
21 import static android.Manifest.permission_group.ACTIVITY_RECOGNITION;
22 import static android.Manifest.permission_group.CALENDAR;
23 import static android.Manifest.permission_group.CALL_LOG;
24 import static android.Manifest.permission_group.CAMERA;
25 import static android.Manifest.permission_group.CONTACTS;
26 import static android.Manifest.permission_group.LOCATION;
27 import static android.Manifest.permission_group.MICROPHONE;
28 import static android.Manifest.permission_group.PHONE;
29 import static android.Manifest.permission_group.SENSORS;
30 import static android.Manifest.permission_group.SMS;
31 import static android.Manifest.permission_group.STORAGE;
32 import static android.content.Context.MODE_PRIVATE;
33 import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
34 import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
35 import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
36 import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED;
37 import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED;
38 import static android.content.pm.PackageManager.GET_SERVICES;
39 import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
40 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
41 import static android.os.UserHandle.myUserId;
42 
43 import static com.android.permissioncontroller.Constants.INVALID_SESSION_ID;
44 
45 import android.Manifest;
46 import android.app.Application;
47 import android.app.role.RoleManager;
48 import android.content.ActivityNotFoundException;
49 import android.content.ComponentName;
50 import android.content.Context;
51 import android.content.Intent;
52 import android.content.SharedPreferences;
53 import android.content.pm.ApplicationInfo;
54 import android.content.pm.PackageItemInfo;
55 import android.content.pm.PackageManager;
56 import android.content.pm.PackageManager.NameNotFoundException;
57 import android.content.pm.PermissionInfo;
58 import android.content.pm.ResolveInfo;
59 import android.content.pm.ServiceInfo;
60 import android.content.res.Resources;
61 import android.content.res.Resources.Theme;
62 import android.graphics.Bitmap;
63 import android.graphics.drawable.BitmapDrawable;
64 import android.graphics.drawable.Drawable;
65 import android.os.Parcelable;
66 import android.os.Process;
67 import android.os.UserHandle;
68 import android.os.UserManager;
69 import android.provider.DeviceConfig;
70 import android.provider.Settings;
71 import android.service.carrier.CarrierService;
72 import android.telephony.TelephonyManager;
73 import android.text.Html;
74 import android.text.TextUtils;
75 import android.text.format.DateFormat;
76 import android.util.ArrayMap;
77 import android.util.ArraySet;
78 import android.util.Log;
79 import android.util.TypedValue;
80 import android.view.Menu;
81 import android.view.MenuItem;
82 
83 import androidx.annotation.NonNull;
84 import androidx.annotation.Nullable;
85 import androidx.annotation.StringRes;
86 import androidx.core.text.BidiFormatter;
87 import androidx.core.util.Preconditions;
88 
89 import com.android.launcher3.icons.IconFactory;
90 import com.android.permissioncontroller.Constants;
91 import com.android.permissioncontroller.DeviceUtils;
92 import com.android.permissioncontroller.PermissionControllerApplication;
93 import com.android.permissioncontroller.R;
94 import com.android.permissioncontroller.permission.model.AppPermissionGroup;
95 
96 import java.util.ArrayList;
97 import java.util.Calendar;
98 import java.util.Collections;
99 import java.util.List;
100 import java.util.Locale;
101 import java.util.Random;
102 
103 public final class Utils {
104 
105     private static final String LOG_TAG = "Utils";
106 
107     public static final String OS_PKG = "android";
108 
109     public static final float DEFAULT_MAX_LABEL_SIZE_PX = 500f;
110 
111     /** Whether to show the Permissions Hub. */
112     private static final String PROPERTY_PERMISSIONS_HUB_ENABLED = "permissions_hub_enabled";
113 
114     /** The timeout for one-time permissions */
115     private static final String PROPERTY_ONE_TIME_PERMISSIONS_TIMEOUT_MILLIS =
116             "one_time_permissions_timeout_millis";
117 
118     /** The timeout for auto-revoke permissions */
119     public static final String PROPERTY_AUTO_REVOKE_UNUSED_THRESHOLD_MILLIS =
120             "auto_revoke_unused_threshold_millis2";
121 
122     /** The frequency of running the job for auto-revoke permissions */
123     public static final String PROPERTY_AUTO_REVOKE_CHECK_FREQUENCY_MILLIS =
124             "auto_revoke_check_frequency_millis";
125 
126     /** Whether to show location access check notifications. */
127     private static final String PROPERTY_LOCATION_ACCESS_CHECK_ENABLED =
128             "location_access_check_enabled";
129 
130     /** All permission whitelists. */
131     public static final int FLAGS_PERMISSION_WHITELIST_ALL =
132             PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM
133                     | PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE
134                     | PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER;
135 
136     /** All permission restriction excemptions. */
137     public static final int FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT =
138             FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT
139                     | FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT
140                     | FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
141 
142     /**
143      * The default length of the timeout for one-time permissions
144      */
145     public static final long ONE_TIME_PERMISSIONS_TIMEOUT_MILLIS = 1 * 60 * 1000; // 1 minute
146 
147     /** Mapping permission -> group for all dangerous platform permissions */
148     private static final ArrayMap<String, String> PLATFORM_PERMISSIONS;
149 
150     /** Mapping group -> permissions for all dangerous platform permissions */
151     private static final ArrayMap<String, ArrayList<String>> PLATFORM_PERMISSION_GROUPS;
152 
153     /** Set of groups that will be able to receive one-time grant */
154     private static final ArraySet<String> ONE_TIME_PERMISSION_GROUPS;
155 
156     private static final ArrayMap<String, Integer> PERM_GROUP_REQUEST_RES;
157     private static final ArrayMap<String, Integer> PERM_GROUP_REQUEST_DETAIL_RES;
158     private static final ArrayMap<String, Integer> PERM_GROUP_BACKGROUND_REQUEST_RES;
159     private static final ArrayMap<String, Integer> PERM_GROUP_BACKGROUND_REQUEST_DETAIL_RES;
160     private static final ArrayMap<String, Integer> PERM_GROUP_UPGRADE_REQUEST_RES;
161     private static final ArrayMap<String, Integer> PERM_GROUP_UPGRADE_REQUEST_DETAIL_RES;
162 
163     public static final int FLAGS_ALWAYS_USER_SENSITIVE =
164             FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED
165                     | FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED;
166 
167     static {
168         PLATFORM_PERMISSIONS = new ArrayMap<>();
169 
PLATFORM_PERMISSIONS.put(Manifest.permission.READ_CONTACTS, CONTACTS)170         PLATFORM_PERMISSIONS.put(Manifest.permission.READ_CONTACTS, CONTACTS);
PLATFORM_PERMISSIONS.put(Manifest.permission.WRITE_CONTACTS, CONTACTS)171         PLATFORM_PERMISSIONS.put(Manifest.permission.WRITE_CONTACTS, CONTACTS);
PLATFORM_PERMISSIONS.put(Manifest.permission.GET_ACCOUNTS, CONTACTS)172         PLATFORM_PERMISSIONS.put(Manifest.permission.GET_ACCOUNTS, CONTACTS);
173 
PLATFORM_PERMISSIONS.put(Manifest.permission.READ_CALENDAR, CALENDAR)174         PLATFORM_PERMISSIONS.put(Manifest.permission.READ_CALENDAR, CALENDAR);
PLATFORM_PERMISSIONS.put(Manifest.permission.WRITE_CALENDAR, CALENDAR)175         PLATFORM_PERMISSIONS.put(Manifest.permission.WRITE_CALENDAR, CALENDAR);
176 
PLATFORM_PERMISSIONS.put(Manifest.permission.SEND_SMS, SMS)177         PLATFORM_PERMISSIONS.put(Manifest.permission.SEND_SMS, SMS);
PLATFORM_PERMISSIONS.put(Manifest.permission.RECEIVE_SMS, SMS)178         PLATFORM_PERMISSIONS.put(Manifest.permission.RECEIVE_SMS, SMS);
PLATFORM_PERMISSIONS.put(Manifest.permission.READ_SMS, SMS)179         PLATFORM_PERMISSIONS.put(Manifest.permission.READ_SMS, SMS);
PLATFORM_PERMISSIONS.put(Manifest.permission.RECEIVE_MMS, SMS)180         PLATFORM_PERMISSIONS.put(Manifest.permission.RECEIVE_MMS, SMS);
PLATFORM_PERMISSIONS.put(Manifest.permission.RECEIVE_WAP_PUSH, SMS)181         PLATFORM_PERMISSIONS.put(Manifest.permission.RECEIVE_WAP_PUSH, SMS);
PLATFORM_PERMISSIONS.put(Manifest.permission.READ_CELL_BROADCASTS, SMS)182         PLATFORM_PERMISSIONS.put(Manifest.permission.READ_CELL_BROADCASTS, SMS);
183 
PLATFORM_PERMISSIONS.put(Manifest.permission.READ_EXTERNAL_STORAGE, STORAGE)184         PLATFORM_PERMISSIONS.put(Manifest.permission.READ_EXTERNAL_STORAGE, STORAGE);
PLATFORM_PERMISSIONS.put(Manifest.permission.WRITE_EXTERNAL_STORAGE, STORAGE)185         PLATFORM_PERMISSIONS.put(Manifest.permission.WRITE_EXTERNAL_STORAGE, STORAGE);
PLATFORM_PERMISSIONS.put(Manifest.permission.ACCESS_MEDIA_LOCATION, STORAGE)186         PLATFORM_PERMISSIONS.put(Manifest.permission.ACCESS_MEDIA_LOCATION, STORAGE);
187 
PLATFORM_PERMISSIONS.put(Manifest.permission.ACCESS_FINE_LOCATION, LOCATION)188         PLATFORM_PERMISSIONS.put(Manifest.permission.ACCESS_FINE_LOCATION, LOCATION);
PLATFORM_PERMISSIONS.put(Manifest.permission.ACCESS_COARSE_LOCATION, LOCATION)189         PLATFORM_PERMISSIONS.put(Manifest.permission.ACCESS_COARSE_LOCATION, LOCATION);
PLATFORM_PERMISSIONS.put(Manifest.permission.ACCESS_BACKGROUND_LOCATION, LOCATION)190         PLATFORM_PERMISSIONS.put(Manifest.permission.ACCESS_BACKGROUND_LOCATION, LOCATION);
191 
PLATFORM_PERMISSIONS.put(Manifest.permission.READ_CALL_LOG, CALL_LOG)192         PLATFORM_PERMISSIONS.put(Manifest.permission.READ_CALL_LOG, CALL_LOG);
PLATFORM_PERMISSIONS.put(Manifest.permission.WRITE_CALL_LOG, CALL_LOG)193         PLATFORM_PERMISSIONS.put(Manifest.permission.WRITE_CALL_LOG, CALL_LOG);
PLATFORM_PERMISSIONS.put(Manifest.permission.PROCESS_OUTGOING_CALLS, CALL_LOG)194         PLATFORM_PERMISSIONS.put(Manifest.permission.PROCESS_OUTGOING_CALLS, CALL_LOG);
195 
PLATFORM_PERMISSIONS.put(Manifest.permission.READ_PHONE_STATE, PHONE)196         PLATFORM_PERMISSIONS.put(Manifest.permission.READ_PHONE_STATE, PHONE);
PLATFORM_PERMISSIONS.put(Manifest.permission.READ_PHONE_NUMBERS, PHONE)197         PLATFORM_PERMISSIONS.put(Manifest.permission.READ_PHONE_NUMBERS, PHONE);
PLATFORM_PERMISSIONS.put(Manifest.permission.CALL_PHONE, PHONE)198         PLATFORM_PERMISSIONS.put(Manifest.permission.CALL_PHONE, PHONE);
PLATFORM_PERMISSIONS.put(Manifest.permission.ADD_VOICEMAIL, PHONE)199         PLATFORM_PERMISSIONS.put(Manifest.permission.ADD_VOICEMAIL, PHONE);
PLATFORM_PERMISSIONS.put(Manifest.permission.USE_SIP, PHONE)200         PLATFORM_PERMISSIONS.put(Manifest.permission.USE_SIP, PHONE);
PLATFORM_PERMISSIONS.put(Manifest.permission.ANSWER_PHONE_CALLS, PHONE)201         PLATFORM_PERMISSIONS.put(Manifest.permission.ANSWER_PHONE_CALLS, PHONE);
PLATFORM_PERMISSIONS.put(Manifest.permission.ACCEPT_HANDOVER, PHONE)202         PLATFORM_PERMISSIONS.put(Manifest.permission.ACCEPT_HANDOVER, PHONE);
203 
PLATFORM_PERMISSIONS.put(Manifest.permission.RECORD_AUDIO, MICROPHONE)204         PLATFORM_PERMISSIONS.put(Manifest.permission.RECORD_AUDIO, MICROPHONE);
205 
PLATFORM_PERMISSIONS.put(Manifest.permission.ACTIVITY_RECOGNITION, ACTIVITY_RECOGNITION)206         PLATFORM_PERMISSIONS.put(Manifest.permission.ACTIVITY_RECOGNITION, ACTIVITY_RECOGNITION);
207 
PLATFORM_PERMISSIONS.put(Manifest.permission.CAMERA, CAMERA)208         PLATFORM_PERMISSIONS.put(Manifest.permission.CAMERA, CAMERA);
209 
PLATFORM_PERMISSIONS.put(Manifest.permission.BODY_SENSORS, SENSORS)210         PLATFORM_PERMISSIONS.put(Manifest.permission.BODY_SENSORS, SENSORS);
211 
212         PLATFORM_PERMISSION_GROUPS = new ArrayMap<>();
213         int numPlatformPermissions = PLATFORM_PERMISSIONS.size();
214         for (int i = 0; i < numPlatformPermissions; i++) {
215             String permission = PLATFORM_PERMISSIONS.keyAt(i);
216             String permissionGroup = PLATFORM_PERMISSIONS.valueAt(i);
217 
218             ArrayList<String> permissionsOfThisGroup = PLATFORM_PERMISSION_GROUPS.get(
219                     permissionGroup);
220             if (permissionsOfThisGroup == null) {
221                 permissionsOfThisGroup = new ArrayList<>();
PLATFORM_PERMISSION_GROUPS.put(permissionGroup, permissionsOfThisGroup)222                 PLATFORM_PERMISSION_GROUPS.put(permissionGroup, permissionsOfThisGroup);
223             }
224 
225             permissionsOfThisGroup.add(permission);
226         }
227 
228         ONE_TIME_PERMISSION_GROUPS = new ArraySet<>();
229         ONE_TIME_PERMISSION_GROUPS.add(LOCATION);
230         ONE_TIME_PERMISSION_GROUPS.add(CAMERA);
231         ONE_TIME_PERMISSION_GROUPS.add(MICROPHONE);
232 
233         PERM_GROUP_REQUEST_RES = new ArrayMap<>();
PERM_GROUP_REQUEST_RES.put(CONTACTS, R.string.permgrouprequest_contacts)234         PERM_GROUP_REQUEST_RES.put(CONTACTS, R.string.permgrouprequest_contacts);
PERM_GROUP_REQUEST_RES.put(LOCATION, R.string.permgrouprequest_location)235         PERM_GROUP_REQUEST_RES.put(LOCATION, R.string.permgrouprequest_location);
PERM_GROUP_REQUEST_RES.put(CALENDAR, R.string.permgrouprequest_calendar)236         PERM_GROUP_REQUEST_RES.put(CALENDAR, R.string.permgrouprequest_calendar);
PERM_GROUP_REQUEST_RES.put(SMS, R.string.permgrouprequest_sms)237         PERM_GROUP_REQUEST_RES.put(SMS, R.string.permgrouprequest_sms);
PERM_GROUP_REQUEST_RES.put(STORAGE, R.string.permgrouprequest_storage)238         PERM_GROUP_REQUEST_RES.put(STORAGE, R.string.permgrouprequest_storage);
PERM_GROUP_REQUEST_RES.put(MICROPHONE, R.string.permgrouprequest_microphone)239         PERM_GROUP_REQUEST_RES.put(MICROPHONE, R.string.permgrouprequest_microphone);
240         PERM_GROUP_REQUEST_RES
put(ACTIVITY_RECOGNITION, R.string.permgrouprequest_activityRecognition)241                 .put(ACTIVITY_RECOGNITION, R.string.permgrouprequest_activityRecognition);
PERM_GROUP_REQUEST_RES.put(CAMERA, R.string.permgrouprequest_camera)242         PERM_GROUP_REQUEST_RES.put(CAMERA, R.string.permgrouprequest_camera);
PERM_GROUP_REQUEST_RES.put(CALL_LOG, R.string.permgrouprequest_calllog)243         PERM_GROUP_REQUEST_RES.put(CALL_LOG, R.string.permgrouprequest_calllog);
PERM_GROUP_REQUEST_RES.put(PHONE, R.string.permgrouprequest_phone)244         PERM_GROUP_REQUEST_RES.put(PHONE, R.string.permgrouprequest_phone);
PERM_GROUP_REQUEST_RES.put(SENSORS, R.string.permgrouprequest_sensors)245         PERM_GROUP_REQUEST_RES.put(SENSORS, R.string.permgrouprequest_sensors);
246 
247         PERM_GROUP_REQUEST_DETAIL_RES = new ArrayMap<>();
PERM_GROUP_REQUEST_DETAIL_RES.put(LOCATION, R.string.permgrouprequestdetail_location)248         PERM_GROUP_REQUEST_DETAIL_RES.put(LOCATION, R.string.permgrouprequestdetail_location);
249 
250         PERM_GROUP_BACKGROUND_REQUEST_RES = new ArrayMap<>();
251         PERM_GROUP_BACKGROUND_REQUEST_RES
put(LOCATION, R.string.permgroupbackgroundrequest_location)252                 .put(LOCATION, R.string.permgroupbackgroundrequest_location);
253 
254         PERM_GROUP_BACKGROUND_REQUEST_DETAIL_RES = new ArrayMap<>();
255         PERM_GROUP_BACKGROUND_REQUEST_DETAIL_RES
put(LOCATION, R.string.permgroupbackgroundrequestdetail_location)256                 .put(LOCATION, R.string.permgroupbackgroundrequestdetail_location);
257 
258         PERM_GROUP_UPGRADE_REQUEST_RES = new ArrayMap<>();
PERM_GROUP_UPGRADE_REQUEST_RES.put(LOCATION, R.string.permgroupupgraderequest_location)259         PERM_GROUP_UPGRADE_REQUEST_RES.put(LOCATION, R.string.permgroupupgraderequest_location);
260 
261         PERM_GROUP_UPGRADE_REQUEST_DETAIL_RES = new ArrayMap<>();
262         PERM_GROUP_UPGRADE_REQUEST_DETAIL_RES
put(LOCATION, R.string.permgroupupgraderequestdetail_location)263                 .put(LOCATION, R.string.permgroupupgraderequestdetail_location);
264     }
265 
Utils()266     private Utils() {
267         /* do nothing - hide constructor */
268     }
269 
270     private static ArrayMap<UserHandle, Context> sUserContexts = new ArrayMap<>();
271 
272     public enum ForegroundCapableType {
273         SOUND_TRIGGER,
274         ASSISTANT,
275         VOICE_INTERACTION,
276         CARRIER_SERVICE,
277         NONE
278     }
279 
280     /**
281      * Creates and caches a PackageContext for the requested user, or returns the previously cached
282      * value. The package of the PackageContext is the application's package.
283      *
284      * @param app The currently running application
285      * @param user The desired user for the context
286      *
287      * @return The generated or cached Context for the requested user
288      *
289      * @throws PackageManager.NameNotFoundException If the app has no package name attached
290      */
getUserContext(Application app, UserHandle user)291     public static @NonNull Context getUserContext(Application app, UserHandle user) throws
292             PackageManager.NameNotFoundException {
293         if (!sUserContexts.containsKey(user)) {
294             sUserContexts.put(user, app.getApplicationContext()
295                     .createPackageContextAsUser(app.getPackageName(), 0, user));
296         }
297         return sUserContexts.get(user);
298     }
299 
300     /**
301      * {@code @NonNull} version of {@link Context#getSystemService(Class)}
302      */
getSystemServiceSafe(@onNull Context context, Class<M> clazz)303     public static @NonNull <M> M getSystemServiceSafe(@NonNull Context context, Class<M> clazz) {
304         return Preconditions.checkNotNull(context.getSystemService(clazz),
305                 "Could not resolve " + clazz.getSimpleName());
306     }
307 
308     /**
309      * {@code @NonNull} version of {@link Context#getSystemService(Class)}
310      */
getSystemServiceSafe(@onNull Context context, Class<M> clazz, @NonNull UserHandle user)311     public static @NonNull <M> M getSystemServiceSafe(@NonNull Context context, Class<M> clazz,
312             @NonNull UserHandle user) {
313         try {
314             return Preconditions.checkNotNull(context.createPackageContextAsUser(
315                     context.getPackageName(), 0, user).getSystemService(clazz),
316                     "Could not resolve " + clazz.getSimpleName());
317         } catch (PackageManager.NameNotFoundException neverHappens) {
318             throw new IllegalStateException();
319         }
320     }
321 
322     /**
323      * {@code @NonNull} version of {@link Intent#getParcelableExtra(String)}
324      */
getParcelableExtraSafe(@onNull Intent intent, @NonNull String name)325     public static @NonNull <T extends Parcelable> T getParcelableExtraSafe(@NonNull Intent intent,
326             @NonNull String name) {
327         return Preconditions.checkNotNull(intent.getParcelableExtra(name),
328                 "Could not get parcelable extra for " + name);
329     }
330 
331     /**
332      * {@code @NonNull} version of {@link Intent#getStringExtra(String)}
333      */
getStringExtraSafe(@onNull Intent intent, @NonNull String name)334     public static @NonNull String getStringExtraSafe(@NonNull Intent intent,
335             @NonNull String name) {
336         return Preconditions.checkNotNull(intent.getStringExtra(name),
337                 "Could not get string extra for " + name);
338     }
339 
340     /**
341      * Returns true if a permission is dangerous, installed, and not removed
342      * @param permissionInfo The permission we wish to check
343      * @return If all of the conditions are met
344      */
isPermissionDangerousInstalledNotRemoved(PermissionInfo permissionInfo)345     public static boolean isPermissionDangerousInstalledNotRemoved(PermissionInfo permissionInfo) {
346         return permissionInfo != null
347                   && permissionInfo.getProtection() == PermissionInfo.PROTECTION_DANGEROUS
348                   && (permissionInfo.flags & PermissionInfo.FLAG_INSTALLED) != 0
349                   && (permissionInfo.flags & PermissionInfo.FLAG_REMOVED) == 0;
350     }
351 
352     /**
353      * Get permission group a platform permission belongs to, or null if the permission is not a
354      * platform permission.
355      *
356      * @param permission the permission to resolve
357      *
358      * @return The group the permission belongs to
359      */
getGroupOfPlatformPermission(@onNull String permission)360     public static @Nullable String getGroupOfPlatformPermission(@NonNull String permission) {
361         return PLATFORM_PERMISSIONS.get(permission);
362     }
363 
364     /**
365      * Get name of the permission group a permission belongs to.
366      *
367      * @param permission the {@link PermissionInfo info} of the permission to resolve
368      *
369      * @return The group the permission belongs to
370      */
getGroupOfPermission(@onNull PermissionInfo permission)371     public static @Nullable String getGroupOfPermission(@NonNull PermissionInfo permission) {
372         String groupName = Utils.getGroupOfPlatformPermission(permission.name);
373         if (groupName == null) {
374             groupName = permission.group;
375         }
376 
377         return groupName;
378     }
379 
380     /**
381      * Get the names for all platform permissions belonging to a group.
382      *
383      * @param group the group
384      *
385      * @return The permission names  or an empty list if the
386      *         group is not does not have platform runtime permissions
387      */
getPlatformPermissionNamesOfGroup(@onNull String group)388     public static @NonNull List<String> getPlatformPermissionNamesOfGroup(@NonNull String group) {
389         final ArrayList<String> permissions = PLATFORM_PERMISSION_GROUPS.get(group);
390         return (permissions != null) ? permissions : Collections.emptyList();
391     }
392 
393     /**
394      * Get the {@link PermissionInfo infos} for all platform permissions belonging to a group.
395      *
396      * @param pm    Package manager to use to resolve permission infos
397      * @param group the group
398      *
399      * @return The infos for platform permissions belonging to the group or an empty list if the
400      *         group is not does not have platform runtime permissions
401      */
getPlatformPermissionsOfGroup( @onNull PackageManager pm, @NonNull String group)402     public static @NonNull List<PermissionInfo> getPlatformPermissionsOfGroup(
403             @NonNull PackageManager pm, @NonNull String group) {
404         ArrayList<PermissionInfo> permInfos = new ArrayList<>();
405 
406         ArrayList<String> permissions = PLATFORM_PERMISSION_GROUPS.get(group);
407         if (permissions == null) {
408             return Collections.emptyList();
409         }
410 
411         int numPermissions = permissions.size();
412         for (int i = 0; i < numPermissions; i++) {
413             String permName = permissions.get(i);
414             PermissionInfo permInfo;
415             try {
416                 permInfo = pm.getPermissionInfo(permName, 0);
417             } catch (PackageManager.NameNotFoundException e) {
418                 throw new IllegalStateException(permName + " not defined by platform", e);
419             }
420 
421             permInfos.add(permInfo);
422         }
423 
424         return permInfos;
425     }
426 
427     /**
428      * Get the {@link PermissionInfo infos} for all permission infos belonging to a group.
429      *
430      * @param pm    Package manager to use to resolve permission infos
431      * @param group the group
432      *
433      * @return The infos of permissions belonging to the group or an empty list if the group
434      *         does not have runtime permissions
435      */
getPermissionInfosForGroup( @onNull PackageManager pm, @NonNull String group)436     public static @NonNull List<PermissionInfo> getPermissionInfosForGroup(
437             @NonNull PackageManager pm, @NonNull String group)
438             throws PackageManager.NameNotFoundException {
439         List<PermissionInfo> permissions = pm.queryPermissionsByGroup(group, 0);
440         permissions.addAll(getPlatformPermissionsOfGroup(pm, group));
441 
442         /*
443          * If the undefined group is requested, the package manager will return all platform
444          * permissions, since they are marked as Undefined in the manifest. Do not return these
445          * permissions.
446          */
447         if (group.equals(Manifest.permission_group.UNDEFINED)) {
448             List<PermissionInfo> undefinedPerms = new ArrayList<>();
449             for (PermissionInfo permissionInfo : permissions) {
450                 String permGroup = getGroupOfPlatformPermission(permissionInfo.name);
451                 if (permGroup == null || permGroup.equals(Manifest.permission_group.UNDEFINED)) {
452                     undefinedPerms.add(permissionInfo);
453                 }
454             }
455             return undefinedPerms;
456         }
457 
458         return permissions;
459     }
460 
461     /**
462      * Get the {@link PermissionInfo infos} for all runtime installed permission infos belonging to
463      * a group.
464      *
465      * @param pm    Package manager to use to resolve permission infos
466      * @param group the group
467      *
468      * @return The infos of installed runtime permissions belonging to the group or an empty list
469      * if the group does not have runtime permissions
470      */
getInstalledRuntimePermissionInfosForGroup( @onNull PackageManager pm, @NonNull String group)471     public static @NonNull List<PermissionInfo> getInstalledRuntimePermissionInfosForGroup(
472             @NonNull PackageManager pm, @NonNull String group)
473             throws PackageManager.NameNotFoundException {
474         List<PermissionInfo> permissions = pm.queryPermissionsByGroup(group, 0);
475         permissions.addAll(getPlatformPermissionsOfGroup(pm, group));
476 
477         List<PermissionInfo> installedRuntime = new ArrayList<>();
478         for (PermissionInfo permissionInfo: permissions) {
479             if (permissionInfo.getProtection() == PermissionInfo.PROTECTION_DANGEROUS
480                     && (permissionInfo.flags & PermissionInfo.FLAG_INSTALLED) != 0
481                     && (permissionInfo.flags & PermissionInfo.FLAG_REMOVED) == 0) {
482                 installedRuntime.add(permissionInfo);
483             }
484         }
485 
486         /*
487          * If the undefined group is requested, the package manager will return all platform
488          * permissions, since they are marked as Undefined in the manifest. Do not return these
489          * permissions.
490          */
491         if (group.equals(Manifest.permission_group.UNDEFINED)) {
492             List<PermissionInfo> undefinedPerms = new ArrayList<>();
493             for (PermissionInfo permissionInfo : installedRuntime) {
494                 String permGroup = getGroupOfPlatformPermission(permissionInfo.name);
495                 if (permGroup == null || permGroup.equals(Manifest.permission_group.UNDEFINED)) {
496                     undefinedPerms.add(permissionInfo);
497                 }
498             }
499             return undefinedPerms;
500         }
501 
502         return installedRuntime;
503     }
504 
505     /**
506      * Get the {@link PackageItemInfo infos} for the given permission group.
507      *
508      * @param groupName the group
509      * @param context the {@code Context} to retrieve {@code PackageManager}
510      *
511      * @return The info of permission group or null if the group does not have runtime permissions.
512      */
getGroupInfo(@onNull String groupName, @NonNull Context context)513     public static @Nullable PackageItemInfo getGroupInfo(@NonNull String groupName,
514             @NonNull Context context) {
515         try {
516             return context.getPackageManager().getPermissionGroupInfo(groupName, 0);
517         } catch (NameNotFoundException e) {
518             /* ignore */
519         }
520         try {
521             return context.getPackageManager().getPermissionInfo(groupName, 0);
522         } catch (NameNotFoundException e) {
523             /* ignore */
524         }
525         return null;
526     }
527 
528     /**
529      * Get the {@link PermissionInfo infos} for all permission infos belonging to a group.
530      *
531      * @param groupName the group
532      * @param context the {@code Context} to retrieve {@code PackageManager}
533      *
534      * @return The infos of permissions belonging to the group or null if the group does not have
535      *         runtime permissions.
536      */
getGroupPermissionInfos(@onNull String groupName, @NonNull Context context)537     public static @Nullable List<PermissionInfo> getGroupPermissionInfos(@NonNull String groupName,
538             @NonNull Context context) {
539         try {
540             return Utils.getPermissionInfosForGroup(context.getPackageManager(), groupName);
541         } catch (NameNotFoundException e) {
542             /* ignore */
543         }
544         try {
545             PermissionInfo permissionInfo = context.getPackageManager()
546                     .getPermissionInfo(groupName, 0);
547             List<PermissionInfo> permissions = new ArrayList<>();
548             permissions.add(permissionInfo);
549             return permissions;
550         } catch (NameNotFoundException e) {
551             /* ignore */
552         }
553         return null;
554     }
555 
556     /**
557      * Get the label for an application, truncating if it is too long.
558      *
559      * @param applicationInfo the {@link ApplicationInfo} of the application
560      * @param context the {@code Context} to retrieve {@code PackageManager}
561      *
562      * @return the label for the application
563      */
564     @NonNull
getAppLabel(@onNull ApplicationInfo applicationInfo, @NonNull Context context)565     public static String getAppLabel(@NonNull ApplicationInfo applicationInfo,
566             @NonNull Context context) {
567         return getAppLabel(applicationInfo, DEFAULT_MAX_LABEL_SIZE_PX, context);
568     }
569 
570     /**
571      * Get the full label for an application without truncation.
572      *
573      * @param applicationInfo the {@link ApplicationInfo} of the application
574      * @param context the {@code Context} to retrieve {@code PackageManager}
575      *
576      * @return the label for the application
577      */
578     @NonNull
getFullAppLabel(@onNull ApplicationInfo applicationInfo, @NonNull Context context)579     public static String getFullAppLabel(@NonNull ApplicationInfo applicationInfo,
580             @NonNull Context context) {
581         return getAppLabel(applicationInfo, 0, context);
582     }
583 
584     /**
585      * Get the label for an application with the ability to control truncating.
586      *
587      * @param applicationInfo the {@link ApplicationInfo} of the application
588      * @param ellipsizeDip see {@link TextUtils#makeSafeForPresentation}.
589      * @param context the {@code Context} to retrieve {@code PackageManager}
590      *
591      * @return the label for the application
592      */
593     @NonNull
getAppLabel(@onNull ApplicationInfo applicationInfo, float ellipsizeDip, @NonNull Context context)594     private static String getAppLabel(@NonNull ApplicationInfo applicationInfo, float ellipsizeDip,
595             @NonNull Context context) {
596         return BidiFormatter.getInstance().unicodeWrap(applicationInfo.loadSafeLabel(
597                 context.getPackageManager(), ellipsizeDip,
598                 TextUtils.SAFE_STRING_FLAG_TRIM | TextUtils.SAFE_STRING_FLAG_FIRST_LINE)
599                 .toString());
600     }
601 
loadDrawable(PackageManager pm, String pkg, int resId)602     public static Drawable loadDrawable(PackageManager pm, String pkg, int resId) {
603         try {
604             return pm.getResourcesForApplication(pkg).getDrawable(resId, null);
605         } catch (Resources.NotFoundException | PackageManager.NameNotFoundException e) {
606             Log.d(LOG_TAG, "Couldn't get resource", e);
607             return null;
608         }
609     }
610 
isModernPermissionGroup(String name)611     public static boolean isModernPermissionGroup(String name) {
612         return PLATFORM_PERMISSION_GROUPS.containsKey(name);
613     }
614 
615     /**
616      * Get the names of the platform permission groups.
617      *
618      * @return the names of the platform permission groups.
619      */
getPlatformPermissionGroups()620     public static List<String> getPlatformPermissionGroups() {
621         return new ArrayList<>(PLATFORM_PERMISSION_GROUPS.keySet());
622     }
623 
624     /**
625      * Get the names of the runtime platform permissions
626      *
627      * @return the names of the runtime platform permissions.
628      */
getRuntimePlatformPermissionNames()629     public static List<String> getRuntimePlatformPermissionNames() {
630         return new ArrayList<>(PLATFORM_PERMISSIONS.keySet());
631     }
632 
633     /**
634      * Is the permissions a platform runtime permission
635      *
636      * @return the names of the runtime platform permissions.
637      */
isRuntimePlatformPermission(@onNull String permission)638     public static boolean isRuntimePlatformPermission(@NonNull String permission) {
639         return PLATFORM_PERMISSIONS.containsKey(permission);
640     }
641 
642     /**
643      * Should UI show this permission.
644      *
645      * <p>If the user cannot change the group, it should not be shown.
646      *
647      * @param group The group that might need to be shown to the user
648      *
649      * @return
650      */
shouldShowPermission(Context context, AppPermissionGroup group)651     public static boolean shouldShowPermission(Context context, AppPermissionGroup group) {
652         if (!group.isGrantingAllowed()) {
653             return false;
654         }
655 
656         final boolean isPlatformPermission = group.getDeclaringPackage().equals(OS_PKG);
657         // Show legacy permissions only if the user chose that.
658         if (isPlatformPermission
659                 && !Utils.isModernPermissionGroup(group.getName())) {
660             return false;
661         }
662         return true;
663     }
664 
applyTint(Context context, Drawable icon, int attr)665     public static Drawable applyTint(Context context, Drawable icon, int attr) {
666         Theme theme = context.getTheme();
667         TypedValue typedValue = new TypedValue();
668         theme.resolveAttribute(attr, typedValue, true);
669         icon = icon.mutate();
670         icon.setTint(context.getColor(typedValue.resourceId));
671         return icon;
672     }
673 
applyTint(Context context, int iconResId, int attr)674     public static Drawable applyTint(Context context, int iconResId, int attr) {
675         return applyTint(context, context.getDrawable(iconResId), attr);
676     }
677 
getAllInstalledApplications(Context context)678     public static List<ApplicationInfo> getAllInstalledApplications(Context context) {
679         return context.getPackageManager().getInstalledApplications(0);
680     }
681 
682     /**
683      * Is the group or background group user sensitive?
684      *
685      * @param group The group that might be user sensitive
686      *
687      * @return {@code true} if the group (or it's subgroup) is user sensitive.
688      */
isGroupOrBgGroupUserSensitive(AppPermissionGroup group)689     public static boolean isGroupOrBgGroupUserSensitive(AppPermissionGroup group) {
690         return group.isUserSensitive() || (group.getBackgroundPermissions() != null
691                 && group.getBackgroundPermissions().isUserSensitive());
692     }
693 
areGroupPermissionsIndividuallyControlled(Context context, String group)694     public static boolean areGroupPermissionsIndividuallyControlled(Context context, String group) {
695         if (!context.getPackageManager().arePermissionsIndividuallyControlled()) {
696             return false;
697         }
698         return Manifest.permission_group.SMS.equals(group)
699                 || Manifest.permission_group.PHONE.equals(group)
700                 || Manifest.permission_group.CONTACTS.equals(group);
701     }
702 
isPermissionIndividuallyControlled(Context context, String permission)703     public static boolean isPermissionIndividuallyControlled(Context context, String permission) {
704         if (!context.getPackageManager().arePermissionsIndividuallyControlled()) {
705             return false;
706         }
707         return Manifest.permission.READ_CONTACTS.equals(permission)
708                 || Manifest.permission.WRITE_CONTACTS.equals(permission)
709                 || Manifest.permission.SEND_SMS.equals(permission)
710                 || Manifest.permission.RECEIVE_SMS.equals(permission)
711                 || Manifest.permission.READ_SMS.equals(permission)
712                 || Manifest.permission.RECEIVE_MMS.equals(permission)
713                 || Manifest.permission.CALL_PHONE.equals(permission)
714                 || Manifest.permission.READ_CALL_LOG.equals(permission)
715                 || Manifest.permission.WRITE_CALL_LOG.equals(permission);
716     }
717 
718     /**
719      * Get the message shown to grant a permission group to an app.
720      *
721      * @param appLabel The label of the app
722      * @param group the group to be granted
723      * @param context A context to resolve resources
724      * @param requestRes The resource id of the grant request message
725      *
726      * @return The formatted message to be used as title when granting permissions
727      */
getRequestMessage(CharSequence appLabel, AppPermissionGroup group, Context context, @StringRes int requestRes)728     public static CharSequence getRequestMessage(CharSequence appLabel, AppPermissionGroup group,
729             Context context, @StringRes int requestRes) {
730         if (group.getName().equals(STORAGE) && !group.isNonIsolatedStorage()) {
731             return Html.fromHtml(
732                     String.format(context.getResources().getConfiguration().getLocales().get(0),
733                             context.getString(R.string.permgrouprequest_storage_isolated),
734                             appLabel), 0);
735         } else if (requestRes != 0) {
736             return Html.fromHtml(context.getResources().getString(requestRes, appLabel), 0);
737         }
738 
739         return Html.fromHtml(context.getString(R.string.permission_warning_template, appLabel,
740                 group.getDescription()), 0);
741     }
742 
743     /**
744      * Build a string representing the given time if it happened on the current day and the date
745      * otherwise.
746      *
747      * @param context the context.
748      * @param lastAccessTime the time in milliseconds.
749      *
750      * @return a string representing the time or date of the given time or null if the time is 0.
751      */
getAbsoluteTimeString(@onNull Context context, long lastAccessTime)752     public static @Nullable String getAbsoluteTimeString(@NonNull Context context,
753             long lastAccessTime) {
754         if (lastAccessTime == 0) {
755             return null;
756         }
757         if (isToday(lastAccessTime)) {
758             return DateFormat.getTimeFormat(context).format(lastAccessTime);
759         } else {
760             return DateFormat.getMediumDateFormat(context).format(lastAccessTime);
761         }
762     }
763 
764     /**
765      * Check whether the given time (in milliseconds) is in the current day.
766      *
767      * @param time the time in milliseconds
768      *
769      * @return whether the given time is in the current day.
770      */
isToday(long time)771     private static boolean isToday(long time) {
772         Calendar today = Calendar.getInstance(Locale.getDefault());
773         today.setTimeInMillis(System.currentTimeMillis());
774         today.set(Calendar.HOUR_OF_DAY, 0);
775         today.set(Calendar.MINUTE, 0);
776         today.set(Calendar.SECOND, 0);
777         today.set(Calendar.MILLISECOND, 0);
778 
779         Calendar date = Calendar.getInstance(Locale.getDefault());
780         date.setTimeInMillis(time);
781         return !date.before(today);
782     }
783 
784     /**
785      * Add a menu item for searching Settings, if there is an activity handling the action.
786      *
787      * @param menu the menu to add the menu item into
788      * @param context the context for checking whether there is an activity handling the action
789      */
prepareSearchMenuItem(@onNull Menu menu, @NonNull Context context)790     public static void prepareSearchMenuItem(@NonNull Menu menu, @NonNull Context context) {
791         Intent intent = new Intent(Settings.ACTION_APP_SEARCH_SETTINGS);
792         if (context.getPackageManager().resolveActivity(intent, 0) == null) {
793             return;
794         }
795         MenuItem searchItem = menu.add(Menu.NONE, Menu.NONE, Menu.NONE, R.string.search_menu);
796         searchItem.setIcon(R.drawable.ic_search_24dp);
797         searchItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
798         searchItem.setOnMenuItemClickListener(item -> {
799             try {
800                 context.startActivity(intent);
801             } catch (ActivityNotFoundException e) {
802                 Log.e(LOG_TAG, "Cannot start activity to search settings", e);
803             }
804             return true;
805         });
806     }
807 
808     /**
809      * Get badged app icon if necessary, similar as used in the Settings UI.
810      *
811      * @param context The context to use
812      * @param appInfo The app the icon belong to
813      *
814      * @return The icon to use
815      */
getBadgedIcon(@onNull Context context, @NonNull ApplicationInfo appInfo)816     public static @NonNull Drawable getBadgedIcon(@NonNull Context context,
817             @NonNull ApplicationInfo appInfo) {
818         UserHandle user = UserHandle.getUserHandleForUid(appInfo.uid);
819         try (IconFactory iconFactory = IconFactory.obtain(context)) {
820             Bitmap iconBmp = iconFactory.createBadgedIconBitmap(
821                     appInfo.loadUnbadgedIcon(context.getPackageManager()), user, false).icon;
822             return new BitmapDrawable(context.getResources(), iconBmp);
823         }
824     }
825 
826     /**
827      * Get a string saying what apps with the given permission group can do.
828      *
829      * @param context The context to use
830      * @param groupName The name of the permission group
831      * @param description The description of the permission group
832      *
833      * @return a string saying what apps with the given permission group can do.
834      */
getPermissionGroupDescriptionString(@onNull Context context, @NonNull String groupName, @NonNull CharSequence description)835     public static @NonNull String getPermissionGroupDescriptionString(@NonNull Context context,
836             @NonNull String groupName, @NonNull CharSequence description) {
837         switch (groupName) {
838             case ACTIVITY_RECOGNITION:
839                 return context.getString(
840                         R.string.permission_description_summary_activity_recognition);
841             case CALENDAR:
842                 return context.getString(R.string.permission_description_summary_calendar);
843             case CALL_LOG:
844                 return context.getString(R.string.permission_description_summary_call_log);
845             case CAMERA:
846                 return context.getString(R.string.permission_description_summary_camera);
847             case CONTACTS:
848                 return context.getString(R.string.permission_description_summary_contacts);
849             case LOCATION:
850                 return context.getString(R.string.permission_description_summary_location);
851             case MICROPHONE:
852                 return context.getString(R.string.permission_description_summary_microphone);
853             case PHONE:
854                 return context.getString(R.string.permission_description_summary_phone);
855             case SENSORS:
856                 return context.getString(R.string.permission_description_summary_sensors);
857             case SMS:
858                 return context.getString(R.string.permission_description_summary_sms);
859             case STORAGE:
860                 return context.getString(R.string.permission_description_summary_storage);
861             default:
862                 return context.getString(R.string.permission_description_summary_generic,
863                         description);
864         }
865     }
866 
867     /**
868      * Whether the Location Access Check is enabled.
869      *
870      * @return {@code true} iff the Location Access Check is enabled.
871      */
isLocationAccessCheckEnabled()872     public static boolean isLocationAccessCheckEnabled() {
873         return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
874                 PROPERTY_LOCATION_ACCESS_CHECK_ENABLED, true);
875     }
876 
877     /**
878      * Get a device protected storage based shared preferences. Avoid storing sensitive data in it.
879      *
880      * @param context the context to get the shared preferences
881      * @return a device protected storage based shared preferences
882      */
883     @NonNull
getDeviceProtectedSharedPreferences(@onNull Context context)884     public static SharedPreferences getDeviceProtectedSharedPreferences(@NonNull Context context) {
885         if (!context.isDeviceProtectedStorage()) {
886             context = context.createDeviceProtectedStorageContext();
887         }
888         return context.getSharedPreferences(Constants.PREFERENCES_FILE, MODE_PRIVATE);
889     }
890 
getOneTimePermissionsTimeout()891     public static long getOneTimePermissionsTimeout() {
892         return DeviceConfig.getLong(DeviceConfig.NAMESPACE_PERMISSIONS,
893                 PROPERTY_ONE_TIME_PERMISSIONS_TIMEOUT_MILLIS, ONE_TIME_PERMISSIONS_TIMEOUT_MILLIS);
894     }
895 
896     /**
897      * Get context of the parent user of the profile group (i.e. usually the 'personal' profile,
898      * not the 'work' profile).
899      *
900      * @param context The context of a user of the profile user group.
901      *
902      * @return The context of the parent user
903      */
getParentUserContext(@onNull Context context)904     public static Context getParentUserContext(@NonNull Context context) {
905         UserHandle parentUser = getSystemServiceSafe(context, UserManager.class)
906                 .getProfileParent(UserHandle.of(myUserId()));
907 
908         if (parentUser == null) {
909             return context;
910         }
911 
912         // In a multi profile environment perform all operations as the parent user of the
913         // current profile
914         try {
915             return context.createPackageContextAsUser(context.getPackageName(), 0,
916                     parentUser);
917         } catch (PackageManager.NameNotFoundException e) {
918             // cannot happen
919             throw new IllegalStateException("Could not switch to parent user " + parentUser, e);
920         }
921     }
922 
923     /**
924      * Whether the permission group supports one-time
925      * @param permissionGroup The permission group to check
926      * @return {@code true} iff the group supports one-time
927      */
supportsOneTimeGrant(String permissionGroup)928     public static boolean supportsOneTimeGrant(String permissionGroup) {
929         return ONE_TIME_PERMISSION_GROUPS.contains(permissionGroup);
930     }
931 
932     /**
933      * The resource id for the request message for a permission group
934      * @param groupName Permission group name
935      * @return The id or 0 if the permission group doesn't exist or have a message
936      */
getRequest(String groupName)937     public static int getRequest(String groupName) {
938         return PERM_GROUP_REQUEST_RES.getOrDefault(groupName, 0);
939     }
940 
941     /**
942      * The resource id for the request detail message for a permission group
943      * @param groupName Permission group name
944      * @return The id or 0 if the permission group doesn't exist or have a message
945      */
getRequestDetail(String groupName)946     public static int getRequestDetail(String groupName) {
947         return PERM_GROUP_REQUEST_DETAIL_RES.getOrDefault(groupName, 0);
948     }
949 
950     /**
951      * The resource id for the background request message for a permission group
952      * @param groupName Permission group name
953      * @return The id or 0 if the permission group doesn't exist or have a message
954      */
getBackgroundRequest(String groupName)955     public static int getBackgroundRequest(String groupName) {
956         return PERM_GROUP_BACKGROUND_REQUEST_RES.getOrDefault(groupName, 0);
957     }
958 
959     /**
960      * The resource id for the background request detail message for a permission group
961      * @param groupName Permission group name
962      * @return The id or 0 if the permission group doesn't exist or have a message
963      */
getBackgroundRequestDetail(String groupName)964     public static int getBackgroundRequestDetail(String groupName) {
965         return PERM_GROUP_BACKGROUND_REQUEST_DETAIL_RES.getOrDefault(groupName, 0);
966     }
967 
968     /**
969      * The resource id for the upgrade request message for a permission group
970      * @param groupName Permission group name
971      * @return The id or 0 if the permission group doesn't exist or have a message
972      */
getUpgradeRequest(String groupName)973     public static int getUpgradeRequest(String groupName) {
974         return PERM_GROUP_UPGRADE_REQUEST_RES.getOrDefault(groupName, 0);
975     }
976 
977     /**
978      * The resource id for the upgrade request detail message for a permission group
979      * @param groupName Permission group name
980      * @return The id or 0 if the permission group doesn't exist or have a message
981      */
getUpgradeRequestDetail(String groupName)982     public static int getUpgradeRequestDetail(String groupName) {
983         return PERM_GROUP_UPGRADE_REQUEST_DETAIL_RES.getOrDefault(groupName, 0);
984     }
985 
986     /**
987      * Checks whether a package has an active one-time permission according to the system server's
988      * flags
989      *
990      * @param context the {@code Context} to retrieve {@code PackageManager}
991      * @param packageName The package to check for
992      * @return Whether a package has an active one-time permission
993      */
hasOneTimePermissions(Context context, String packageName)994     public static boolean hasOneTimePermissions(Context context, String packageName) {
995         String[] permissions;
996         PackageManager pm = context.getPackageManager();
997         try {
998             permissions = pm.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS)
999                     .requestedPermissions;
1000         } catch (NameNotFoundException e) {
1001             Log.w(LOG_TAG, "Checking for one-time permissions in nonexistent package");
1002             return false;
1003         }
1004         if (permissions == null) {
1005             return false;
1006         }
1007         for (String permissionName : permissions) {
1008             if ((pm.getPermissionFlags(permissionName, packageName, Process.myUserHandle())
1009                     & PackageManager.FLAG_PERMISSION_ONE_TIME) != 0
1010                     && pm.checkPermission(permissionName, packageName)
1011                     == PERMISSION_GRANTED) {
1012                 return true;
1013             }
1014         }
1015         return false;
1016     }
1017 
1018     /**
1019      * Returns a random session ID value that's guaranteed to not be {@code INVALID_SESSION_ID}.
1020      *
1021      * @return A valid session ID.
1022      */
getValidSessionId()1023     public static long getValidSessionId() {
1024         long sessionId = INVALID_SESSION_ID;
1025         while (sessionId == INVALID_SESSION_ID) {
1026             sessionId = new Random().nextLong();
1027         }
1028         return sessionId;
1029     }
1030 
1031     /**
1032      * Gets the label of the Settings application
1033      *
1034      * @param pm The packageManager used to get the activity resolution
1035      *
1036      * @return The CharSequence title of the settings app
1037      */
1038     @Nullable
getSettingsLabelForNotifications(PackageManager pm)1039     public static CharSequence getSettingsLabelForNotifications(PackageManager pm) {
1040         // We pretend we're the Settings app sending the notification, so figure out its name.
1041         Intent openSettingsIntent = new Intent(Settings.ACTION_SETTINGS);
1042         ResolveInfo resolveInfo = pm.resolveActivity(openSettingsIntent, MATCH_SYSTEM_ONLY);
1043         if (resolveInfo == null) {
1044             return null;
1045         }
1046         return pm.getApplicationLabel(resolveInfo.activityInfo.applicationInfo);
1047     }
1048 
1049     /**
1050      * If an app could have foreground capabilities it is because it meets some criteria. This
1051      * function returns which criteria it meets.
1052      * @param context The context as the user of interest.
1053      * @param packageName The package to check.
1054      * @return the type of foreground capable app.
1055      * @throws NameNotFoundException
1056      */
getForegroundCapableType(@onNull Context context, @NonNull String packageName)1057     public static @NonNull ForegroundCapableType getForegroundCapableType(@NonNull Context context,
1058             @NonNull String packageName) throws NameNotFoundException {
1059 
1060         PackageManager pm = context.getPackageManager();
1061 
1062         // Apps which can be bound by SoundTriggerService
1063         if (pm.checkPermission(CAPTURE_AUDIO_HOTWORD, packageName) == PERMISSION_GRANTED) {
1064             ServiceInfo[] services = pm.getPackageInfo(packageName, GET_SERVICES).services;
1065             if (services != null) {
1066                 for (ServiceInfo service : services) {
1067                     if (BIND_SOUND_TRIGGER_DETECTION_SERVICE.equals(service.permission)) {
1068                         return ForegroundCapableType.SOUND_TRIGGER;
1069                     }
1070                 }
1071             }
1072         }
1073 
1074         // VoiceInteractionService
1075         if (context.getSystemService(RoleManager.class).getRoleHolders(RoleManager.ROLE_ASSISTANT)
1076                 .contains(packageName)) {
1077             return ForegroundCapableType.ASSISTANT;
1078         }
1079         String voiceInteraction = Settings.Secure.getString(context.getContentResolver(),
1080                 "voice_interaction_service");
1081         if (!TextUtils.isEmpty(voiceInteraction)) {
1082             ComponentName component = ComponentName.unflattenFromString(voiceInteraction);
1083             if (component != null && TextUtils.equals(packageName, component.getPackageName())) {
1084                 return ForegroundCapableType.VOICE_INTERACTION;
1085             }
1086         }
1087 
1088         // Carrier privileged apps implementing the carrier service
1089         final TelephonyManager telephonyManager =
1090                 context.getSystemService(TelephonyManager.class);
1091         int numPhones = telephonyManager.getActiveModemCount();
1092         for (int phoneId = 0; phoneId < numPhones; phoneId++) {
1093             List<String> packages = telephonyManager.getCarrierPackageNamesForIntentAndPhone(
1094                     new Intent(CarrierService.CARRIER_SERVICE_INTERFACE), phoneId);
1095             if (packages != null && packages.contains(packageName)) {
1096                 return ForegroundCapableType.CARRIER_SERVICE;
1097             }
1098         }
1099 
1100         return ForegroundCapableType.NONE;
1101     }
1102 
1103     /**
1104      * This tells whether we should blame the app for potential background access. Intended to be
1105      * used for creating Ui.
1106      * @param context The context as the user of interest
1107      * @param packageName The package to check
1108      * @return true if the given package could possibly have foreground capabilities while in the
1109      * background, otherwise false.
1110      * @throws NameNotFoundException
1111      */
couldHaveForegroundCapabilities(@onNull Context context, @NonNull String packageName)1112     public static boolean couldHaveForegroundCapabilities(@NonNull Context context,
1113             @NonNull String packageName) throws NameNotFoundException {
1114         return getForegroundCapableType(context, packageName) != ForegroundCapableType.NONE;
1115     }
1116 
1117     /**
1118      * Determines if a given user is disabled, or is a work profile.
1119      * @param user The user to check
1120      * @return true if the user is disabled, or the user is a work profile
1121      */
isUserDisabledOrWorkProfile(UserHandle user)1122     public static boolean isUserDisabledOrWorkProfile(UserHandle user) {
1123         Application app = PermissionControllerApplication.get();
1124         UserManager userManager = app.getSystemService(UserManager.class);
1125         // In android TV, parental control accounts are managed profiles
1126         return !userManager.getEnabledProfiles().contains(user)
1127                 || (userManager.isManagedProfile(user.getIdentifier())
1128                 && !DeviceUtils.isTelevision(app));
1129     }
1130 
1131     /**
1132      * @return Whether a package is an emergency app.
1133      */
isEmergencyApp(@onNull Context context, @NonNull String packageName)1134     public static boolean isEmergencyApp(@NonNull Context context,  @NonNull String packageName) {
1135         try {
1136             return context.getSystemService(RoleManager.class)
1137                     .getRoleHolders(RoleManager.ROLE_EMERGENCY).contains(packageName);
1138         } catch (Throwable t) {
1139             // Avoid crashing for any reason, this isn't very well tested
1140             Log.e(LOG_TAG, "Unable to check if " + packageName + " is an emergency app.", t);
1141             return false;
1142         }
1143     }
1144 }
1145