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