1 /** 2 * Copyright (C) 2007 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy 6 * 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, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations 14 * under the License. 15 */ 16 17 package com.android.settings; 18 19 import static android.content.Intent.EXTRA_USER; 20 import static android.content.Intent.EXTRA_USER_ID; 21 import static android.os.UserManager.USER_TYPE_FULL_SYSTEM; 22 import static android.os.UserManager.USER_TYPE_PROFILE_MANAGED; 23 import static android.os.UserManager.USER_TYPE_PROFILE_PRIVATE; 24 import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH; 25 import static android.text.format.DateUtils.FORMAT_SHOW_DATE; 26 27 import static com.android.settings.password.ConfirmDeviceCredentialActivity.BIOMETRIC_PROMPT_AUTHENTICATORS; 28 import static com.android.settings.password.ConfirmDeviceCredentialActivity.BIOMETRIC_PROMPT_HIDE_BACKGROUND; 29 import static com.android.settings.password.ConfirmDeviceCredentialActivity.BIOMETRIC_PROMPT_NEGATIVE_BUTTON_TEXT; 30 import static com.android.settings.password.ConfirmDeviceCredentialActivity.EXTRA_DATA; 31 32 import android.app.ActionBar; 33 import android.app.Activity; 34 import android.app.ActivityManager; 35 import android.app.AppGlobals; 36 import android.app.IActivityManager; 37 import android.app.KeyguardManager; 38 import android.app.admin.DevicePolicyManager; 39 import android.content.ActivityNotFoundException; 40 import android.content.ComponentName; 41 import android.content.ContentResolver; 42 import android.content.Context; 43 import android.content.Intent; 44 import android.content.IntentFilter; 45 import android.content.pm.ApplicationInfo; 46 import android.content.pm.IPackageManager; 47 import android.content.pm.IntentFilterVerificationInfo; 48 import android.content.pm.PackageManager; 49 import android.content.pm.PackageManager.NameNotFoundException; 50 import android.content.pm.UserInfo; 51 import android.content.pm.UserProperties; 52 import android.content.res.Configuration; 53 import android.content.res.Resources; 54 import android.content.res.TypedArray; 55 import android.database.Cursor; 56 import android.graphics.Bitmap; 57 import android.graphics.Canvas; 58 import android.graphics.drawable.AdaptiveIconDrawable; 59 import android.graphics.drawable.BitmapDrawable; 60 import android.graphics.drawable.Drawable; 61 import android.graphics.drawable.VectorDrawable; 62 import android.hardware.biometrics.BiometricManager; 63 import android.hardware.biometrics.SensorProperties; 64 import android.hardware.face.Face; 65 import android.hardware.face.FaceManager; 66 import android.hardware.face.FaceSensorPropertiesInternal; 67 import android.hardware.fingerprint.Fingerprint; 68 import android.hardware.fingerprint.FingerprintManager; 69 import android.net.ConnectivityManager; 70 import android.net.LinkAddress; 71 import android.net.LinkProperties; 72 import android.net.Network; 73 import android.net.wifi.WifiManager; 74 import android.os.BatteryManager; 75 import android.os.Binder; 76 import android.os.Build; 77 import android.os.Bundle; 78 import android.os.Flags; 79 import android.os.IBinder; 80 import android.os.INetworkManagementService; 81 import android.os.RemoteException; 82 import android.os.ServiceManager; 83 import android.os.UserHandle; 84 import android.os.UserManager; 85 import android.os.storage.StorageManager; 86 import android.os.storage.VolumeInfo; 87 import android.preference.PreferenceFrameLayout; 88 import android.provider.ContactsContract.CommonDataKinds; 89 import android.provider.ContactsContract.Contacts; 90 import android.provider.ContactsContract.Data; 91 import android.provider.ContactsContract.Profile; 92 import android.provider.ContactsContract.RawContacts; 93 import android.telephony.SubscriptionManager; 94 import android.telephony.TelephonyManager; 95 import android.text.Spannable; 96 import android.text.SpannableString; 97 import android.text.TextUtils; 98 import android.text.format.DateUtils; 99 import android.text.style.TtsSpan; 100 import android.util.ArraySet; 101 import android.util.IconDrawableFactory; 102 import android.util.Log; 103 import android.view.LayoutInflater; 104 import android.view.View; 105 import android.view.ViewGroup; 106 import android.widget.EditText; 107 import android.widget.ListView; 108 import android.widget.TabWidget; 109 110 import androidx.annotation.ColorInt; 111 import androidx.annotation.NonNull; 112 import androidx.annotation.Nullable; 113 import androidx.annotation.VisibleForTesting; 114 import androidx.core.graphics.Insets; 115 import androidx.core.graphics.drawable.IconCompat; 116 import androidx.core.graphics.drawable.RoundedBitmapDrawable; 117 import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory; 118 import androidx.core.view.ViewCompat; 119 import androidx.core.view.WindowInsetsCompat; 120 import androidx.fragment.app.Fragment; 121 import androidx.fragment.app.FragmentActivity; 122 import androidx.lifecycle.Lifecycle; 123 124 import com.android.internal.app.UnlaunchableAppActivity; 125 import com.android.internal.util.ArrayUtils; 126 import com.android.internal.widget.LockPatternUtils; 127 import com.android.settings.dashboard.profileselector.ProfileFragmentBridge; 128 import com.android.settings.dashboard.profileselector.ProfileSelectFragment; 129 import com.android.settings.dashboard.profileselector.ProfileSelectFragment.ProfileType; 130 import com.android.settings.password.ChooseLockSettingsHelper; 131 import com.android.settings.password.ConfirmDeviceCredentialActivity; 132 import com.android.settingslib.widget.ActionBarShadowController; 133 import com.android.settingslib.widget.AdaptiveIcon; 134 135 import java.util.Arrays; 136 import java.util.Iterator; 137 import java.util.List; 138 import java.util.Locale; 139 import java.util.Objects; 140 import java.util.Set; 141 142 public final class Utils extends com.android.settingslib.Utils { 143 144 private static final String TAG = "Settings"; 145 146 public static final String FILE_PROVIDER_AUTHORITY = "com.android.settings.files"; 147 148 /** 149 * Set the preference's title to the matching activity's label. 150 */ 151 public static final int UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY = 1; 152 153 public static final String SETTINGS_PACKAGE_NAME = "com.android.settings"; 154 155 public static final String SYSTEMUI_PACKAGE_NAME = "com.android.systemui"; 156 157 public static final String PHONE_PACKAGE_NAME = "com.android.phone"; 158 159 public static final String OS_PKG = "os"; 160 161 /** 162 * Whether to disable the new device identifier access restrictions. 163 */ 164 public static final String PROPERTY_DEVICE_IDENTIFIER_ACCESS_RESTRICTIONS_DISABLED = 165 "device_identifier_access_restrictions_disabled"; 166 167 /** 168 * Whether to show location indicators. 169 */ 170 public static final String PROPERTY_LOCATION_INDICATORS_ENABLED = "location_indicators_enabled"; 171 172 /** 173 * Whether to show location indicator settings in developer options. 174 */ 175 public static final String PROPERTY_LOCATION_INDICATOR_SETTINGS_ENABLED = 176 "location_indicator_settings_enabled"; 177 178 /** Whether or not app hibernation is enabled on the device **/ 179 public static final String PROPERTY_APP_HIBERNATION_ENABLED = "app_hibernation_enabled"; 180 181 /** Whether or not app hibernation targets apps that target a pre-S SDK **/ 182 public static final String PROPERTY_HIBERNATION_TARGETS_PRE_S_APPS = 183 "app_hibernation_targets_pre_s_apps"; 184 185 /** 186 * Whether or not Cloned Apps menu is available in Apps page. Default is false. 187 */ 188 public static final String PROPERTY_CLONED_APPS_ENABLED = "cloned_apps_enabled"; 189 190 /** 191 * Whether or not Delete All App Clones sub-menu is available in the Cloned Apps page. 192 * Default is false. 193 */ 194 public static final String PROPERTY_DELETE_ALL_APP_CLONES_ENABLED = 195 "delete_all_app_clones_enabled"; 196 197 /** 198 * Returns true if Monkey is running. 199 */ isMonkeyRunning()200 public static boolean isMonkeyRunning() { 201 return ActivityManager.isUserAMonkey(); 202 } 203 204 /** 205 * Enum for returning biometric status. 206 * {@link OK} no error detected when requesting mandatory biometrics authentication 207 * {@link NOT_ACTIVE} mandatory biometrics is not active 208 * {@link LOCKOUT} biometric sensors are in lockout mode 209 * {@link ERROR} corresponds to other errors 210 */ 211 public enum BiometricStatus {OK, NOT_ACTIVE, LOCKOUT, ERROR} 212 213 /** 214 * Returns whether the device is voice-capable (meaning, it is also a phone). 215 */ isVoiceCapable(Context context)216 public static boolean isVoiceCapable(Context context) { 217 final TelephonyManager telephony = 218 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); 219 return telephony != null && telephony.isVoiceCapable(); 220 } 221 222 /** 223 * Returns the WIFI IP Addresses, if any, taking into account IPv4 and IPv6 style addresses. 224 * @param context the application context 225 * @return the formatted and newline-separated IP addresses, or null if none. 226 */ getWifiIpAddresses(Context context)227 public static String getWifiIpAddresses(Context context) { 228 final WifiManager wifiManager = context.getSystemService(WifiManager.class); 229 final Network currentNetwork = wifiManager.getCurrentNetwork(); 230 if (currentNetwork != null) { 231 final ConnectivityManager cm = (ConnectivityManager) 232 context.getSystemService(Context.CONNECTIVITY_SERVICE); 233 final LinkProperties prop = cm.getLinkProperties(currentNetwork); 234 return formatIpAddresses(prop); 235 } 236 return null; 237 } 238 formatIpAddresses(LinkProperties prop)239 private static String formatIpAddresses(LinkProperties prop) { 240 if (prop == null) return null; 241 final Iterator<LinkAddress> iter = prop.getAllLinkAddresses().iterator(); 242 // If there are no entries, return null 243 if (!iter.hasNext()) return null; 244 // Concatenate all available addresses, comma separated 245 String addresses = ""; 246 while (iter.hasNext()) { 247 addresses += iter.next().getAddress().getHostAddress(); 248 if (iter.hasNext()) addresses += "\n"; 249 } 250 return addresses; 251 } 252 createLocaleFromString(String localeStr)253 public static Locale createLocaleFromString(String localeStr) { 254 // TODO: is there a better way to actually construct a locale that will match? 255 // The main problem is, on top of Java specs, locale.toString() and 256 // new Locale(locale.toString()).toString() do not return equal() strings in 257 // many cases, because the constructor takes the only string as the language 258 // code. So : new Locale("en", "US").toString() => "en_US" 259 // And : new Locale("en_US").toString() => "en_us" 260 if (null == localeStr) 261 return Locale.getDefault(); 262 final String[] brokenDownLocale = localeStr.split("_", 3); 263 // split may not return a 0-length array. 264 if (1 == brokenDownLocale.length) { 265 return new Locale(brokenDownLocale[0]); 266 } else if (2 == brokenDownLocale.length) { 267 return new Locale(brokenDownLocale[0], brokenDownLocale[1]); 268 } else { 269 return new Locale(brokenDownLocale[0], brokenDownLocale[1], brokenDownLocale[2]); 270 } 271 } 272 isBatteryPresent(Intent batteryChangedIntent)273 public static boolean isBatteryPresent(Intent batteryChangedIntent) { 274 return batteryChangedIntent.getBooleanExtra(BatteryManager.EXTRA_PRESENT, true); 275 } 276 277 /** 278 * Return true if battery is present. 279 */ isBatteryPresent(Context context)280 public static boolean isBatteryPresent(Context context) { 281 Intent batteryBroadcast = context.registerReceiver(null /* receiver */, 282 new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); 283 return isBatteryPresent(batteryBroadcast); 284 } 285 getBatteryPercentage(Intent batteryChangedIntent)286 public static String getBatteryPercentage(Intent batteryChangedIntent) { 287 return formatPercentage(getBatteryLevel(batteryChangedIntent)); 288 } 289 290 /** 291 * Prepare a custom preferences layout, moving padding to {@link ListView} 292 * when outside scrollbars are requested. Usually used to display 293 * {@link ListView} and {@link TabWidget} with correct padding. 294 */ prepareCustomPreferencesList( ViewGroup parent, View child, View list, boolean ignoreSidePadding)295 public static void prepareCustomPreferencesList( 296 ViewGroup parent, View child, View list, boolean ignoreSidePadding) { 297 final boolean movePadding = list.getScrollBarStyle() == View.SCROLLBARS_OUTSIDE_OVERLAY; 298 if (movePadding) { 299 final Resources res = list.getResources(); 300 final int paddingBottom = res.getDimensionPixelSize( 301 com.android.internal.R.dimen.preference_fragment_padding_bottom); 302 303 if (parent instanceof PreferenceFrameLayout) { 304 ((PreferenceFrameLayout.LayoutParams) child.getLayoutParams()).removeBorders = true; 305 } 306 list.setPaddingRelative(0 /* start */, 0 /* top */, 0 /* end */, paddingBottom); 307 } 308 } 309 forceCustomPadding(View view, boolean additive)310 public static void forceCustomPadding(View view, boolean additive) { 311 final Resources res = view.getResources(); 312 313 final int paddingStart = additive ? view.getPaddingStart() : 0; 314 final int paddingEnd = additive ? view.getPaddingEnd() : 0; 315 final int paddingBottom = res.getDimensionPixelSize( 316 com.android.internal.R.dimen.preference_fragment_padding_bottom); 317 318 view.setPaddingRelative(paddingStart, 0, paddingEnd, paddingBottom); 319 } 320 getMeProfileName(Context context, boolean full)321 public static String getMeProfileName(Context context, boolean full) { 322 if (full) { 323 return getProfileDisplayName(context); 324 } else { 325 return getShorterNameIfPossible(context); 326 } 327 } 328 getShorterNameIfPossible(Context context)329 private static String getShorterNameIfPossible(Context context) { 330 final String given = getLocalProfileGivenName(context); 331 return !TextUtils.isEmpty(given) ? given : getProfileDisplayName(context); 332 } 333 getLocalProfileGivenName(Context context)334 private static String getLocalProfileGivenName(Context context) { 335 final ContentResolver cr = context.getContentResolver(); 336 337 // Find the raw contact ID for the local ME profile raw contact. 338 final long localRowProfileId; 339 final Cursor localRawProfile = cr.query( 340 Profile.CONTENT_RAW_CONTACTS_URI, 341 new String[] {RawContacts._ID}, 342 RawContacts.ACCOUNT_TYPE + " IS NULL AND " + 343 RawContacts.ACCOUNT_NAME + " IS NULL", 344 null, null); 345 if (localRawProfile == null) return null; 346 347 try { 348 if (!localRawProfile.moveToFirst()) { 349 return null; 350 } 351 localRowProfileId = localRawProfile.getLong(0); 352 } finally { 353 localRawProfile.close(); 354 } 355 356 // Find the structured name for the raw contact. 357 final Cursor structuredName = cr.query( 358 Profile.CONTENT_URI.buildUpon().appendPath(Contacts.Data.CONTENT_DIRECTORY).build(), 359 new String[] {CommonDataKinds.StructuredName.GIVEN_NAME, 360 CommonDataKinds.StructuredName.FAMILY_NAME}, 361 Data.RAW_CONTACT_ID + "=" + localRowProfileId, 362 null, null); 363 if (structuredName == null) return null; 364 365 try { 366 if (!structuredName.moveToFirst()) { 367 return null; 368 } 369 String partialName = structuredName.getString(0); 370 if (TextUtils.isEmpty(partialName)) { 371 partialName = structuredName.getString(1); 372 } 373 return partialName; 374 } finally { 375 structuredName.close(); 376 } 377 } 378 getProfileDisplayName(Context context)379 private static final String getProfileDisplayName(Context context) { 380 final ContentResolver cr = context.getContentResolver(); 381 final Cursor profile = cr.query(Profile.CONTENT_URI, 382 new String[] {Profile.DISPLAY_NAME}, null, null, null); 383 if (profile == null) return null; 384 385 try { 386 if (!profile.moveToFirst()) { 387 return null; 388 } 389 return profile.getString(0); 390 } finally { 391 profile.close(); 392 } 393 } 394 hasMultipleUsers(Context context)395 public static boolean hasMultipleUsers(Context context) { 396 return context.getSystemService(UserManager.class) 397 .getUsers().size() > 1; 398 } 399 400 /** 401 * Returns the managed profile of the current user or {@code null} if none is found or a profile 402 * exists but it is disabled. 403 */ getManagedProfile(UserManager userManager)404 public static UserHandle getManagedProfile(UserManager userManager) { 405 final List<UserHandle> userProfiles = userManager.getUserProfiles(); 406 for (UserHandle profile : userProfiles) { 407 if (profile.getIdentifier() == userManager.getProcessUserId()) { 408 continue; 409 } 410 final UserInfo userInfo = userManager.getUserInfo(profile.getIdentifier()); 411 if (userInfo.isManagedProfile()) { 412 return profile; 413 } 414 } 415 return null; 416 } 417 418 /** 419 * Returns the profile of userType of the current user or {@code null} if none is found or a 420 * profile exists, but it is disabled. 421 */ 422 @Nullable getProfileOfType( @onNull UserManager userManager, @ProfileType int userType)423 public static UserHandle getProfileOfType( 424 @NonNull UserManager userManager, @ProfileType int userType) { 425 final List<UserHandle> userProfiles = userManager.getUserProfiles(); 426 String umUserType = getUmUserType(userType); 427 for (UserHandle profile : userProfiles) { 428 if (!com.android.settings.flags.Flags.utilsReturnUserHandleForCurrentUserId() 429 && profile.getIdentifier() == UserHandle.myUserId()) { 430 continue; 431 } 432 final UserInfo userInfo = userManager.getUserInfo(profile.getIdentifier()); 433 if (Objects.equals(umUserType, userInfo.userType)) { 434 return profile; 435 } 436 } 437 return null; 438 } 439 440 /** 441 * Returns true if a profile of specified userType exists. Note that it considers all profiles, 442 * including the disabled profiles and the parent user itself. 443 */ doesProfileOfTypeExists( @onNull UserManager userManager, @ProfileType int userType)444 public static boolean doesProfileOfTypeExists( 445 @NonNull UserManager userManager, @ProfileType int userType) { 446 final List<UserInfo> userProfiles = userManager.getProfiles(UserHandle.myUserId()); 447 String umUserType = getUmUserType(userType); 448 for (UserInfo profile : userProfiles) { 449 if (Objects.equals(umUserType, profile.userType)) { 450 return true; 451 } 452 } 453 return false; 454 } 455 getUmUserType(@rofileType int userType)456 private static String getUmUserType(@ProfileType int userType) throws IllegalArgumentException { 457 if (userType == ProfileType.WORK) { 458 return USER_TYPE_PROFILE_MANAGED; 459 } else if (userType == ProfileType.PRIVATE) { 460 return USER_TYPE_PROFILE_PRIVATE; 461 } else if (userType == ProfileType.PERSONAL) { 462 return USER_TYPE_FULL_SYSTEM; 463 } 464 throw new IllegalArgumentException("Cannot get user type for ALL types"); 465 } 466 467 /** 468 * Returns the managed profile of the current user or {@code null} if none is found. Unlike 469 * {@link #getManagedProfile} this method returns enabled and disabled managed profiles. 470 */ getManagedProfileWithDisabled(UserManager userManager)471 public static UserHandle getManagedProfileWithDisabled(UserManager userManager) { 472 return getManagedProfileWithDisabled(userManager, UserHandle.myUserId()); 473 } 474 475 /** 476 * Returns the managed profile of the given user or {@code null} if none is found. Unlike 477 * {@link #getManagedProfile} this method returns enabled and disabled managed profiles. 478 */ getManagedProfileWithDisabled(UserManager um, int parentUserId)479 private static UserHandle getManagedProfileWithDisabled(UserManager um, int parentUserId) { 480 final List<UserInfo> profiles = um.getProfiles(parentUserId); 481 final int count = profiles.size(); 482 for (int i = 0; i < count; i++) { 483 final UserInfo profile = profiles.get(i); 484 if (profile.isManagedProfile() 485 && profile.getUserHandle().getIdentifier() != parentUserId) { 486 return profile.getUserHandle(); 487 } 488 } 489 return null; 490 } 491 492 /** 493 * Retrieves the id for the given user's managed profile. 494 * Unlike {@link #getManagedProfile} this method returns enabled and disabled managed profiles. 495 * 496 * @return the managed profile id or UserHandle.USER_NULL if there is none. 497 */ getManagedProfileId(UserManager um, int parentUserId)498 public static int getManagedProfileId(UserManager um, int parentUserId) { 499 final UserHandle profile = getManagedProfileWithDisabled(um, parentUserId); 500 if (profile != null) { 501 return profile.getIdentifier(); 502 } 503 return UserHandle.USER_NULL; 504 } 505 506 /** 507 * Returns user ID of the user of specified type under the current context, throws 508 * IllegalStateException if it's not available. 509 */ getCurrentUserIdOfType( @onNull UserManager userManager, @ProfileType int userType)510 public static int getCurrentUserIdOfType( 511 @NonNull UserManager userManager, 512 @ProfileType int userType) throws IllegalStateException { 513 if (userType != ProfileType.PERSONAL) { 514 final UserHandle userHandle = getProfileOfType(userManager, userType); 515 if (userHandle == null) { 516 throw new IllegalStateException("User ID of requested profile type is not " 517 + "available."); 518 } 519 return userHandle.getIdentifier(); 520 } 521 return UserHandle.myUserId(); 522 } 523 524 /** 525 * Returns the target user for a Settings activity. 526 * <p> 527 * User would be retrieved in this order: 528 * <ul> 529 * <li> If this activity is launched from other user, return that user id. 530 * <li> If this is launched from the Settings app in same user, return the user contained as an 531 * extra in the arguments or intent extras. 532 * <li> Otherwise, return UserHandle.myUserId(). 533 * </ul> 534 * <p> 535 * Note: This is secure in the sense that it only returns a target user different to the current 536 * one if the app launching this activity is the Settings app itself, running in the same user 537 * or in one that is in the same profile group, or if the user id is provided by the system. 538 */ getSecureTargetUser(IBinder activityToken, UserManager um, @Nullable Bundle arguments, @Nullable Bundle intentExtras)539 public static UserHandle getSecureTargetUser(IBinder activityToken, 540 UserManager um, @Nullable Bundle arguments, @Nullable Bundle intentExtras) { 541 final UserHandle currentUser = new UserHandle(UserHandle.myUserId()); 542 final IActivityManager am = ActivityManager.getService(); 543 try { 544 final String launchedFromPackage = am.getLaunchedFromPackage(activityToken); 545 final boolean launchedFromSettingsApp = 546 SETTINGS_PACKAGE_NAME.equals(launchedFromPackage); 547 548 final UserHandle launchedFromUser = new UserHandle(UserHandle.getUserId( 549 am.getLaunchedFromUid(activityToken))); 550 if (launchedFromUser != null && !launchedFromUser.equals(currentUser)) { 551 // Check it's secure 552 if (isProfileOf(um, launchedFromUser)) { 553 return launchedFromUser; 554 } 555 } 556 final UserHandle extrasUser = getUserHandleFromBundle(intentExtras); 557 if (extrasUser != null && !extrasUser.equals(currentUser)) { 558 // Check it's secure 559 if (launchedFromSettingsApp && isProfileOf(um, extrasUser)) { 560 return extrasUser; 561 } 562 } 563 final UserHandle argumentsUser = getUserHandleFromBundle(arguments); 564 if (argumentsUser != null && !argumentsUser.equals(currentUser)) { 565 // Check it's secure 566 if (launchedFromSettingsApp && isProfileOf(um, argumentsUser)) { 567 return argumentsUser; 568 } 569 } 570 } catch (RemoteException e) { 571 // Should not happen 572 Log.v(TAG, "Could not talk to activity manager.", e); 573 } 574 return currentUser; 575 } 576 577 /** 578 * Lookup both {@link Intent#EXTRA_USER} and {@link Intent#EXTRA_USER_ID} in the bundle 579 * and return the {@link UserHandle} object. Return {@code null} if nothing is found. 580 */ getUserHandleFromBundle(Bundle bundle)581 private static @Nullable UserHandle getUserHandleFromBundle(Bundle bundle) { 582 if (bundle == null) { 583 return null; 584 } 585 final UserHandle user = bundle.getParcelable(EXTRA_USER); 586 if (user != null) { 587 return user; 588 } 589 final int userId = bundle.getInt(EXTRA_USER_ID, -1); 590 if (userId != -1) { 591 return UserHandle.of(userId); 592 } 593 return null; 594 } 595 596 /** 597 * Returns true if the user provided is in the same profiles group as the current user. 598 */ isProfileOf(UserManager um, UserHandle otherUser)599 private static boolean isProfileOf(UserManager um, UserHandle otherUser) { 600 if (um == null || otherUser == null) return false; 601 return (UserHandle.myUserId() == otherUser.getIdentifier()) 602 || um.getUserProfiles().contains(otherUser); 603 } 604 605 /** 606 * Queries for the UserInfo of a user. Returns null if the user doesn't exist (was removed). 607 * @param userManager Instance of UserManager 608 * @param checkUser The user to check the existence of. 609 * @return UserInfo of the user or null for non-existent user. 610 */ getExistingUser(UserManager userManager, UserHandle checkUser)611 public static UserInfo getExistingUser(UserManager userManager, UserHandle checkUser) { 612 final List<UserInfo> users = userManager.getAliveUsers(); 613 final int checkUserId = checkUser.getIdentifier(); 614 for (UserInfo user : users) { 615 if (user.id == checkUserId) { 616 return user; 617 } 618 } 619 return null; 620 } 621 inflateCategoryHeader(LayoutInflater inflater, ViewGroup parent)622 public static View inflateCategoryHeader(LayoutInflater inflater, ViewGroup parent) { 623 final TypedArray a = inflater.getContext().obtainStyledAttributes(null, 624 com.android.internal.R.styleable.Preference, 625 com.android.internal.R.attr.preferenceCategoryStyle, 0); 626 final int resId = a.getResourceId(com.android.internal.R.styleable.Preference_layout, 627 0); 628 a.recycle(); 629 return inflater.inflate(resId, parent, false); 630 } 631 632 /** Gets all the domains that the given package could handled. */ 633 @NonNull getHandledDomains(PackageManager pm, String packageName)634 public static Set<String> getHandledDomains(PackageManager pm, String packageName) { 635 final List<IntentFilterVerificationInfo> iviList = 636 pm.getIntentFilterVerifications(packageName); 637 final List<IntentFilter> filters = pm.getAllIntentFilters(packageName); 638 639 final ArraySet<String> result = new ArraySet<>(); 640 if (iviList != null && iviList.size() > 0) { 641 for (IntentFilterVerificationInfo ivi : iviList) { 642 result.addAll(ivi.getDomains()); 643 } 644 } 645 if (filters != null && filters.size() > 0) { 646 for (IntentFilter filter : filters) { 647 if (filter.hasCategory(Intent.CATEGORY_BROWSABLE) 648 && (filter.hasDataScheme(IntentFilter.SCHEME_HTTP) || 649 filter.hasDataScheme(IntentFilter.SCHEME_HTTPS))) { 650 result.addAll(filter.getHostsList()); 651 } 652 } 653 } 654 return result; 655 } 656 657 /** 658 * Returns the application info of the currently installed MDM package. 659 */ getAdminApplicationInfo(Context context, int profileId)660 public static ApplicationInfo getAdminApplicationInfo(Context context, int profileId) { 661 final DevicePolicyManager dpm = 662 (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE); 663 final ComponentName mdmPackage = dpm.getProfileOwnerAsUser(profileId); 664 if (mdmPackage == null) { 665 return null; 666 } 667 final String mdmPackageName = mdmPackage.getPackageName(); 668 try { 669 final IPackageManager ipm = AppGlobals.getPackageManager(); 670 final ApplicationInfo mdmApplicationInfo = 671 ipm.getApplicationInfo(mdmPackageName, 0, profileId); 672 return mdmApplicationInfo; 673 } catch (RemoteException e) { 674 Log.e(TAG, "Error while retrieving application info for package " + mdmPackageName 675 + ", userId " + profileId, e); 676 return null; 677 } 678 } 679 isBandwidthControlEnabled()680 public static boolean isBandwidthControlEnabled() { 681 final INetworkManagementService netManager = INetworkManagementService.Stub 682 .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE)); 683 try { 684 return netManager.isBandwidthControlEnabled(); 685 } catch (RemoteException e) { 686 return false; 687 } 688 } 689 690 /** 691 * Returns an accessible SpannableString. 692 * @param displayText the text to display 693 * @param accessibileText the text text-to-speech engines should read 694 */ createAccessibleSequence(CharSequence displayText, String accessibileText)695 public static SpannableString createAccessibleSequence(CharSequence displayText, 696 String accessibileText) { 697 final SpannableString str = new SpannableString(displayText); 698 str.setSpan(new TtsSpan.TextBuilder(accessibileText).build(), 0, 699 displayText.length(), 700 Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 701 return str; 702 } 703 704 /** 705 * Returns the user id present in the bundle with 706 * {@link Intent#EXTRA_USER_ID} if it belongs to the current user. 707 * 708 * @throws SecurityException if the given userId does not belong to the 709 * current user group. 710 */ getUserIdFromBundle(Context context, Bundle bundle)711 public static int getUserIdFromBundle(Context context, Bundle bundle) { 712 return getUserIdFromBundle(context, bundle, false); 713 } 714 715 /** 716 * Returns the user id present in the bundle with 717 * {@link Intent#EXTRA_USER_ID} if it belongs to the current user. 718 * 719 * @param isInternal indicating if the caller is "internal" to the system, 720 * meaning we're willing to trust extras like 721 * {@link ChooseLockSettingsHelper#EXTRA_KEY_ALLOW_ANY_USER}. 722 * @throws SecurityException if the given userId does not belong to the 723 * current user group. 724 */ getUserIdFromBundle(Context context, Bundle bundle, boolean isInternal)725 public static int getUserIdFromBundle(Context context, Bundle bundle, boolean isInternal) { 726 if (bundle == null) { 727 return getCredentialOwnerUserId(context); 728 } 729 final boolean allowAnyUser = isInternal 730 && bundle.getBoolean(ChooseLockSettingsHelper.EXTRA_KEY_ALLOW_ANY_USER, false); 731 final int userId = bundle.getInt(Intent.EXTRA_USER_ID, UserHandle.myUserId()); 732 if (userId == LockPatternUtils.USER_FRP) { 733 return allowAnyUser ? userId : checkUserOwnsFrpCredential(context, userId); 734 } 735 if (userId == LockPatternUtils.USER_REPAIR_MODE) { 736 enforceRepairModeActive(context); 737 // any users can exit repair mode 738 return userId; 739 } 740 return allowAnyUser ? userId : enforceSameOwner(context, userId); 741 } 742 743 /** 744 * Returns the given user id if the current user owns frp credential. 745 * 746 * @throws SecurityException if the current user do not own the frp credential. 747 */ 748 @VisibleForTesting checkUserOwnsFrpCredential(Context context, int userId)749 static int checkUserOwnsFrpCredential(Context context, int userId) { 750 final UserManager um = context.getSystemService(UserManager.class); 751 if (LockPatternUtils.userOwnsFrpCredential(context, 752 um.getUserInfo(UserHandle.myUserId()))) { 753 return userId; 754 } 755 throw new SecurityException("Current user id " + UserHandle.myUserId() 756 + " does not own frp credential."); 757 } 758 759 /** 760 * Throws {@link SecurityException} if repair mode is not active on the device. 761 */ enforceRepairModeActive(Context context)762 private static void enforceRepairModeActive(Context context) { 763 if (LockPatternUtils.isRepairModeActive(context)) { 764 return; 765 } 766 throw new SecurityException("Repair mode is not active on the device."); 767 } 768 769 /** 770 * Returns the given user id if it belongs to the current user. 771 * 772 * @throws SecurityException if the given userId does not belong to the current user group. 773 */ enforceSameOwner(Context context, int userId)774 public static int enforceSameOwner(Context context, int userId) { 775 final UserManager um = context.getSystemService(UserManager.class); 776 final int[] profileIds = um.getProfileIdsWithDisabled(UserHandle.myUserId()); 777 if (ArrayUtils.contains(profileIds, userId)) { 778 return userId; 779 } 780 throw new SecurityException("Given user id " + userId + " does not belong to user " 781 + UserHandle.myUserId()); 782 } 783 784 /** 785 * Returns the effective credential owner of the calling user. 786 */ getCredentialOwnerUserId(Context context)787 public static int getCredentialOwnerUserId(Context context) { 788 return getCredentialOwnerUserId(context, UserHandle.myUserId()); 789 } 790 791 /** 792 * Returns the user id of the credential owner of the given user id. 793 */ getCredentialOwnerUserId(Context context, int userId)794 public static int getCredentialOwnerUserId(Context context, int userId) { 795 final UserManager um = context.getSystemService(UserManager.class); 796 return um.getCredentialOwnerProfile(userId); 797 } 798 799 /** 800 * Returns the credential type of the given user id. 801 */ getCredentialType(Context context, int userId)802 public static @LockPatternUtils.CredentialType int getCredentialType(Context context, 803 int userId) { 804 final LockPatternUtils lpu = new LockPatternUtils(context); 805 return lpu.getCredentialTypeForUser(userId); 806 } 807 808 /** 809 * Returns the confirmation credential string of the given user id. 810 */ getConfirmCredentialStringForUser(@onNull Context context, int userId, @LockPatternUtils.CredentialType int credentialType)811 @Nullable public static String getConfirmCredentialStringForUser(@NonNull Context context, 812 int userId, @LockPatternUtils.CredentialType int credentialType) { 813 final int effectiveUserId = UserManager.get(context).getCredentialOwnerProfile(userId); 814 if (UserManager.get(context).isManagedProfile(effectiveUserId)) { 815 return null; 816 } 817 switch (credentialType) { 818 case LockPatternUtils.CREDENTIAL_TYPE_PIN: 819 return context.getString(R.string.lockpassword_confirm_your_pin_generic); 820 case LockPatternUtils.CREDENTIAL_TYPE_PATTERN: 821 return context.getString(R.string.lockpassword_confirm_your_pattern_generic); 822 case LockPatternUtils.CREDENTIAL_TYPE_PASSWORD: 823 return context.getString(R.string.lockpassword_confirm_your_password_generic); 824 } 825 return null; 826 } 827 828 private static final StringBuilder sBuilder = new StringBuilder(50); 829 private static final java.util.Formatter sFormatter = new java.util.Formatter( 830 sBuilder, Locale.getDefault()); 831 formatDateRange(Context context, long start, long end)832 public static String formatDateRange(Context context, long start, long end) { 833 final int flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_MONTH; 834 835 synchronized (sBuilder) { 836 sBuilder.setLength(0); 837 return DateUtils.formatDateRange(context, sFormatter, start, end, flags, null) 838 .toString(); 839 } 840 } 841 startQuietModeDialogIfNecessary(Context context, UserManager um, int userId)842 public static boolean startQuietModeDialogIfNecessary(Context context, UserManager um, 843 int userId) { 844 if (um.isQuietModeEnabled(UserHandle.of(userId))) { 845 final Intent intent = UnlaunchableAppActivity.createInQuietModeDialogIntent(userId); 846 context.startActivity(intent); 847 return true; 848 } 849 return false; 850 } 851 unlockWorkProfileIfNecessary(Context context, int userId)852 public static boolean unlockWorkProfileIfNecessary(Context context, int userId) { 853 try { 854 if (!ActivityManager.getService().isUserRunning(userId, 855 ActivityManager.FLAG_AND_LOCKED)) { 856 return false; 857 } 858 } catch (RemoteException e) { 859 return false; 860 } 861 if (!(new LockPatternUtils(context)).isSecure(userId)) { 862 return false; 863 } 864 return confirmWorkProfileCredentials(context, userId); 865 } 866 confirmWorkProfileCredentials(Context context, int userId)867 private static boolean confirmWorkProfileCredentials(Context context, int userId) { 868 final KeyguardManager km = (KeyguardManager) context.getSystemService( 869 Context.KEYGUARD_SERVICE); 870 final Intent unlockIntent = km.createConfirmDeviceCredentialIntent(null, null, userId); 871 if (unlockIntent != null) { 872 context.startActivity(unlockIntent); 873 return true; 874 } else { 875 return false; 876 } 877 } 878 879 /** Gets the application label of the given package name. */ 880 @Nullable getApplicationLabel(Context context, @NonNull String packageName)881 public static CharSequence getApplicationLabel(Context context, @NonNull String packageName) { 882 try { 883 final ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo( 884 packageName, 885 PackageManager.MATCH_DISABLED_COMPONENTS 886 | PackageManager.MATCH_ANY_USER); 887 return appInfo.loadLabel(context.getPackageManager()); 888 } catch (PackageManager.NameNotFoundException e) { 889 Log.e(TAG, "Unable to find info for package: " + packageName); 890 } 891 return null; 892 } 893 isPackageDirectBootAware(Context context, String packageName)894 public static boolean isPackageDirectBootAware(Context context, String packageName) { 895 try { 896 final ApplicationInfo ai = context.getPackageManager().getApplicationInfo( 897 packageName, 0); 898 return ai.isDirectBootAware() || ai.isPartiallyDirectBootAware(); 899 } catch (NameNotFoundException ignored) { 900 } 901 return false; 902 } 903 904 /** 905 * Returns a context created from the given context for the given user, or null if it fails 906 */ createPackageContextAsUser(Context context, int userId)907 public static Context createPackageContextAsUser(Context context, int userId) { 908 try { 909 return context.createPackageContextAsUser( 910 context.getPackageName(), 0 /* flags */, UserHandle.of(userId)); 911 } catch (PackageManager.NameNotFoundException e) { 912 Log.e(TAG, "Failed to create user context", e); 913 } 914 return null; 915 } 916 getFingerprintManagerOrNull(Context context)917 public static FingerprintManager getFingerprintManagerOrNull(Context context) { 918 if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) { 919 return (FingerprintManager) context.getSystemService(Context.FINGERPRINT_SERVICE); 920 } else { 921 return null; 922 } 923 } 924 hasFingerprintHardware(Context context)925 public static boolean hasFingerprintHardware(Context context) { 926 final FingerprintManager fingerprintManager = getFingerprintManagerOrNull(context); 927 return fingerprintManager != null && fingerprintManager.isHardwareDetected(); 928 } 929 getFaceManagerOrNull(Context context)930 public static FaceManager getFaceManagerOrNull(Context context) { 931 if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FACE)) { 932 return (FaceManager) context.getSystemService(Context.FACE_SERVICE); 933 } else { 934 return null; 935 } 936 } 937 hasFaceHardware(Context context)938 public static boolean hasFaceHardware(Context context) { 939 final FaceManager faceManager = getFaceManagerOrNull(context); 940 return faceManager != null && faceManager.isHardwareDetected(); 941 } 942 943 /** 944 * Return true if the device supports multiple biometrics authentications. 945 */ isMultipleBiometricsSupported(Context context)946 public static boolean isMultipleBiometricsSupported(Context context) { 947 return hasFingerprintHardware(context) && hasFaceHardware(context); 948 } 949 950 /** 951 * Return true if face is supported as Class 2 biometrics and above on the device, false 952 * otherwise. 953 */ isFaceNotConvenienceBiometric(@onNull Context context)954 public static boolean isFaceNotConvenienceBiometric(@NonNull Context context) { 955 FaceManager faceManager = getFaceManagerOrNull(context); 956 if (faceManager != null) { 957 final List<FaceSensorPropertiesInternal> faceProperties = 958 faceManager.getSensorPropertiesInternal(); 959 if (!faceProperties.isEmpty()) { 960 final FaceSensorPropertiesInternal props = faceProperties.get(0); 961 return props.sensorStrength != SensorProperties.STRENGTH_CONVENIENCE; 962 } 963 } 964 return false; 965 } 966 967 /** 968 * Launches an intent which may optionally have a user id defined. 969 * @param fragment Fragment to use to launch the activity. 970 * @param intent Intent to launch. 971 */ launchIntent(Fragment fragment, Intent intent)972 public static void launchIntent(Fragment fragment, Intent intent) { 973 try { 974 final int userId = intent.getIntExtra(Intent.EXTRA_USER_ID, -1); 975 976 if (userId == -1) { 977 fragment.startActivity(intent); 978 } else { 979 fragment.getActivity().startActivityAsUser(intent, new UserHandle(userId)); 980 } 981 } catch (ActivityNotFoundException e) { 982 Log.w(TAG, "No activity found for " + intent); 983 } 984 } 985 isDemoUser(Context context)986 public static boolean isDemoUser(Context context) { 987 return UserManager.isDeviceInDemoMode(context) 988 && context.getSystemService(UserManager.class).isDemoUser(); 989 } 990 getDeviceOwnerComponent(Context context)991 public static ComponentName getDeviceOwnerComponent(Context context) { 992 final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( 993 Context.DEVICE_POLICY_SERVICE); 994 return dpm.getDeviceOwnerComponentOnAnyUser(); 995 } 996 997 /** 998 * Returns if a given user is a profile of another user. 999 * @param user The user whose profiles wibe checked. 1000 * @param profile The (potential) profile. 1001 * @return if the profile is actually a profile 1002 */ isProfileOf(UserInfo user, UserInfo profile)1003 public static boolean isProfileOf(UserInfo user, UserInfo profile) { 1004 return user.id == profile.id || 1005 (user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID 1006 && user.profileGroupId == profile.profileGroupId); 1007 } 1008 1009 /** 1010 * Tries to initalize a volume with the given bundle. If it is a valid, private, and readable 1011 * {@link VolumeInfo}, it is returned. If it is not valid, null is returned. 1012 */ 1013 @Nullable maybeInitializeVolume(StorageManager sm, Bundle bundle)1014 public static VolumeInfo maybeInitializeVolume(StorageManager sm, Bundle bundle) { 1015 final String volumeId = bundle.getString(VolumeInfo.EXTRA_VOLUME_ID, 1016 VolumeInfo.ID_PRIVATE_INTERNAL); 1017 final VolumeInfo volume = sm.findVolumeById(volumeId); 1018 return isVolumeValid(volume) ? volume : null; 1019 } 1020 1021 /** 1022 * Return {@code true} if the supplied package is device owner or profile owner of at 1023 * least one user. 1024 * @param userManager used to get profile owner app for each user 1025 * @param devicePolicyManager used to check whether it is device owner app 1026 * @param packageName package to check about 1027 */ isProfileOrDeviceOwner(UserManager userManager, DevicePolicyManager devicePolicyManager, String packageName)1028 public static boolean isProfileOrDeviceOwner(UserManager userManager, 1029 DevicePolicyManager devicePolicyManager, String packageName) { 1030 final List<UserInfo> userInfos = userManager.getUsers(); 1031 if (devicePolicyManager.isDeviceOwnerAppOnAnyUser(packageName)) { 1032 return true; 1033 } 1034 for (int i = 0, size = userInfos.size(); i < size; i++) { 1035 final ComponentName cn = devicePolicyManager 1036 .getProfileOwnerAsUser(userInfos.get(i).id); 1037 if (cn != null && cn.getPackageName().equals(packageName)) { 1038 return true; 1039 } 1040 } 1041 return false; 1042 } 1043 1044 /** 1045 * Return {@code true} if the supplied package is the device owner or profile owner of a 1046 * given user. 1047 * 1048 * @param devicePolicyManager used to check whether it is device owner and profile owner app 1049 * @param packageName package to check about 1050 * @param userId the if of the relevant user 1051 */ isProfileOrDeviceOwner(DevicePolicyManager devicePolicyManager, String packageName, int userId)1052 public static boolean isProfileOrDeviceOwner(DevicePolicyManager devicePolicyManager, 1053 String packageName, int userId) { 1054 if ((devicePolicyManager.getDeviceOwnerUserId() == userId) 1055 && devicePolicyManager.isDeviceOwnerApp(packageName)) { 1056 return true; 1057 } 1058 final ComponentName cn = devicePolicyManager.getProfileOwnerAsUser(userId); 1059 if (cn != null && cn.getPackageName().equals(packageName)) { 1060 return true; 1061 } 1062 return false; 1063 } 1064 isVolumeValid(VolumeInfo volume)1065 private static boolean isVolumeValid(VolumeInfo volume) { 1066 return (volume != null) && (volume.getType() == VolumeInfo.TYPE_PRIVATE) 1067 && volume.isMountedReadable(); 1068 } 1069 setEditTextCursorPosition(EditText editText)1070 public static void setEditTextCursorPosition(EditText editText) { 1071 editText.setSelection(editText.getText().length()); 1072 } 1073 1074 /** 1075 * Gets the adaptive icon with a drawable that wrapped with an adaptive background using {@code 1076 * backgroundColor} if it is not a {@link AdaptiveIconDrawable} 1077 * 1078 * If the given {@code icon} is too big, it will be auto scaled down to to avoid crashing 1079 * Settings. 1080 */ getAdaptiveIcon(Context context, Drawable icon, @ColorInt int backgroundColor)1081 public static Drawable getAdaptiveIcon(Context context, Drawable icon, 1082 @ColorInt int backgroundColor) { 1083 Drawable adaptiveIcon = getSafeIcon(icon); 1084 1085 if (!(adaptiveIcon instanceof AdaptiveIconDrawable)) { 1086 adaptiveIcon = new AdaptiveIcon(context, adaptiveIcon); 1087 ((AdaptiveIcon) adaptiveIcon).setBackgroundColor(backgroundColor); 1088 } 1089 1090 return adaptiveIcon; 1091 } 1092 1093 /** 1094 * Gets the icon with a drawable that is scaled down to to avoid crashing Settings if it's too 1095 * big and not a {@link VectorDrawable}. 1096 */ getSafeIcon(Drawable icon)1097 public static Drawable getSafeIcon(Drawable icon) { 1098 Drawable safeIcon = icon; 1099 1100 if ((icon != null) && !(icon instanceof VectorDrawable)) { 1101 safeIcon = getSafeDrawable(icon, 1102 /* MAX_DRAWABLE_SIZE */ 600, /* MAX_DRAWABLE_SIZE */ 600); 1103 } 1104 1105 return safeIcon; 1106 } 1107 1108 /** 1109 * Gets a drawable with a limited size to avoid crashing Settings if it's too big. 1110 * 1111 * @param original original drawable, typically an app icon. 1112 * @param maxWidth maximum width, in pixels. 1113 * @param maxHeight maximum height, in pixels. 1114 */ getSafeDrawable(Drawable original, int maxWidth, int maxHeight)1115 private static Drawable getSafeDrawable(Drawable original, int maxWidth, int maxHeight) { 1116 final int actualWidth = original.getMinimumWidth(); 1117 final int actualHeight = original.getMinimumHeight(); 1118 1119 if (actualWidth <= maxWidth && actualHeight <= maxHeight) { 1120 return original; 1121 } 1122 1123 final float scaleWidth = ((float) maxWidth) / actualWidth; 1124 final float scaleHeight = ((float) maxHeight) / actualHeight; 1125 final float scale = Math.min(scaleWidth, scaleHeight); 1126 final int width = (int) (actualWidth * scale); 1127 final int height = (int) (actualHeight * scale); 1128 1129 final Bitmap bitmap; 1130 if (original instanceof BitmapDrawable) { 1131 bitmap = Bitmap.createScaledBitmap(((BitmapDrawable) original).getBitmap(), width, 1132 height, false); 1133 } else { 1134 bitmap = createBitmap(original, width, height); 1135 } 1136 return new BitmapDrawable(null, bitmap); 1137 } 1138 1139 /** 1140 * Create an Icon pointing to a drawable. 1141 */ createIconWithDrawable(Drawable drawable)1142 public static IconCompat createIconWithDrawable(Drawable drawable) { 1143 Bitmap bitmap; 1144 if (drawable instanceof BitmapDrawable) { 1145 bitmap = ((BitmapDrawable)drawable).getBitmap(); 1146 } else { 1147 final int width = drawable.getIntrinsicWidth(); 1148 final int height = drawable.getIntrinsicHeight(); 1149 bitmap = createBitmap(drawable, 1150 width > 0 ? width : 1, 1151 height > 0 ? height : 1); 1152 } 1153 return IconCompat.createWithBitmap(bitmap); 1154 } 1155 1156 /** 1157 * Creates a drawable with specified width and height. 1158 */ createBitmap(Drawable drawable, int width, int height)1159 public static Bitmap createBitmap(Drawable drawable, int width, int height) { 1160 final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); 1161 final Canvas canvas = new Canvas(bitmap); 1162 drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); 1163 drawable.draw(canvas); 1164 return bitmap; 1165 } 1166 1167 /** 1168 * Get the {@link Drawable} that represents the app icon 1169 */ getBadgedIcon(IconDrawableFactory iconDrawableFactory, PackageManager packageManager, String packageName, int userId)1170 public static Drawable getBadgedIcon(IconDrawableFactory iconDrawableFactory, 1171 PackageManager packageManager, String packageName, int userId) { 1172 try { 1173 final ApplicationInfo appInfo = packageManager.getApplicationInfoAsUser( 1174 packageName, PackageManager.GET_META_DATA, userId); 1175 return iconDrawableFactory.getBadgedIcon(appInfo, userId); 1176 } catch (PackageManager.NameNotFoundException e) { 1177 return packageManager.getDefaultActivityIcon(); 1178 } 1179 } 1180 1181 /** Returns true if the current package is installed & enabled. */ isPackageEnabled(Context context, String packageName)1182 public static boolean isPackageEnabled(Context context, String packageName) { 1183 try { 1184 return context.getPackageManager().getApplicationInfo(packageName, 0).enabled; 1185 } catch (Exception e) { 1186 // Expected, package is not installed or not enabled. 1187 return false; 1188 } 1189 } 1190 1191 /** Get {@link Resources} by subscription id if subscription id is valid. */ getResourcesForSubId(Context context, int subId)1192 public static Resources getResourcesForSubId(Context context, int subId) { 1193 if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) { 1194 return SubscriptionManager.getResourcesForSubId(context, subId); 1195 } else { 1196 return context.getResources(); 1197 } 1198 } 1199 1200 /** 1201 * Returns true if SYSTEM_ALERT_WINDOW permission is available. 1202 * Starting from Q, SYSTEM_ALERT_WINDOW is disabled on low ram phones. 1203 */ isSystemAlertWindowEnabled(Context context)1204 public static boolean isSystemAlertWindowEnabled(Context context) { 1205 // SYSTEM_ALERT_WINDOW is disabled on on low ram devices starting from Q 1206 ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); 1207 return !(am.isLowRamDevice() && (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)); 1208 } 1209 1210 /** 1211 * Adds a shadow appear/disappear animation to action bar scroll. 1212 * 1213 * <p/> 1214 * This method must be called after {@link Fragment#onCreate(Bundle)}. 1215 */ setActionBarShadowAnimation(Activity activity, Lifecycle lifecycle, View scrollView)1216 public static void setActionBarShadowAnimation(Activity activity, Lifecycle lifecycle, 1217 View scrollView) { 1218 if (activity == null) { 1219 Log.w(TAG, "No activity, cannot style actionbar."); 1220 return; 1221 } 1222 final ActionBar actionBar = activity.getActionBar(); 1223 if (actionBar == null) { 1224 Log.w(TAG, "No actionbar, cannot style actionbar."); 1225 return; 1226 } 1227 actionBar.setElevation(0); 1228 1229 if (lifecycle != null && scrollView != null) { 1230 ActionBarShadowController.attachToView(activity, lifecycle, scrollView); 1231 } 1232 } 1233 1234 /** 1235 * Return correct target fragment based on argument 1236 * 1237 * @param activity the activity target fragment will be launched. 1238 * @param fragmentName initial target fragment name. 1239 * @param args fragment launch arguments. 1240 */ getTargetFragment(Activity activity, String fragmentName, Bundle args)1241 public static Fragment getTargetFragment(Activity activity, String fragmentName, Bundle args) { 1242 Fragment f = null; 1243 final boolean isPersonal = args != null ? args.getInt(ProfileSelectFragment.EXTRA_PROFILE) 1244 == ProfileSelectFragment.ProfileType.PERSONAL : false; 1245 final boolean isWork = args != null ? args.getInt(ProfileSelectFragment.EXTRA_PROFILE) 1246 == ProfileSelectFragment.ProfileType.WORK : false; 1247 try { 1248 if (isNewTabNeeded(activity) 1249 && ProfileFragmentBridge.FRAGMENT_MAP.get(fragmentName) != null 1250 && !isWork && !isPersonal) { 1251 f = Fragment.instantiate(activity, 1252 ProfileFragmentBridge.FRAGMENT_MAP.get(fragmentName), args); 1253 } else { 1254 f = Fragment.instantiate(activity, fragmentName, args); 1255 } 1256 } catch (Exception e) { 1257 Log.e(TAG, "Unable to get target fragment", e); 1258 } 1259 return f; 1260 } 1261 1262 /** 1263 * Checks if a new tab is needed or not for any user profile associated with the context user. 1264 * 1265 * <p> Checks if any user has the property {@link UserProperties#SHOW_IN_SETTINGS_SEPARATE} set. 1266 */ isNewTabNeeded(Activity activity)1267 public static boolean isNewTabNeeded(Activity activity) { 1268 UserManager userManager = activity.getSystemService(UserManager.class); 1269 List<UserHandle> profiles = userManager.getUserProfiles(); 1270 for (UserHandle userHandle : profiles) { 1271 UserProperties userProperties = userManager.getUserProperties(userHandle); 1272 if (userProperties.getShowInSettings() == UserProperties.SHOW_IN_SETTINGS_SEPARATE) { 1273 if (Flags.allowPrivateProfile() 1274 && android.multiuser.Flags.enablePrivateSpaceFeatures() 1275 && userProperties.getShowInQuietMode() 1276 == UserProperties.SHOW_IN_QUIET_MODE_HIDDEN) { 1277 if (!userManager.isQuietModeEnabled(userHandle)) { 1278 return true; 1279 } else { 1280 continue; 1281 } 1282 } 1283 return true; 1284 } 1285 } 1286 return false; 1287 } 1288 1289 /** 1290 * Returns true if current binder uid is Settings Intelligence. 1291 */ isSettingsIntelligence(Context context)1292 public static boolean isSettingsIntelligence(Context context) { 1293 final int callingUid = Binder.getCallingUid(); 1294 final String callingPackage = context.getPackageManager().getPackagesForUid(callingUid)[0]; 1295 final boolean isSettingsIntelligence = TextUtils.equals(callingPackage, 1296 context.getString(R.string.config_settingsintelligence_package_name)); 1297 return isSettingsIntelligence; 1298 } 1299 1300 /** 1301 * Returns true if the night mode is enabled. 1302 */ isNightMode(Context context)1303 public static boolean isNightMode(Context context) { 1304 final int currentNightMode = 1305 context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; 1306 return currentNightMode == Configuration.UI_MODE_NIGHT_YES; 1307 } 1308 1309 /** 1310 * Returns a bitmap with rounded corner. 1311 * 1312 * @param context application context. 1313 * @param source bitmap to apply round corner. 1314 * @param cornerRadius corner radius value. 1315 */ convertCornerRadiusBitmap(@onNull Context context, @NonNull Bitmap source, @NonNull float cornerRadius)1316 public static Bitmap convertCornerRadiusBitmap(@NonNull Context context, 1317 @NonNull Bitmap source, @NonNull float cornerRadius) { 1318 final Bitmap roundedBitmap = Bitmap.createBitmap(source.getWidth(), source.getHeight(), 1319 Bitmap.Config.ARGB_8888); 1320 final RoundedBitmapDrawable drawable = 1321 RoundedBitmapDrawableFactory.create(context.getResources(), source); 1322 drawable.setAntiAlias(true); 1323 drawable.setCornerRadius(cornerRadius); 1324 final Canvas canvas = new Canvas(roundedBitmap); 1325 drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); 1326 drawable.draw(canvas); 1327 return roundedBitmap; 1328 } 1329 1330 /** 1331 * Returns the color of homepage preference icons. 1332 */ 1333 @ColorInt getHomepageIconColor(Context context)1334 public static int getHomepageIconColor(Context context) { 1335 return context.getColor(com.android.internal.R.color.materialColorOnSurface); 1336 } 1337 1338 /** 1339 * Returns the highlight color of homepage preference icons. 1340 */ 1341 @ColorInt getHomepageIconColorHighlight(Context context)1342 public static int getHomepageIconColorHighlight(Context context) { 1343 return context.getColor(R.color.accent_select_primary_text); 1344 } 1345 1346 /** 1347 * Returns user id of clone profile if present, else returns -1. 1348 */ getCloneUserId(Context context)1349 public static int getCloneUserId(Context context) { 1350 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE); 1351 for (UserHandle userHandle : userManager.getUserProfiles()) { 1352 if (userManager.getUserInfo(userHandle.getIdentifier()).isCloneProfile()) { 1353 return userHandle.getIdentifier(); 1354 } 1355 } 1356 return -1; 1357 } 1358 1359 /** 1360 * Returns if the current user is able to use Dreams. 1361 */ canCurrentUserDream(Context context)1362 public static boolean canCurrentUserDream(Context context) { 1363 final UserHandle mainUser = context.getSystemService(UserManager.class).getMainUser(); 1364 if (mainUser == null) { 1365 return false; 1366 } 1367 return context.createContextAsUser(mainUser, 0).getSystemService(UserManager.class) 1368 .isUserForeground(); 1369 } 1370 1371 /** 1372 * Returns if dreams are available to the current user. 1373 */ areDreamsAvailableToCurrentUser(Context context)1374 public static boolean areDreamsAvailableToCurrentUser(Context context) { 1375 final boolean dreamsSupported = context.getResources().getBoolean( 1376 com.android.internal.R.bool.config_dreamsSupported); 1377 final boolean dreamsOnlyEnabledForDockUser = context.getResources().getBoolean( 1378 com.android.internal.R.bool.config_dreamsOnlyEnabledForDockUser); 1379 return dreamsSupported && (!dreamsOnlyEnabledForDockUser || canCurrentUserDream(context)); 1380 } 1381 1382 1383 /** 1384 * Removes fingerprint templates enrolled for a given user. 1385 * 1386 * @param context application context. 1387 * @param userId the id of the relevant user 1388 */ removeEnrolledFingerprintForUser(Context context, int userId)1389 public static void removeEnrolledFingerprintForUser(Context context, int userId) { 1390 FingerprintManager fingerprintManager = getFingerprintManagerOrNull(context); 1391 if (fingerprintManager != null && fingerprintManager.hasEnrolledTemplates(userId)) { 1392 fingerprintManager.removeAll(userId, 1393 fingerprintManagerRemovalCallback(userId)); 1394 } 1395 } 1396 1397 /** 1398 * Removes face templates enrolled for a given user. 1399 * 1400 * @param context application context. 1401 * @param userId the id of the relevant user 1402 */ removeEnrolledFaceForUser(Context context, int userId)1403 public static void removeEnrolledFaceForUser(Context context, int userId) { 1404 FaceManager faceManager = getFaceManagerOrNull(context); 1405 if (faceManager != null && faceManager.hasEnrolledTemplates(userId)) { 1406 faceManager.removeAll(userId, faceManagerRemovalCallback(userId)); 1407 } 1408 } 1409 1410 /** 1411 * Returns true if the user should be hidden in Settings when it's in quiet mode. 1412 */ shouldHideUser( @onNull UserHandle userHandle, @NonNull UserManager userManager)1413 public static boolean shouldHideUser( 1414 @NonNull UserHandle userHandle, @NonNull UserManager userManager) { 1415 UserProperties userProperties = userManager.getUserProperties(userHandle); 1416 return userProperties.getShowInQuietMode() == UserProperties.SHOW_IN_QUIET_MODE_HIDDEN 1417 && userManager.isQuietModeEnabled(userHandle); 1418 } 1419 1420 /** 1421 * Returns true if the userId is a private profile, false otherwise. 1422 */ isPrivateProfile(int userId, @NonNull Context context)1423 public static boolean isPrivateProfile(int userId, @NonNull Context context) { 1424 final UserManager userManager = context.getSystemService(UserManager.class); 1425 UserInfo userInfo = userManager.getUserInfo(userId); 1426 return Flags.allowPrivateProfile() && android.multiuser.Flags.enablePrivateSpaceFeatures() 1427 && userInfo.isPrivateProfile(); 1428 } 1429 1430 /** 1431 * Enable new edge to edge feature. 1432 * 1433 * @param activity the Activity need to setup the edge to edge feature. 1434 */ setupEdgeToEdge(@onNull FragmentActivity activity)1435 public static void setupEdgeToEdge(@NonNull FragmentActivity activity) { 1436 ViewCompat.setOnApplyWindowInsetsListener(activity.findViewById(android.R.id.content), 1437 (v, windowInsets) -> { 1438 final Insets insets = windowInsets.getInsets( 1439 WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.ime() 1440 | WindowInsetsCompat.Type.displayCutout()); 1441 int newInsetsTop = activity.getWindow().getDecorView().getRootWindowInsets() 1442 .getInsets(WindowInsetsCompat.Type.statusBars() 1443 | WindowInsetsCompat.Type.captionBar()).top; 1444 1445 // Apply the insets paddings to the view. 1446 v.setPadding(insets.left, newInsetsTop, insets.right, insets.bottom); 1447 1448 // Return CONSUMED if you don't want the window insets to keep being 1449 // passed down to descendant views. 1450 return WindowInsetsCompat.CONSUMED; 1451 }); 1452 } 1453 faceManagerRemovalCallback(int userId)1454 private static FaceManager.RemovalCallback faceManagerRemovalCallback(int userId) { 1455 return new FaceManager.RemovalCallback() { 1456 @Override 1457 public void onRemovalError(@Nullable Face face, int errMsgId, CharSequence err) { 1458 Log.e(TAG, "Unable to remove face template for user " + userId + ", error: " + err); 1459 } 1460 1461 @Override 1462 public void onRemovalSucceeded(Face face, int remaining) { 1463 if (remaining == 0) { 1464 Log.d(TAG, "Enrolled face templates removed for user " + userId); 1465 } 1466 } 1467 }; 1468 } 1469 1470 private static FingerprintManager.RemovalCallback fingerprintManagerRemovalCallback( 1471 int userId) { 1472 return new FingerprintManager.RemovalCallback() { 1473 @Override 1474 public void onRemovalError(@Nullable Fingerprint fp, int errMsgId, CharSequence err) { 1475 Log.e(TAG, "Unable to remove fingerprint for user " + userId + " , error: " + err); 1476 } 1477 1478 @Override 1479 public void onRemovalSucceeded(Fingerprint fp, int remaining) { 1480 if (remaining == 0) { 1481 Log.d(TAG, "Enrolled fingerprints removed for user " + userId); 1482 } 1483 } 1484 }; 1485 } 1486 1487 /** 1488 * Disables the launcher icon and shortcut picker component for the Settings app corresponding 1489 * to the context user. 1490 */ 1491 public static void disableComponentsToHideSettings(@NonNull Context context, 1492 @NonNull PackageManager pm) { 1493 // Disable settings app launcher icon 1494 disableComponent(pm, new ComponentName(context, Settings.class)); 1495 1496 //Disable Shortcut picker 1497 disableComponent(pm, new ComponentName(context, Settings.CreateShortcutActivity.class)); 1498 } 1499 1500 /** 1501 * Request biometric authentication if all requirements for mandatory biometrics is satisfied. 1502 * 1503 * @param context of the corresponding activity/fragment 1504 * @param biometricsAuthenticationRequested if the activity/fragment has already requested for 1505 * biometric prompt 1506 * @param userId user id for the authentication request 1507 * @return biometric status when mandatory biometrics authentication is requested 1508 */ 1509 public static BiometricStatus requestBiometricAuthenticationForMandatoryBiometrics( 1510 @NonNull Context context, boolean biometricsAuthenticationRequested, int userId) { 1511 final BiometricManager biometricManager = context.getSystemService(BiometricManager.class); 1512 if (biometricManager == null) { 1513 Log.e(TAG, "Biometric Manager is null."); 1514 return BiometricStatus.NOT_ACTIVE; 1515 } 1516 if (android.hardware.biometrics.Flags.mandatoryBiometrics() 1517 && !biometricsAuthenticationRequested) { 1518 final UserManager userManager = context.getSystemService( 1519 UserManager.class); 1520 final int status = biometricManager.canAuthenticate(getEffectiveUserId( 1521 userManager, userId), BiometricManager.Authenticators.IDENTITY_CHECK); 1522 switch(status) { 1523 case BiometricManager.BIOMETRIC_SUCCESS: 1524 return BiometricStatus.OK; 1525 case BiometricManager.BIOMETRIC_ERROR_LOCKOUT: 1526 return BiometricStatus.LOCKOUT; 1527 case BiometricManager.BIOMETRIC_ERROR_IDENTITY_CHECK_NOT_ACTIVE: 1528 case BiometricManager.BIOMETRIC_ERROR_NOT_ENABLED_FOR_APPS: 1529 return BiometricStatus.NOT_ACTIVE; 1530 default: 1531 return BiometricStatus.ERROR; 1532 } 1533 } 1534 return BiometricStatus.NOT_ACTIVE; 1535 } 1536 1537 /** 1538 * Launch biometric prompt for mandatory biometrics. Call 1539 * {@link #requestBiometricAuthenticationForMandatoryBiometrics(Context, boolean, int)} 1540 * to check if all requirements for mandatory biometrics is satisfied 1541 * before launching biometric prompt. 1542 * 1543 * @param fragment corresponding fragment of the surface 1544 * @param requestCode for starting the new activity 1545 * @param userId user id for the authentication request 1546 * @param hideBackground if the background activity screen needs to be hidden 1547 */ 1548 public static void launchBiometricPromptForMandatoryBiometrics(@NonNull Fragment fragment, 1549 int requestCode, int userId, boolean hideBackground) { 1550 final UserManager userManager = (UserManager) fragment.getContext().getSystemService( 1551 UserManager.class); 1552 fragment.startActivityForResult(getIntentForBiometricAuthentication(fragment.getResources(), 1553 getEffectiveUserId(userManager, userId), hideBackground, null /* data */), 1554 requestCode); 1555 } 1556 1557 /** 1558 * Launch biometric prompt for mandatory biometrics. Call 1559 * {@link #requestBiometricAuthenticationForMandatoryBiometrics(Context, boolean, int)} 1560 * to check if all requirements for mandatory biometrics is satisfied 1561 * before launching biometric prompt. 1562 * 1563 * @param fragment corresponding fragment of the surface 1564 * @param requestCode for starting the new activity 1565 * @param userId user id for the authentication request 1566 * @param hideBackground if the background activity screen needs to be hidden 1567 * @param data additional info to returned to the activity after authentication 1568 * has ended 1569 */ 1570 public static void launchBiometricPromptForMandatoryBiometrics(@NonNull Fragment fragment, 1571 int requestCode, int userId, boolean hideBackground, @Nullable Intent data) { 1572 final UserManager userManager = (UserManager) fragment.getContext().getSystemService( 1573 UserManager.class); 1574 fragment.startActivityForResult(getIntentForBiometricAuthentication(fragment.getResources(), 1575 getEffectiveUserId(userManager, userId), hideBackground, data), requestCode); 1576 } 1577 1578 /** 1579 * Launch biometric prompt for mandatory biometrics. Call 1580 * {@link #requestBiometricAuthenticationForMandatoryBiometrics(Context, boolean, int)} 1581 * to check if all requirements for mandatory biometrics is satisfied 1582 * before launching biometric prompt. 1583 * 1584 * @param activity corresponding activity of the surface 1585 * @param requestCode for starting the new activity 1586 * @param userId user id for the authentication request 1587 * @param hideBackground if the background activity screen needs to be hidden 1588 */ 1589 public static void launchBiometricPromptForMandatoryBiometrics(@NonNull Activity activity, 1590 int requestCode, int userId, boolean hideBackground) { 1591 final UserManager userManager = activity.getSystemService(UserManager.class); 1592 activity.startActivityForResult(getIntentForBiometricAuthentication( 1593 activity.getResources(), getEffectiveUserId(userManager, userId), 1594 hideBackground, null /* data */), requestCode); 1595 } 1596 1597 private static int getEffectiveUserId(UserManager userManager, int userId) { 1598 if (userManager != null) { 1599 return userManager.getCredentialOwnerProfile(userId); 1600 } 1601 return userId; 1602 } 1603 1604 private static Intent getIntentForBiometricAuthentication(Resources resources, 1605 int effectiveUserId, boolean hideBackground, @Nullable Intent data) { 1606 final Intent intent = new Intent(); 1607 if (android.hardware.biometrics.Flags.mandatoryBiometrics()) { 1608 intent.putExtra(BIOMETRIC_PROMPT_AUTHENTICATORS, 1609 BiometricManager.Authenticators.IDENTITY_CHECK); 1610 } 1611 intent.putExtra(BIOMETRIC_PROMPT_NEGATIVE_BUTTON_TEXT, 1612 resources.getString(R.string.cancel)); 1613 intent.putExtra(KeyguardManager.EXTRA_DESCRIPTION, 1614 resources.getString(R.string.mandatory_biometrics_prompt_description)); 1615 intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_ALLOW_ANY_USER, true); 1616 intent.putExtra(EXTRA_USER_ID, effectiveUserId); 1617 intent.putExtra(BIOMETRIC_PROMPT_HIDE_BACKGROUND, hideBackground); 1618 intent.putExtra(EXTRA_DATA, data); 1619 intent.setClassName(SETTINGS_PACKAGE_NAME, 1620 ConfirmDeviceCredentialActivity.InternalActivity.class.getName()); 1621 return intent; 1622 } 1623 1624 private static void disableComponent(PackageManager pm, ComponentName componentName) { 1625 pm.setComponentEnabledSetting(componentName, 1626 PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP); 1627 } 1628 1629 /** 1630 * Returns {@code true} if the supplied package is a protected package. Otherwise, returns 1631 * {@code false}. 1632 * 1633 * @param context the context 1634 * @param packageName the package name 1635 */ 1636 public static boolean isProtectedPackage( 1637 @NonNull Context context, @NonNull String packageName) { 1638 final List<String> protectedPackageNames = Arrays.asList(context.getResources() 1639 .getStringArray(com.android.internal.R.array 1640 .config_biometric_protected_package_names)); 1641 return protectedPackageNames != null && protectedPackageNames.contains(packageName); 1642 } 1643 } 1644