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