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