• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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