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