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