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