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