1 package com.android.settingslib; 2 3 import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_USER_LABEL; 4 import static android.webkit.Flags.updateServiceV2; 5 6 import android.annotation.ColorInt; 7 import android.app.admin.DevicePolicyManager; 8 import android.content.Context; 9 import android.content.Intent; 10 import android.content.pm.ApplicationInfo; 11 import android.content.pm.PackageInfo; 12 import android.content.pm.PackageManager; 13 import android.content.pm.PackageManager.NameNotFoundException; 14 import android.content.pm.Signature; 15 import android.content.pm.UserInfo; 16 import android.content.res.ColorStateList; 17 import android.content.res.Resources; 18 import android.content.res.TypedArray; 19 import android.graphics.Bitmap; 20 import android.graphics.Canvas; 21 import android.graphics.Color; 22 import android.graphics.ColorFilter; 23 import android.graphics.ColorMatrix; 24 import android.graphics.ColorMatrixColorFilter; 25 import android.graphics.drawable.Drawable; 26 import android.hardware.usb.UsbManager; 27 import android.hardware.usb.UsbPort; 28 import android.hardware.usb.UsbPortStatus; 29 import android.hardware.usb.flags.Flags; 30 import android.icu.text.NumberFormat; 31 import android.location.LocationManager; 32 import android.media.AudioManager; 33 import android.net.NetworkCapabilities; 34 import android.net.TetheringManager; 35 import android.net.vcn.VcnTransportInfo; 36 import android.net.wifi.WifiInfo; 37 import android.os.BatteryManager; 38 import android.os.Build; 39 import android.os.RemoteException; 40 import android.os.SystemProperties; 41 import android.os.UserHandle; 42 import android.os.UserManager; 43 import android.print.PrintManager; 44 import android.provider.Settings; 45 import android.telephony.AccessNetworkConstants; 46 import android.telephony.NetworkRegistrationInfo; 47 import android.telephony.ServiceState; 48 import android.telephony.TelephonyManager; 49 import android.util.Log; 50 import android.webkit.IWebViewUpdateService; 51 import android.webkit.WebViewFactory; 52 import android.webkit.WebViewProviderInfo; 53 import android.webkit.WebViewUpdateManager; 54 55 import androidx.annotation.NonNull; 56 import androidx.annotation.Nullable; 57 import androidx.annotation.RequiresApi; 58 import androidx.core.graphics.drawable.RoundedBitmapDrawable; 59 import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory; 60 61 import com.android.internal.annotations.VisibleForTesting; 62 import com.android.internal.util.UserIcons; 63 import com.android.launcher3.icons.BaseIconFactory.IconOptions; 64 import com.android.launcher3.icons.IconFactory; 65 import com.android.launcher3.util.UserIconInfo; 66 import com.android.settingslib.drawable.UserIconDrawable; 67 import com.android.settingslib.fuelgauge.BatteryStatus; 68 import com.android.settingslib.fuelgauge.BatteryUtils; 69 import com.android.settingslib.utils.BuildCompatUtils; 70 71 import java.util.List; 72 73 public class Utils { 74 75 private static final String TAG = "Utils"; 76 77 public static final String INCOMPATIBLE_CHARGER_WARNING_DISABLED = 78 "incompatible_charger_warning_disabled"; 79 80 @VisibleForTesting 81 static final String STORAGE_MANAGER_ENABLED_PROPERTY = "ro.storage_manager.enabled"; 82 83 private static Signature[] sSystemSignature; 84 private static String sPermissionControllerPackageName; 85 private static String sServicesSystemSharedLibPackageName; 86 private static String sSharedSystemSharedLibPackageName; 87 private static String sDefaultWebViewPackageName; 88 89 static final int[] WIFI_PIE = { 90 com.android.internal.R.drawable.ic_wifi_signal_0, 91 com.android.internal.R.drawable.ic_wifi_signal_1, 92 com.android.internal.R.drawable.ic_wifi_signal_2, 93 com.android.internal.R.drawable.ic_wifi_signal_3, 94 com.android.internal.R.drawable.ic_wifi_signal_4 95 }; 96 97 static final int[] SHOW_X_WIFI_PIE = { 98 R.drawable.ic_show_x_wifi_signal_0, 99 R.drawable.ic_show_x_wifi_signal_1, 100 R.drawable.ic_show_x_wifi_signal_2, 101 R.drawable.ic_show_x_wifi_signal_3, 102 R.drawable.ic_show_x_wifi_signal_4 103 }; 104 105 /** Update the location enable state. */ updateLocationEnabled( @onNull Context context, boolean enabled, int userId, int source)106 public static void updateLocationEnabled( 107 @NonNull Context context, boolean enabled, int userId, int source) { 108 Settings.Secure.putIntForUser( 109 context.getContentResolver(), Settings.Secure.LOCATION_CHANGER, source, userId); 110 111 LocationManager locationManager = context.getSystemService(LocationManager.class); 112 locationManager.setLocationEnabledForUser(enabled, UserHandle.of(userId)); 113 } 114 115 /** 116 * Return string resource that best describes combination of tethering options available on this 117 * device. 118 */ getTetheringLabel(TetheringManager tm)119 public static int getTetheringLabel(TetheringManager tm) { 120 String[] usbRegexs = tm.getTetherableUsbRegexs(); 121 String[] wifiRegexs = tm.getTetherableWifiRegexs(); 122 String[] bluetoothRegexs = tm.getTetherableBluetoothRegexs(); 123 124 boolean usbAvailable = usbRegexs.length != 0; 125 boolean wifiAvailable = wifiRegexs.length != 0; 126 boolean bluetoothAvailable = bluetoothRegexs.length != 0; 127 128 if (wifiAvailable && usbAvailable && bluetoothAvailable) { 129 return R.string.tether_settings_title_all; 130 } else if (wifiAvailable && usbAvailable) { 131 return R.string.tether_settings_title_all; 132 } else if (wifiAvailable && bluetoothAvailable) { 133 return R.string.tether_settings_title_all; 134 } else if (wifiAvailable) { 135 return R.string.tether_settings_title_wifi; 136 } else if (usbAvailable && bluetoothAvailable) { 137 return R.string.tether_settings_title_usb_bluetooth; 138 } else if (usbAvailable) { 139 return R.string.tether_settings_title_usb; 140 } else { 141 return R.string.tether_settings_title_bluetooth; 142 } 143 } 144 145 /** Returns a label for the user, in the form of "User: user name" or "Work profile". */ getUserLabel(Context context, UserInfo info)146 public static String getUserLabel(Context context, UserInfo info) { 147 String name = info != null ? info.name : null; 148 if (info.isManagedProfile()) { 149 // We use predefined values for managed profiles 150 return BuildCompatUtils.isAtLeastT() 151 ? getUpdatableManagedUserTitle(context) 152 : context.getString(R.string.managed_user_title); 153 } else if (info.isGuest()) { 154 name = context.getString(com.android.internal.R.string.guest_name); 155 } 156 if (name == null && info != null) { 157 name = Integer.toString(info.id); 158 } else if (info == null) { 159 name = context.getString(R.string.unknown); 160 } 161 return context.getResources().getString(R.string.running_process_item_user_label, name); 162 } 163 164 @RequiresApi(Build.VERSION_CODES.TIRAMISU) getUpdatableManagedUserTitle(Context context)165 private static String getUpdatableManagedUserTitle(Context context) { 166 return context.getSystemService(DevicePolicyManager.class) 167 .getResources() 168 .getString( 169 WORK_PROFILE_USER_LABEL, 170 () -> context.getString(R.string.managed_user_title)); 171 } 172 173 /** Returns a circular icon for a user. */ getUserIcon(Context context, UserManager um, UserInfo user)174 public static Drawable getUserIcon(Context context, UserManager um, UserInfo user) { 175 final int iconSize = UserIconDrawable.getDefaultSize(context); 176 if (user.isManagedProfile()) { 177 Drawable drawable = UserIconDrawable.getManagedUserDrawable(context); 178 drawable.setBounds(0, 0, iconSize, iconSize); 179 return drawable; 180 } 181 if (user.iconPath != null) { 182 Bitmap icon = um.getUserIcon(user.id); 183 if (icon != null) { 184 return new UserIconDrawable(iconSize).setIcon(icon).bake(); 185 } 186 } 187 return new UserIconDrawable(iconSize) 188 .setIconDrawable( 189 UserIcons.getDefaultUserIcon( 190 context.getResources(), user.id, /* light= */ false)) 191 .bake(); 192 } 193 194 /** Formats a double from 0.0..100.0 with an option to round */ formatPercentage(double percentage, boolean round)195 public static String formatPercentage(double percentage, boolean round) { 196 final int localPercentage = round ? Math.round((float) percentage) : (int) percentage; 197 return formatPercentage(localPercentage); 198 } 199 200 /** Formats the ratio of amount/total as a percentage. */ formatPercentage(long amount, long total)201 public static String formatPercentage(long amount, long total) { 202 return formatPercentage(((double) amount) / total); 203 } 204 205 /** Formats an integer from 0..100 as a percentage. */ formatPercentage(int percentage)206 public static String formatPercentage(int percentage) { 207 return formatPercentage(((double) percentage) / 100.0); 208 } 209 210 /** Formats a double from 0.0..1.0 as a percentage. */ formatPercentage(double percentage)211 public static String formatPercentage(double percentage) { 212 return NumberFormat.getPercentInstance().format(percentage); 213 } 214 getBatteryLevel(Intent batteryChangedIntent)215 public static int getBatteryLevel(Intent batteryChangedIntent) { 216 int level = batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0); 217 int scale = batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_SCALE, 100); 218 return (level * 100) / scale; 219 } 220 221 /** 222 * Get battery status string 223 * 224 * @param context the context 225 * @param batteryChangedIntent battery broadcast intent received from {@link 226 * Intent.ACTION_BATTERY_CHANGED}. 227 * @param compactStatus to present compact battery charging string if {@code true} 228 * @return battery status string 229 */ 230 @NonNull getBatteryStatus( @onNull Context context, @NonNull Intent batteryChangedIntent, boolean compactStatus)231 public static String getBatteryStatus( 232 @NonNull Context context, @NonNull Intent batteryChangedIntent, boolean compactStatus) { 233 final int status = 234 batteryChangedIntent.getIntExtra( 235 BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_UNKNOWN); 236 final Resources res = context.getResources(); 237 238 String statusString = res.getString(R.string.battery_info_status_unknown); 239 final BatteryStatus batteryStatus = new BatteryStatus(batteryChangedIntent); 240 241 if (batteryStatus.isCharged()) { 242 statusString = 243 res.getString( 244 compactStatus 245 ? R.string.battery_info_status_full_charged 246 : R.string.battery_info_status_full); 247 } else { 248 if (status == BatteryManager.BATTERY_STATUS_CHARGING) { 249 if (compactStatus) { 250 statusString = getRegularChargingStatusString(res); 251 } else if (batteryStatus.isPluggedInWired()) { 252 switch (batteryStatus.getChargingSpeed(context)) { 253 case BatteryStatus.CHARGING_FAST: 254 statusString = getFastChargingStatusString(res); 255 break; 256 case BatteryStatus.CHARGING_SLOWLY: 257 statusString = getSlowChargingStatusString(res); 258 break; 259 default: 260 statusString = getRegularChargingStatusString(res); 261 break; 262 } 263 } else if (batteryStatus.isPluggedInDock()) { 264 statusString = getDockChargingStatusString(res); 265 } else { 266 statusString = getWirelessChargingStatusString(res); 267 } 268 } else if (status == BatteryManager.BATTERY_STATUS_DISCHARGING) { 269 statusString = res.getString(R.string.battery_info_status_discharging); 270 } else if (status == BatteryManager.BATTERY_STATUS_NOT_CHARGING) { 271 statusString = res.getString(R.string.battery_info_status_not_charging); 272 } 273 } 274 275 return statusString; 276 } 277 getFastChargingStatusString(Resources res)278 private static String getFastChargingStatusString(Resources res) { 279 return res.getString( 280 BatteryUtils.isChargingStringV2Enabled() 281 ? R.string.battery_info_status_charging_fast_v2 282 : R.string.battery_info_status_charging_fast); 283 } 284 getSlowChargingStatusString(Resources res)285 private static String getSlowChargingStatusString(Resources res) { 286 return res.getString( 287 BatteryUtils.isChargingStringV2Enabled() 288 ? R.string.battery_info_status_charging_v2 289 : R.string.battery_info_status_charging_slow); 290 } 291 getRegularChargingStatusString(Resources res)292 private static String getRegularChargingStatusString(Resources res) { 293 return res.getString( 294 BatteryUtils.isChargingStringV2Enabled() 295 ? R.string.battery_info_status_charging_v2 296 : R.string.battery_info_status_charging); 297 } 298 getWirelessChargingStatusString(Resources res)299 private static String getWirelessChargingStatusString(Resources res) { 300 return res.getString( 301 BatteryUtils.isChargingStringV2Enabled() 302 ? R.string.battery_info_status_charging_v2 303 : R.string.battery_info_status_charging_wireless); 304 } 305 getDockChargingStatusString(Resources res)306 private static String getDockChargingStatusString(Resources res) { 307 return res.getString( 308 BatteryUtils.isChargingStringV2Enabled() 309 ? R.string.battery_info_status_charging_v2 310 : R.string.battery_info_status_charging_dock); 311 } 312 getColorAccent(Context context)313 public static ColorStateList getColorAccent(Context context) { 314 return getColorAttr(context, android.R.attr.colorAccent); 315 } 316 getColorError(Context context)317 public static ColorStateList getColorError(Context context) { 318 return getColorAttr(context, android.R.attr.colorError); 319 } 320 321 @ColorInt getColorAccentDefaultColor(Context context)322 public static int getColorAccentDefaultColor(Context context) { 323 return getColorAttrDefaultColor(context, android.R.attr.colorAccent); 324 } 325 326 @ColorInt getColorErrorDefaultColor(Context context)327 public static int getColorErrorDefaultColor(Context context) { 328 return getColorAttrDefaultColor(context, android.R.attr.colorError); 329 } 330 331 @ColorInt getColorStateListDefaultColor(Context context, int resId)332 public static int getColorStateListDefaultColor(Context context, int resId) { 333 final ColorStateList list = 334 context.getResources().getColorStateList(resId, context.getTheme()); 335 return list.getDefaultColor(); 336 } 337 338 /** 339 * This method computes disabled color from normal color 340 * 341 * @param context the context 342 * @param inputColor normal color. 343 * @return disabled color. 344 */ 345 @ColorInt getDisabled(Context context, int inputColor)346 public static int getDisabled(Context context, int inputColor) { 347 return applyAlphaAttr(context, android.R.attr.disabledAlpha, inputColor); 348 } 349 350 @ColorInt applyAlphaAttr(Context context, int attr, int inputColor)351 public static int applyAlphaAttr(Context context, int attr, int inputColor) { 352 TypedArray ta = context.obtainStyledAttributes(new int[] {attr}); 353 float alpha = ta.getFloat(0, 0); 354 ta.recycle(); 355 return applyAlpha(alpha, inputColor); 356 } 357 358 @ColorInt applyAlpha(float alpha, int inputColor)359 public static int applyAlpha(float alpha, int inputColor) { 360 alpha *= Color.alpha(inputColor); 361 return Color.argb( 362 (int) (alpha), 363 Color.red(inputColor), 364 Color.green(inputColor), 365 Color.blue(inputColor)); 366 } 367 368 @ColorInt getColorAttrDefaultColor(Context context, int attr)369 public static int getColorAttrDefaultColor(Context context, int attr) { 370 return getColorAttrDefaultColor(context, attr, 0); 371 } 372 373 /** Get color styled attribute {@code attr}, default to {@code defValue} if not found. */ 374 @ColorInt getColorAttrDefaultColor(Context context, int attr, @ColorInt int defValue)375 public static int getColorAttrDefaultColor(Context context, int attr, @ColorInt int defValue) { 376 TypedArray ta = context.obtainStyledAttributes(new int[] {attr}); 377 @ColorInt int colorAccent = ta.getColor(0, defValue); 378 ta.recycle(); 379 return colorAccent; 380 } 381 getColorAttr(Context context, int attr)382 public static ColorStateList getColorAttr(Context context, int attr) { 383 TypedArray ta = context.obtainStyledAttributes(new int[] {attr}); 384 ColorStateList stateList = null; 385 try { 386 stateList = ta.getColorStateList(0); 387 } finally { 388 ta.recycle(); 389 } 390 return stateList; 391 } 392 getThemeAttr(Context context, int attr)393 public static int getThemeAttr(Context context, int attr) { 394 return getThemeAttr(context, attr, 0); 395 } 396 getThemeAttr(Context context, int attr, int defaultValue)397 public static int getThemeAttr(Context context, int attr, int defaultValue) { 398 TypedArray ta = context.obtainStyledAttributes(new int[] {attr}); 399 int theme = ta.getResourceId(0, defaultValue); 400 ta.recycle(); 401 return theme; 402 } 403 getDrawable(Context context, int attr)404 public static Drawable getDrawable(Context context, int attr) { 405 TypedArray ta = context.obtainStyledAttributes(new int[] {attr}); 406 Drawable drawable = ta.getDrawable(0); 407 ta.recycle(); 408 return drawable; 409 } 410 411 /** 412 * Create a color matrix suitable for a ColorMatrixColorFilter that modifies only the color but 413 * preserves the alpha for a given drawable 414 * 415 * @return a color matrix that uses the source alpha and given color 416 */ getAlphaInvariantColorMatrixForColor(@olorInt int color)417 public static ColorMatrix getAlphaInvariantColorMatrixForColor(@ColorInt int color) { 418 int r = Color.red(color); 419 int g = Color.green(color); 420 int b = Color.blue(color); 421 422 ColorMatrix cm = 423 new ColorMatrix( 424 new float[] { 425 0, 0, 0, 0, r, 426 0, 0, 0, 0, g, 427 0, 0, 0, 0, b, 428 0, 0, 0, 1, 0 429 }); 430 431 return cm; 432 } 433 434 /** 435 * Create a ColorMatrixColorFilter to tint a drawable but retain its alpha characteristics 436 * 437 * @return a ColorMatrixColorFilter which changes the color of the output but is invariant on 438 * the source alpha 439 */ getAlphaInvariantColorFilterForColor(@olorInt int color)440 public static ColorFilter getAlphaInvariantColorFilterForColor(@ColorInt int color) { 441 return new ColorMatrixColorFilter(getAlphaInvariantColorMatrixForColor(color)); 442 } 443 444 /** 445 * Determine whether a package is a "system package", in which case certain things (like 446 * disabling notifications or disabling the package altogether) should be disallowed. 447 * 448 * <p>Note: This function is just for UI treatment, and should not be used for security 449 * purposes. 450 * 451 * @deprecated Use {@link ApplicationInfo#isSignedWithPlatformKey()} and {@link 452 * #isEssentialPackage} instead. 453 */ 454 @Deprecated isSystemPackage(Resources resources, PackageManager pm, PackageInfo pkg)455 public static boolean isSystemPackage(Resources resources, PackageManager pm, PackageInfo pkg) { 456 if (sSystemSignature == null) { 457 sSystemSignature = new Signature[] {getSystemSignature(pm)}; 458 } 459 return (sSystemSignature[0] != null && sSystemSignature[0].equals(getFirstSignature(pkg))) 460 || isEssentialPackage(resources, pm, pkg.packageName); 461 } 462 getFirstSignature(PackageInfo pkg)463 private static Signature getFirstSignature(PackageInfo pkg) { 464 if (pkg != null && pkg.signatures != null && pkg.signatures.length > 0) { 465 return pkg.signatures[0]; 466 } 467 return null; 468 } 469 getSystemSignature(PackageManager pm)470 private static Signature getSystemSignature(PackageManager pm) { 471 try { 472 final PackageInfo sys = pm.getPackageInfo("android", PackageManager.GET_SIGNATURES); 473 return getFirstSignature(sys); 474 } catch (NameNotFoundException e) { 475 } 476 return null; 477 } 478 479 /** 480 * Determine whether a package is a "essential package". 481 * 482 * <p>In which case certain things (like disabling the package) should be disallowed. 483 */ isEssentialPackage( Resources resources, PackageManager pm, String packageName)484 public static boolean isEssentialPackage( 485 Resources resources, PackageManager pm, String packageName) { 486 if (sPermissionControllerPackageName == null) { 487 sPermissionControllerPackageName = pm.getPermissionControllerPackageName(); 488 } 489 if (sServicesSystemSharedLibPackageName == null) { 490 sServicesSystemSharedLibPackageName = pm.getServicesSystemSharedLibraryPackageName(); 491 } 492 if (sSharedSystemSharedLibPackageName == null) { 493 sSharedSystemSharedLibPackageName = pm.getSharedSystemSharedLibraryPackageName(); 494 } 495 return packageName.equals(sPermissionControllerPackageName) 496 || packageName.equals(sServicesSystemSharedLibPackageName) 497 || packageName.equals(sSharedSystemSharedLibPackageName) 498 || packageName.equals(PrintManager.PRINT_SPOOLER_PACKAGE_NAME) 499 || (updateServiceV2() && packageName.equals(getDefaultWebViewPackageName())) 500 || isDeviceProvisioningPackage(resources, packageName); 501 } 502 503 /** 504 * Returns {@code true} if the supplied package is the device provisioning app. Otherwise, 505 * returns {@code false}. 506 */ isDeviceProvisioningPackage(Resources resources, String packageName)507 public static boolean isDeviceProvisioningPackage(Resources resources, String packageName) { 508 String deviceProvisioningPackage = 509 resources.getString(com.android.internal.R.string.config_deviceProvisioningPackage); 510 return deviceProvisioningPackage != null && deviceProvisioningPackage.equals(packageName); 511 } 512 513 /** Fetch the package name of the default WebView provider. */ 514 @Nullable getDefaultWebViewPackageName()515 private static String getDefaultWebViewPackageName() { 516 if (sDefaultWebViewPackageName != null) { 517 return sDefaultWebViewPackageName; 518 } 519 520 WebViewProviderInfo provider = null; 521 522 if (android.webkit.Flags.updateServiceIpcWrapper()) { 523 WebViewUpdateManager manager = WebViewUpdateManager.getInstance(); 524 if (manager != null) { 525 provider = manager.getDefaultWebViewPackage(); 526 } 527 } else { 528 try { 529 IWebViewUpdateService service = WebViewFactory.getUpdateService(); 530 if (service != null) { 531 provider = service.getDefaultWebViewPackage(); 532 } 533 } catch (RemoteException e) { 534 Log.e(TAG, "RemoteException when trying to fetch default WebView package Name", e); 535 } 536 } 537 538 if (provider != null) { 539 sDefaultWebViewPackageName = provider.packageName; 540 } 541 return sDefaultWebViewPackageName; 542 } 543 544 /** 545 * Returns the Wifi icon resource for a given RSSI level. 546 * 547 * @param level The number of bars to show (0-4) 548 * @throws IllegalArgumentException if an invalid RSSI level is given. 549 */ getWifiIconResource(int level)550 public static int getWifiIconResource(int level) { 551 return getWifiIconResource(false /* showX */, level); 552 } 553 554 /** 555 * Returns the Wifi icon resource for a given RSSI level. 556 * 557 * @param showX True if a connected Wi-Fi network has the problem which should show Pie+x signal 558 * icon to users. 559 * @param level The number of bars to show (0-4) 560 * @throws IllegalArgumentException if an invalid RSSI level is given. 561 */ getWifiIconResource(boolean showX, int level)562 public static int getWifiIconResource(boolean showX, int level) { 563 if (level < 0 || level >= WIFI_PIE.length) { 564 throw new IllegalArgumentException("No Wifi icon found for level: " + level); 565 } 566 return showX ? SHOW_X_WIFI_PIE[level] : WIFI_PIE[level]; 567 } 568 getDefaultStorageManagerDaysToRetain(Resources resources)569 public static int getDefaultStorageManagerDaysToRetain(Resources resources) { 570 int defaultDays = Settings.Secure.AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN_DEFAULT; 571 try { 572 defaultDays = 573 resources.getInteger( 574 com.android.internal.R.integer 575 .config_storageManagerDaystoRetainDefault); 576 } catch (Resources.NotFoundException e) { 577 // We are likely in a test environment. 578 } 579 return defaultDays; 580 } 581 isWifiOnly(Context context)582 public static boolean isWifiOnly(Context context) { 583 return !context.getSystemService(TelephonyManager.class).isDataCapable(); 584 } 585 586 /** Returns if the automatic storage management feature is turned on or not. */ isStorageManagerEnabled(Context context)587 public static boolean isStorageManagerEnabled(Context context) { 588 boolean isDefaultOn; 589 try { 590 isDefaultOn = SystemProperties.getBoolean(STORAGE_MANAGER_ENABLED_PROPERTY, false); 591 } catch (Resources.NotFoundException e) { 592 isDefaultOn = false; 593 } 594 return Settings.Secure.getInt( 595 context.getContentResolver(), 596 Settings.Secure.AUTOMATIC_STORAGE_MANAGER_ENABLED, 597 isDefaultOn ? 1 : 0) 598 != 0; 599 } 600 601 /** get that {@link AudioManager#getMode()} is in ringing/call/communication(VoIP) status. */ isAudioModeOngoingCall(Context context)602 public static boolean isAudioModeOngoingCall(Context context) { 603 final AudioManager audioManager = context.getSystemService(AudioManager.class); 604 final int audioMode = audioManager.getMode(); 605 return audioMode == AudioManager.MODE_RINGTONE 606 || audioMode == AudioManager.MODE_IN_CALL 607 || audioMode == AudioManager.MODE_IN_COMMUNICATION; 608 } 609 610 /** 611 * Return the service state is in-service or not. To make behavior consistent with SystemUI and 612 * Settings/AboutPhone/SIM status UI 613 * 614 * @param serviceState Service state. {@link ServiceState} 615 */ isInService(ServiceState serviceState)616 public static boolean isInService(ServiceState serviceState) { 617 if (serviceState == null) { 618 return false; 619 } 620 int state = getCombinedServiceState(serviceState); 621 if (state == ServiceState.STATE_POWER_OFF 622 || state == ServiceState.STATE_OUT_OF_SERVICE 623 || state == ServiceState.STATE_EMERGENCY_ONLY) { 624 return false; 625 } else { 626 return true; 627 } 628 } 629 630 /** 631 * Return the combined service state. To make behavior consistent with SystemUI and 632 * Settings/AboutPhone/SIM status UI. 633 * 634 * <p>This method returns a single service state int if either the voice reg state is {@link 635 * ServiceState#STATE_IN_SERVICE} or if data network is registered via a WWAN transport type. We 636 * consider the combined service state of an IWLAN network to be OOS. 637 * 638 * @param serviceState Service state. {@link ServiceState} 639 */ getCombinedServiceState(ServiceState serviceState)640 public static int getCombinedServiceState(ServiceState serviceState) { 641 if (serviceState == null) { 642 return ServiceState.STATE_OUT_OF_SERVICE; 643 } 644 645 final int voiceRegState = serviceState.getVoiceRegState(); 646 647 // Consider a mobile connection to be "in service" if either voice is IN_SERVICE 648 // or the data registration reports IN_SERVICE on a transport type of WWAN. This 649 // effectively excludes the IWLAN condition. IWLAN connections imply service via 650 // Wi-Fi rather than cellular, and so we do not consider these transports when 651 // determining if cellular is "in service". 652 653 if (voiceRegState == ServiceState.STATE_OUT_OF_SERVICE 654 || voiceRegState == ServiceState.STATE_EMERGENCY_ONLY) { 655 if (isDataRegInWwanAndInService(serviceState)) { 656 return ServiceState.STATE_IN_SERVICE; 657 } 658 } 659 660 return voiceRegState; 661 } 662 663 // ServiceState#mDataRegState can be set to IN_SERVICE if the network is registered 664 // on either a WLAN or WWAN network. Since we want to exclude the WLAN network, we can 665 // query the WWAN network directly and check for its registration state isDataRegInWwanAndInService(ServiceState serviceState)666 private static boolean isDataRegInWwanAndInService(ServiceState serviceState) { 667 final NetworkRegistrationInfo networkRegWwan = 668 serviceState.getNetworkRegistrationInfo( 669 NetworkRegistrationInfo.DOMAIN_PS, 670 AccessNetworkConstants.TRANSPORT_TYPE_WWAN); 671 672 if (networkRegWwan == null) { 673 return false; 674 } 675 676 return networkRegWwan.isInService(); 677 } 678 679 /** Get the corresponding adaptive icon drawable. */ getBadgedIcon(Context context, Drawable icon, UserHandle user)680 public static Drawable getBadgedIcon(Context context, Drawable icon, UserHandle user) { 681 int userType = UserIconInfo.TYPE_MAIN; 682 try { 683 UserInfo ui = 684 context.getSystemService(UserManager.class).getUserInfo(user.getIdentifier()); 685 if (ui != null) { 686 if (ui.isCloneProfile()) { 687 userType = UserIconInfo.TYPE_CLONED; 688 } else if (ui.isManagedProfile()) { 689 userType = UserIconInfo.TYPE_WORK; 690 } else if (ui.isPrivateProfile()) { 691 userType = UserIconInfo.TYPE_PRIVATE; 692 } 693 } 694 } catch (Exception e) { 695 // Ignore 696 } 697 try (IconFactory iconFactory = IconFactory.obtain(context)) { 698 return iconFactory 699 .createBadgedIconBitmap( 700 icon, new IconOptions().setUser(new UserIconInfo(user, userType))) 701 .newIcon(context); 702 } 703 } 704 705 /** Get the {@link Drawable} that represents the app icon */ getBadgedIcon(Context context, ApplicationInfo appInfo)706 public static Drawable getBadgedIcon(Context context, ApplicationInfo appInfo) { 707 return getBadgedIcon( 708 context, 709 appInfo.loadUnbadgedIcon(context.getPackageManager()), 710 UserHandle.getUserHandleForUid(appInfo.uid)); 711 } 712 713 /** 714 * Returns a bitmap with rounded corner. 715 * 716 * @param context application context. 717 * @param source bitmap to apply round corner. 718 * @param cornerRadius corner radius value. 719 */ 720 @NonNull convertCornerRadiusBitmap( @onNull Context context, @NonNull Bitmap source, @NonNull float cornerRadius)721 public static Bitmap convertCornerRadiusBitmap( 722 @NonNull Context context, @NonNull Bitmap source, @NonNull float cornerRadius) { 723 final Bitmap roundedBitmap = 724 Bitmap.createBitmap(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888); 725 final RoundedBitmapDrawable drawable = 726 RoundedBitmapDrawableFactory.create(context.getResources(), source); 727 drawable.setAntiAlias(true); 728 drawable.setCornerRadius(cornerRadius); 729 final Canvas canvas = new Canvas(roundedBitmap); 730 drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); 731 drawable.draw(canvas); 732 return roundedBitmap; 733 } 734 735 /** 736 * Returns the WifiInfo for the underlying WiFi network of the VCN network, returns null if the 737 * input NetworkCapabilities is not for a VCN network with underlying WiFi network. 738 * 739 * @param networkCapabilities NetworkCapabilities of the network. 740 */ 741 @Nullable tryGetWifiInfoForVcn(NetworkCapabilities networkCapabilities)742 public static WifiInfo tryGetWifiInfoForVcn(NetworkCapabilities networkCapabilities) { 743 if (networkCapabilities.getTransportInfo() == null 744 || !(networkCapabilities.getTransportInfo() instanceof VcnTransportInfo)) { 745 return null; 746 } 747 VcnTransportInfo vcnTransportInfo = 748 (VcnTransportInfo) networkCapabilities.getTransportInfo(); 749 return vcnTransportInfo.getWifiInfo(); 750 } 751 752 /** Whether there is any incompatible chargers in the current UsbPort? */ containsIncompatibleChargers(Context context, String tag)753 public static boolean containsIncompatibleChargers(Context context, String tag) { 754 // Avoid the caller doesn't have permission to read the "Settings.Secure" data. 755 try { 756 // Whether the incompatible charger warning is disabled or not 757 if (Settings.Secure.getInt( 758 context.getContentResolver(), INCOMPATIBLE_CHARGER_WARNING_DISABLED, 0) 759 == 1) { 760 Log.d(tag, "containsIncompatibleChargers: disabled"); 761 return false; 762 } 763 } catch (Exception e) { 764 Log.e(tag, "containsIncompatibleChargers()", e); 765 return false; 766 } 767 768 final UsbManager usbManager = context.getSystemService(UsbManager.class); 769 if (usbManager == null) { 770 return false; 771 } 772 final List<UsbPort> usbPortList = usbManager.getPorts(); 773 if (usbPortList == null || usbPortList.isEmpty()) { 774 return false; 775 } 776 for (UsbPort usbPort : usbPortList) { 777 Log.d(tag, "usbPort: " + usbPort); 778 if (!usbPort.supportsComplianceWarnings()) { 779 continue; 780 } 781 final UsbPortStatus usbStatus = usbPort.getStatus(); 782 if (usbStatus == null || !usbStatus.isConnected()) { 783 continue; 784 } 785 final int[] complianceWarnings = usbStatus.getComplianceWarnings(); 786 if (complianceWarnings == null || complianceWarnings.length == 0) { 787 continue; 788 } 789 for (int complianceWarningType : complianceWarnings) { 790 if (Flags.enableUsbDataComplianceWarning() 791 && Flags.enableInputPowerLimitedWarning()) { 792 switch (complianceWarningType) { 793 case UsbPortStatus.COMPLIANCE_WARNING_INPUT_POWER_LIMITED: 794 case UsbPortStatus.COMPLIANCE_WARNING_DEBUG_ACCESSORY: 795 return true; 796 default: 797 break; 798 } 799 } else { 800 switch (complianceWarningType) { 801 case UsbPortStatus.COMPLIANCE_WARNING_OTHER: 802 case UsbPortStatus.COMPLIANCE_WARNING_DEBUG_ACCESSORY: 803 return true; 804 default: 805 break; 806 } 807 } 808 } 809 } 810 return false; 811 } 812 } 813