1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings.network.telephony; 18 19 import static android.provider.Telephony.Carriers.ENFORCE_MANAGED_URI; 20 21 import android.app.KeyguardManager; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.pm.PackageManager; 25 import android.content.pm.ResolveInfo; 26 import android.database.Cursor; 27 import android.graphics.Color; 28 import android.graphics.drawable.ColorDrawable; 29 import android.graphics.drawable.Drawable; 30 import android.graphics.drawable.LayerDrawable; 31 import android.hardware.biometrics.BiometricPrompt; 32 import android.net.ConnectivityManager; 33 import android.net.Network; 34 import android.net.NetworkCapabilities; 35 import android.os.Bundle; 36 import android.os.CancellationSignal; 37 import android.os.Handler; 38 import android.os.Looper; 39 import android.os.PersistableBundle; 40 import android.os.UserHandle; 41 import android.os.UserManager; 42 import android.provider.Settings; 43 import android.telecom.PhoneAccountHandle; 44 import android.telecom.TelecomManager; 45 import android.telephony.CarrierConfigManager; 46 import android.telephony.RadioAccessFamily; 47 import android.telephony.ServiceState; 48 import android.telephony.SubscriptionInfo; 49 import android.telephony.SubscriptionManager; 50 import android.telephony.TelephonyManager; 51 import android.telephony.ims.ImsManager; 52 import android.telephony.ims.ImsRcsManager; 53 import android.telephony.ims.ProvisioningManager; 54 import android.telephony.ims.RcsUceAdapter; 55 import android.telephony.ims.feature.MmTelFeature; 56 import android.telephony.ims.stub.ImsRegistrationImplBase; 57 import android.text.TextUtils; 58 import android.util.Log; 59 import android.view.Gravity; 60 61 import androidx.annotation.NonNull; 62 import androidx.annotation.Nullable; 63 import androidx.annotation.VisibleForTesting; 64 65 import com.android.internal.util.ArrayUtils; 66 import com.android.settings.R; 67 import com.android.settings.Utils; 68 import com.android.settings.core.BasePreferenceController; 69 import com.android.settings.core.SubSettingLauncher; 70 import com.android.settings.network.CarrierConfigCache; 71 import com.android.settings.network.SubscriptionUtil; 72 import com.android.settings.network.ims.WifiCallingQueryImsState; 73 import com.android.settings.network.telephony.wificalling.WifiCallingRepository; 74 import com.android.settingslib.core.instrumentation.Instrumentable; 75 import com.android.settingslib.graph.SignalDrawable; 76 import com.android.settingslib.mobile.dataservice.SubscriptionInfoEntity; 77 78 import java.util.List; 79 80 public class MobileNetworkUtils { 81 82 private static final String TAG = "MobileNetworkUtils"; 83 84 private static final String LEGACY_ACTION_CONFIGURE_PHONE_ACCOUNT = 85 "android.telecom.action.CONNECTION_SERVICE_CONFIGURE"; 86 private static final String RTL_MARK = "\u200F"; 87 88 // The following constants are used to draw signal icon. 89 public static final int NO_CELL_DATA_TYPE_ICON = 0; 90 public static final Drawable EMPTY_DRAWABLE = new ColorDrawable(Color.TRANSPARENT); 91 92 /** 93 * Return true if current user limited by UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS. 94 * 95 * Note: Guest user should have this restriction through 96 * GuestTelephonyPreferenceController.java. 97 * However, it's not help with those devices upgraded their software. 98 */ isMobileNetworkUserRestricted(Context context)99 public static boolean isMobileNetworkUserRestricted(Context context) { 100 UserManager um = context.getSystemService(UserManager.class); 101 boolean disallow = false; 102 if (um != null) { 103 disallow = um.isGuestUser() || um.hasUserRestriction( 104 UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS); 105 } 106 return disallow; 107 } 108 109 /** 110 * Returns if DPC APNs are enforced. 111 */ isDpcApnEnforced(Context context)112 public static boolean isDpcApnEnforced(Context context) { 113 try (Cursor enforceCursor = context.getContentResolver().query(ENFORCE_MANAGED_URI, 114 null, null, null, null)) { 115 if (enforceCursor == null || enforceCursor.getCount() != 1) { 116 return false; 117 } 118 enforceCursor.moveToFirst(); 119 return enforceCursor.getInt(0) > 0; 120 } 121 } 122 123 /** 124 * Returns true if Wifi calling is provisioned for the specific subscription with id 125 * {@code subId}. 126 */ 127 @VisibleForTesting isWfcProvisionedOnDevice(int subId)128 public static boolean isWfcProvisionedOnDevice(int subId) { 129 final ProvisioningManager provisioningMgr = 130 ProvisioningManager.createForSubscriptionId(subId); 131 if (provisioningMgr == null) { 132 return true; 133 } 134 return provisioningMgr.getProvisioningStatusForCapability( 135 MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE, 136 ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN); 137 } 138 139 /** 140 * @return The current user setting for whether or not contact discovery is enabled for the 141 * subscription id specified. 142 * @see RcsUceAdapter#isUceSettingEnabled() 143 */ isContactDiscoveryEnabled(Context context, int subId)144 public static boolean isContactDiscoveryEnabled(Context context, int subId) { 145 ImsManager imsManager = 146 context.getSystemService(ImsManager.class); 147 return isContactDiscoveryEnabled(imsManager, subId); 148 } 149 150 /** 151 * @return The current user setting for whether or not contact discovery is enabled for the 152 * subscription id specified. 153 * @see RcsUceAdapter#isUceSettingEnabled() 154 */ isContactDiscoveryEnabled(ImsManager imsManager, int subId)155 public static boolean isContactDiscoveryEnabled(ImsManager imsManager, 156 int subId) { 157 ImsRcsManager manager = getImsRcsManager(imsManager, subId); 158 if (manager == null) return false; 159 RcsUceAdapter adapter = manager.getUceAdapter(); 160 try { 161 return adapter.isUceSettingEnabled(); 162 } catch (android.telephony.ims.ImsException e) { 163 Log.w(TAG, "UCE service is not available: " + e.getMessage()); 164 } 165 return false; 166 } 167 168 /** 169 * Set the new user setting to enable or disable contact discovery through RCS UCE. 170 * @see RcsUceAdapter#setUceSettingEnabled(boolean) 171 */ setContactDiscoveryEnabled(ImsManager imsManager, int subId, boolean isEnabled)172 public static void setContactDiscoveryEnabled(ImsManager imsManager, 173 int subId, boolean isEnabled) { 174 ImsRcsManager manager = getImsRcsManager(imsManager, subId); 175 if (manager == null) return; 176 RcsUceAdapter adapter = manager.getUceAdapter(); 177 try { 178 adapter.setUceSettingEnabled(isEnabled); 179 } catch (android.telephony.ims.ImsException e) { 180 Log.w(TAG, "UCE service is not available: " + e.getMessage()); 181 } 182 } 183 184 /** 185 * @return The ImsRcsManager associated with the subscription specified. 186 */ getImsRcsManager(ImsManager imsManager, int subId)187 private static ImsRcsManager getImsRcsManager(ImsManager imsManager, 188 int subId) { 189 if (imsManager == null) return null; 190 try { 191 return imsManager.getImsRcsManager(subId); 192 } catch (Exception e) { 193 Log.w(TAG, "Could not resolve ImsRcsManager: " + e.getMessage()); 194 } 195 return null; 196 } 197 198 /** 199 * @return true if contact discovery is available for the subscription specified and the option 200 * should be shown to the user, false if the option should be hidden. 201 */ isContactDiscoveryVisible(Context context, int subId)202 public static boolean isContactDiscoveryVisible(Context context, int subId) { 203 CarrierConfigCache carrierConfigCache = CarrierConfigCache.getInstance(context); 204 if (!carrierConfigCache.hasCarrierConfigManager()) { 205 Log.w(TAG, "isContactDiscoveryVisible: Could not resolve carrier config"); 206 return false; 207 } 208 PersistableBundle bundle = carrierConfigCache.getConfigForSubId(subId); 209 return bundle == null ? false : bundle.getBoolean( 210 CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL, false /*default*/) 211 || bundle.getBoolean(CarrierConfigManager.Ims.KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL, 212 false /*default*/); 213 } 214 buildPhoneAccountConfigureIntent( Context context, PhoneAccountHandle accountHandle)215 public static Intent buildPhoneAccountConfigureIntent( 216 Context context, PhoneAccountHandle accountHandle) { 217 Intent intent = buildConfigureIntent( 218 context, accountHandle, TelecomManager.ACTION_CONFIGURE_PHONE_ACCOUNT); 219 220 if (intent == null) { 221 // If the new configuration didn't work, try the old configuration intent. 222 intent = buildConfigureIntent(context, accountHandle, 223 LEGACY_ACTION_CONFIGURE_PHONE_ACCOUNT); 224 } 225 return intent; 226 } 227 buildConfigureIntent( Context context, PhoneAccountHandle accountHandle, String actionStr)228 private static Intent buildConfigureIntent( 229 Context context, PhoneAccountHandle accountHandle, String actionStr) { 230 if (accountHandle == null || accountHandle.getComponentName() == null 231 || TextUtils.isEmpty(accountHandle.getComponentName().getPackageName())) { 232 return null; 233 } 234 235 // Build the settings intent. 236 Intent intent = new Intent(actionStr); 237 intent.setPackage(accountHandle.getComponentName().getPackageName()); 238 intent.addCategory(Intent.CATEGORY_DEFAULT); 239 intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, accountHandle); 240 241 // Check to see that the phone account package can handle the setting intent. 242 final PackageManager pm = context.getPackageManager(); 243 final List<ResolveInfo> resolutions = pm.queryIntentActivities(intent, 0); 244 if (resolutions.size() == 0) { 245 intent = null; // set no intent if the package cannot handle it. 246 } 247 248 return intent; 249 } 250 251 /** 252 * Return {@code true} if mobile data is enabled 253 */ isMobileDataEnabled(Context context)254 public static boolean isMobileDataEnabled(Context context) { 255 final TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class); 256 if (!telephonyManager.isDataEnabled()) { 257 // Check if the data is enabled on the second SIM in the case of dual SIM. 258 final TelephonyManager tmDefaultData = telephonyManager.createForSubscriptionId( 259 SubscriptionManager.getDefaultDataSubscriptionId()); 260 if (tmDefaultData == null || !tmDefaultData.isDataEnabled()) { 261 return false; 262 } 263 } 264 return true; 265 } 266 267 /** 268 * Set whether to enable data for {@code subId}, also whether to disable data for other 269 * subscription 270 */ setMobileDataEnabled(Context context, int subId, boolean enabled, boolean disableOtherSubscriptions)271 public static void setMobileDataEnabled(Context context, int subId, boolean enabled, 272 boolean disableOtherSubscriptions) { 273 final TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class) 274 .createForSubscriptionId(subId); 275 final SubscriptionManager subscriptionManager = context.getSystemService( 276 SubscriptionManager.class).createForAllUserProfiles(); 277 Log.d(TAG, "setDataEnabledForReason: " + enabled); 278 telephonyManager.setDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_USER, 279 enabled); 280 281 if (disableOtherSubscriptions) { 282 final List<SubscriptionInfo> subInfoList = 283 subscriptionManager.getActiveSubscriptionInfoList(); 284 if (subInfoList != null) { 285 for (SubscriptionInfo subInfo : subInfoList) { 286 // We never disable mobile data for opportunistic subscriptions. 287 if (subInfo.getSubscriptionId() != subId && !subInfo.isOpportunistic()) { 288 context.getSystemService(TelephonyManager.class) 289 .createForSubscriptionId(subInfo.getSubscriptionId()) 290 .setDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_USER, 291 false); 292 } 293 } 294 } 295 } 296 } 297 298 /** 299 * Return {@code true} if show CDMA category 300 */ isCdmaOptions(Context context, int subId)301 public static boolean isCdmaOptions(Context context, int subId) { 302 if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { 303 return false; 304 } 305 final PersistableBundle carrierConfig = 306 CarrierConfigCache.getInstance(context).getConfigForSubId(subId); 307 if (carrierConfig != null 308 && !carrierConfig.getBoolean( 309 CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL) 310 && carrierConfig.getBoolean(CarrierConfigManager.KEY_WORLD_PHONE_BOOL)) { 311 return true; 312 } 313 314 final TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class) 315 .createForSubscriptionId(subId); 316 if (telephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) { 317 return true; 318 } 319 320 if (isWorldMode(context, subId)) { 321 final int settingsNetworkMode = RadioAccessFamily.getNetworkTypeFromRaf( 322 (int) telephonyManager.getAllowedNetworkTypesForReason( 323 TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER)); 324 325 if (settingsNetworkMode == TelephonyManager.NETWORK_MODE_LTE_GSM_WCDMA 326 || settingsNetworkMode == TelephonyManager.NETWORK_MODE_LTE_CDMA_EVDO 327 || settingsNetworkMode == TelephonyManager.NETWORK_MODE_NR_LTE_GSM_WCDMA 328 || settingsNetworkMode == TelephonyManager.NETWORK_MODE_NR_LTE_CDMA_EVDO) { 329 return true; 330 } 331 332 if (shouldSpeciallyUpdateGsmCdma(context, subId)) { 333 return true; 334 } 335 } 336 337 return false; 338 } 339 340 /** 341 * return {@code true} if we need show Gsm related settings 342 */ isGsmOptions(Context context, int subId)343 public static boolean isGsmOptions(Context context, int subId) { 344 if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { 345 return false; 346 } 347 if (isGsmBasicOptions(context, subId)) { 348 return true; 349 } 350 final TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class) 351 .createForSubscriptionId(subId); 352 final int networkMode = RadioAccessFamily.getNetworkTypeFromRaf( 353 (int) telephonyManager.getAllowedNetworkTypesForReason( 354 TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER)); 355 if (isWorldMode(context, subId)) { 356 if (networkMode == TelephonyManager.NETWORK_MODE_LTE_CDMA_EVDO 357 || networkMode == TelephonyManager.NETWORK_MODE_LTE_GSM_WCDMA 358 || networkMode == TelephonyManager.NETWORK_MODE_NR_LTE_CDMA_EVDO 359 || networkMode == TelephonyManager.NETWORK_MODE_NR_LTE_GSM_WCDMA) { 360 return true; 361 } else if (shouldSpeciallyUpdateGsmCdma(context, subId)) { 362 return true; 363 } 364 } 365 366 return false; 367 } 368 isGsmBasicOptions(Context context, int subId)369 private static boolean isGsmBasicOptions(Context context, int subId) { 370 final PersistableBundle carrierConfig = 371 CarrierConfigCache.getInstance(context).getConfigForSubId(subId); 372 if (carrierConfig != null 373 && !carrierConfig.getBoolean( 374 CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL) 375 && carrierConfig.getBoolean(CarrierConfigManager.KEY_WORLD_PHONE_BOOL)) { 376 return true; 377 } 378 379 final TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class) 380 .createForSubscriptionId(subId); 381 if (telephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM) { 382 return true; 383 } 384 385 return false; 386 } 387 388 /** 389 * Return {@code true} if it is world mode, and we may show advanced options in telephony 390 * settings 391 */ isWorldMode(Context context, int subId)392 public static boolean isWorldMode(Context context, int subId) { 393 final PersistableBundle carrierConfig = 394 CarrierConfigCache.getInstance(context).getConfigForSubId(subId); 395 return carrierConfig == null 396 ? false 397 : carrierConfig.getBoolean(CarrierConfigManager.KEY_WORLD_MODE_ENABLED_BOOL); 398 } 399 400 /** 401 * Return {@code true} if we need show settings for network selection(i.e. Verizon) 402 */ shouldDisplayNetworkSelectOptions(Context context, int subId)403 public static boolean shouldDisplayNetworkSelectOptions(Context context, int subId) { 404 final TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class) 405 .createForSubscriptionId(subId); 406 final PersistableBundle carrierConfig = 407 CarrierConfigCache.getInstance(context).getConfigForSubId(subId); 408 if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID 409 || carrierConfig == null 410 || !carrierConfig.getBoolean( 411 CarrierConfigManager.KEY_OPERATOR_SELECTION_EXPAND_BOOL) 412 || carrierConfig.getBoolean( 413 CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL) 414 || (carrierConfig.getBoolean(CarrierConfigManager.KEY_CSP_ENABLED_BOOL) 415 && !telephonyManager.isManualNetworkSelectionAllowed())) { 416 return false; 417 } 418 419 if (isWorldMode(context, subId)) { 420 final int networkMode = RadioAccessFamily.getNetworkTypeFromRaf( 421 (int) telephonyManager.getAllowedNetworkTypesForReason( 422 TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER)); 423 if (networkMode == TelephonyManager.NETWORK_MODE_LTE_CDMA_EVDO) { 424 return false; 425 } 426 if (shouldSpeciallyUpdateGsmCdma(context, subId)) { 427 return false; 428 } 429 430 if (networkMode == TelephonyManager.NETWORK_MODE_LTE_GSM_WCDMA) { 431 return true; 432 } 433 } 434 435 return isGsmBasicOptions(context, subId); 436 } 437 438 /** 439 * Return {@code true} if Tdscdma is supported in current subscription 440 */ isTdscdmaSupported(Context context, int subId)441 public static boolean isTdscdmaSupported(Context context, int subId) { 442 return isTdscdmaSupported(context, 443 context.getSystemService(TelephonyManager.class).createForSubscriptionId(subId)); 444 } 445 446 //TODO(b/117651939): move it to telephony isTdscdmaSupported(Context context, TelephonyManager telephonyManager)447 private static boolean isTdscdmaSupported(Context context, TelephonyManager telephonyManager) { 448 final PersistableBundle carrierConfig = CarrierConfigCache.getInstance(context).getConfig(); 449 450 if (carrierConfig == null) { 451 return false; 452 } 453 454 if (carrierConfig.getBoolean(CarrierConfigManager.KEY_SUPPORT_TDSCDMA_BOOL)) { 455 return true; 456 } 457 final String[] numericArray = carrierConfig.getStringArray( 458 CarrierConfigManager.KEY_SUPPORT_TDSCDMA_ROAMING_NETWORKS_STRING_ARRAY); 459 if (numericArray == null) { 460 return false; 461 } 462 final ServiceState serviceState = telephonyManager.getServiceState(); 463 final String operatorNumeric = 464 (serviceState != null) ? serviceState.getOperatorNumeric() : null; 465 if (operatorNumeric == null) { 466 return false; 467 } 468 for (String numeric : numericArray) { 469 if (operatorNumeric.equals(numeric)) { 470 return true; 471 } 472 } 473 return false; 474 } 475 476 /** 477 * Return subId that supported by search. If there are more than one, return first one, 478 * otherwise return {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID} 479 */ getSearchableSubscriptionId(Context context)480 public static int getSearchableSubscriptionId(Context context) { 481 final int[] subIds = getActiveSubscriptionIdList(context); 482 483 return subIds.length >= 1 ? subIds[0] : SubscriptionManager.INVALID_SUBSCRIPTION_ID; 484 } 485 486 /** 487 * Return availability for a default subscription id. If subId already been set, use it to 488 * check, otherwise traverse all active subIds on device to check. 489 * @param context context 490 * @param defSubId Default subId get from telephony preference controller 491 * @param callback Callback to check availability for a specific subId 492 * @return Availability 493 * 494 * @see BasePreferenceController#getAvailabilityStatus() 495 */ getAvailability(Context context, int defSubId, TelephonyAvailabilityCallback callback)496 public static int getAvailability(Context context, int defSubId, 497 TelephonyAvailabilityCallback callback) { 498 if (defSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) { 499 // If subId has been set, return the corresponding status 500 return callback.getAvailabilityStatus(defSubId); 501 } else { 502 // Otherwise, search whether there is one subId in device that support this preference 503 final int[] subIds = getActiveSubscriptionIdList(context); 504 if (ArrayUtils.isEmpty(subIds)) { 505 return callback.getAvailabilityStatus( 506 SubscriptionManager.INVALID_SUBSCRIPTION_ID); 507 } else { 508 for (final int subId : subIds) { 509 final int status = callback.getAvailabilityStatus(subId); 510 if (status == BasePreferenceController.AVAILABLE) { 511 return status; 512 } 513 } 514 return callback.getAvailabilityStatus(subIds[0]); 515 } 516 } 517 } 518 519 /** 520 * This method is migrated from {@link com.android.phone.MobileNetworkSettings} and we should 521 * use it carefully. This code snippet doesn't have very clear meaning however we should 522 * update GSM or CDMA differently based on what it returns. 523 * 524 * 1. For all CDMA settings, make them visible if it return {@code true} 525 * 2. For GSM settings, make them visible if it return {@code true} unless 3 526 * 3. For network select settings, make it invisible if it return {@code true} 527 */ 528 @VisibleForTesting shouldSpeciallyUpdateGsmCdma(Context context, int subId)529 static boolean shouldSpeciallyUpdateGsmCdma(Context context, int subId) { 530 if (!isWorldMode(context, subId)) { 531 return false; 532 } 533 final TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class) 534 .createForSubscriptionId(subId); 535 final int networkMode = RadioAccessFamily.getNetworkTypeFromRaf( 536 (int) telephonyManager.getAllowedNetworkTypesForReason( 537 TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER)); 538 if (networkMode == TelephonyManager.NETWORK_MODE_LTE_TDSCDMA_GSM 539 || networkMode == TelephonyManager.NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA 540 || networkMode == TelephonyManager.NETWORK_MODE_LTE_TDSCDMA 541 || networkMode == TelephonyManager.NETWORK_MODE_LTE_TDSCDMA_WCDMA 542 || networkMode 543 == TelephonyManager.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA 544 || networkMode == TelephonyManager.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA) { 545 if (!isTdscdmaSupported(context, subId)) { 546 return true; 547 } 548 } 549 550 return false; 551 } 552 getSignalStrengthIcon(Context context, int level, int numLevels, int iconType, boolean cutOut, boolean carrierNetworkChanged)553 public static Drawable getSignalStrengthIcon(Context context, int level, int numLevels, 554 int iconType, boolean cutOut, boolean carrierNetworkChanged) { 555 final SignalDrawable signalDrawable = new SignalDrawable(context); 556 signalDrawable.setLevel( 557 carrierNetworkChanged ? SignalDrawable.getCarrierChangeState(numLevels) 558 : SignalDrawable.getState(level, numLevels, cutOut)); 559 560 // Make the network type drawable 561 final Drawable networkDrawable = 562 iconType == NO_CELL_DATA_TYPE_ICON 563 ? EMPTY_DRAWABLE 564 : context.getResources().getDrawable(iconType, context.getTheme()); 565 566 // Overlay the two drawables 567 final Drawable[] layers = {networkDrawable, signalDrawable}; 568 final int iconSize = 569 context.getResources().getDimensionPixelSize(R.dimen.signal_strength_icon_size); 570 571 final LayerDrawable icons = new LayerDrawable(layers); 572 // Set the network type icon at the top left 573 icons.setLayerGravity(0 /* index of networkDrawable */, Gravity.TOP | Gravity.LEFT); 574 // Set the signal strength icon at the bottom right 575 icons.setLayerGravity(1 /* index of SignalDrawable */, Gravity.BOTTOM | Gravity.RIGHT); 576 icons.setLayerSize(1 /* index of SignalDrawable */, iconSize, iconSize); 577 icons.setTintList(Utils.getColorAttr(context, android.R.attr.colorControlNormal)); 578 return icons; 579 } 580 581 /** 582 * This method is migrated from 583 * {@link android.telephony.TelephonyManager.getNetworkOperatorName}. Which provides 584 * 585 * 1. Better support under multi-SIM environment. 586 * 2. Similar design which aligned with operator name displayed in status bar 587 */ getCurrentCarrierNameForDisplay(Context context, int subId)588 public static CharSequence getCurrentCarrierNameForDisplay(Context context, int subId) { 589 final SubscriptionInfo subInfo = getSubscriptionInfo(context, subId); 590 if (subInfo != null) { 591 return subInfo.getCarrierName(); 592 } 593 return getOperatorNameFromTelephonyManager(context); 594 } 595 getCurrentCarrierNameForDisplay(Context context)596 public static CharSequence getCurrentCarrierNameForDisplay(Context context) { 597 return getCurrentCarrierNameForDisplay(context, 598 SubscriptionManager.getDefaultSubscriptionId()); 599 } 600 getSubscriptionInfo(Context context, int subId)601 private static @Nullable SubscriptionInfo getSubscriptionInfo(Context context, int subId) { 602 SubscriptionManager sm = context.getSystemService(SubscriptionManager.class); 603 if (sm == null) return null; 604 return sm.createForAllUserProfiles().getActiveSubscriptionInfo(subId); 605 } 606 getOperatorNameFromTelephonyManager(Context context)607 private static String getOperatorNameFromTelephonyManager(Context context) { 608 final TelephonyManager tm = 609 (TelephonyManager) context.getSystemService(TelephonyManager.class); 610 if (tm == null) { 611 return null; 612 } 613 return tm.getNetworkOperatorName(); 614 } 615 616 @VisibleForTesting getActiveSubscriptionIdList(Context context)617 static int[] getActiveSubscriptionIdList(Context context) { 618 final SubscriptionManager subscriptionManager = context.getSystemService( 619 SubscriptionManager.class).createForAllUserProfiles(); 620 final List<SubscriptionInfo> subInfoList = 621 SubscriptionUtil.getActiveSubscriptions(subscriptionManager); 622 if (subInfoList == null || subInfoList.isEmpty()) { 623 return new int[0]; 624 } 625 int[] activeSubIds = new int[subInfoList.size()]; 626 int i = 0; 627 for (SubscriptionInfo subInfo : subInfoList) { 628 activeSubIds[i] = subInfo.getSubscriptionId(); 629 i++; 630 } 631 return activeSubIds; 632 } 633 634 /** 635 * Copied from SubscriptionsPreferenceController#activeNetworkIsCellular() 636 */ activeNetworkIsCellular(Context context)637 public static boolean activeNetworkIsCellular(Context context) { 638 final ConnectivityManager connectivityManager = 639 context.getSystemService(ConnectivityManager.class); 640 final Network activeNetwork = connectivityManager.getActiveNetwork(); 641 if (activeNetwork == null) { 642 return false; 643 } 644 final NetworkCapabilities networkCapabilities = 645 connectivityManager.getNetworkCapabilities(activeNetwork); 646 if (networkCapabilities == null) { 647 return false; 648 } 649 return networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR); 650 } 651 652 /** 653 * Copied from WifiCallingPreferenceController#isWifiCallingEnabled() 654 * 655 * @deprecated Use {@link WifiCallingRepository#wifiCallingReadyFlow()} instead. 656 */ 657 @Deprecated isWifiCallingEnabled(Context context, int subId, @Nullable WifiCallingQueryImsState queryImsState)658 public static boolean isWifiCallingEnabled(Context context, int subId, 659 @Nullable WifiCallingQueryImsState queryImsState) { 660 if (queryImsState == null) { 661 queryImsState = new WifiCallingQueryImsState(context, subId); 662 } 663 return queryImsState.isReadyToWifiCalling(); 664 } 665 666 /** 667 * Returns preferred status of Calls & SMS separately when Provider Model is enabled. 668 */ getPreferredStatus(boolean isRtlMode, Context context, boolean isPreferredCallStatus, List<SubscriptionInfoEntity> entityList)669 public static CharSequence getPreferredStatus(boolean isRtlMode, Context context, 670 boolean isPreferredCallStatus, List<SubscriptionInfoEntity> entityList) { 671 if (entityList != null && !entityList.isEmpty()) { 672 final StringBuilder summary = new StringBuilder(); 673 for (SubscriptionInfoEntity subInfo : entityList) { 674 int subsSize = entityList.size(); 675 final CharSequence displayName = subInfo.uniqueName; 676 677 // Set displayName as summary if there is only one valid SIM. 678 if (subsSize == 1 && subInfo.isValidSubscription) { 679 return displayName; 680 } 681 682 CharSequence status = isPreferredCallStatus 683 ? getPreferredCallStatus(context, subInfo) 684 : getPreferredSmsStatus(context, subInfo); 685 if (status.toString().isEmpty()) { 686 // If there are 2 or more SIMs and one of these has no preferred status, 687 // set only its displayName as summary. 688 summary.append(displayName); 689 } else { 690 summary.append(displayName) 691 .append(" (") 692 .append(status) 693 .append(")"); 694 } 695 // Do not add ", " for the last subscription. 696 if (subInfo != entityList.get(entityList.size() - 1)) { 697 summary.append(", "); 698 } 699 700 if (isRtlMode) { 701 summary.insert(0, RTL_MARK).insert(summary.length(), RTL_MARK); 702 } 703 } 704 return summary; 705 } else { 706 return ""; 707 } 708 } 709 getPreferredCallStatus(Context context, SubscriptionInfoEntity subInfo)710 private static CharSequence getPreferredCallStatus(Context context, 711 SubscriptionInfoEntity subInfo) { 712 String status = ""; 713 if (subInfo.getSubId() == SubscriptionManager.getDefaultVoiceSubscriptionId()) { 714 status = setSummaryResId(context, R.string.calls_sms_preferred); 715 } 716 717 return status; 718 } 719 getPreferredSmsStatus(Context context, SubscriptionInfoEntity subInfo)720 private static CharSequence getPreferredSmsStatus(Context context, 721 SubscriptionInfoEntity subInfo) { 722 String status = ""; 723 if (subInfo.getSubId() == SubscriptionManager.getDefaultSmsSubscriptionId()) { 724 status = setSummaryResId(context, R.string.calls_sms_preferred); 725 } 726 727 return status; 728 } 729 setSummaryResId(Context context, int resId)730 private static String setSummaryResId(Context context, int resId) { 731 return context.getResources().getString(resId); 732 } 733 launchMobileNetworkSettings(Context context, SubscriptionInfo info)734 public static void launchMobileNetworkSettings(Context context, SubscriptionInfo info) { 735 if (!SubscriptionUtil.isSimHardwareVisible(context)) { 736 Log.e(TAG, "launchMobileNetworkSettings fail, device without such UI."); 737 return; 738 } 739 final int subId = info.getSubscriptionId(); 740 if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { 741 Log.d(TAG, "launchMobileNetworkSettings fail, subId is invalid."); 742 return; 743 } 744 745 Log.d(TAG, "launchMobileNetworkSettings for subId: " + subId); 746 final Bundle extra = new Bundle(); 747 extra.putInt(Settings.EXTRA_SUB_ID, subId); 748 new SubSettingLauncher(context) 749 .setTitleText(SubscriptionUtil.getUniqueSubscriptionDisplayName(info, context)) 750 .setDestination(MobileNetworkSettings.class.getCanonicalName()) 751 .setSourceMetricsCategory(Instrumentable.METRICS_CATEGORY_UNKNOWN) 752 .setArguments(extra) 753 .launch(); 754 } 755 launchMobileNetworkSettings(Context context, SubscriptionInfoEntity info)756 public static void launchMobileNetworkSettings(Context context, SubscriptionInfoEntity info) { 757 final int subId = Integer.valueOf(info.subId); 758 if (!info.isValidSubscription) { 759 Log.d(TAG, "launchMobileNetworkSettings fail, subId is invalid."); 760 return; 761 } 762 763 Log.d(TAG, "launchMobileNetworkSettings for SubscriptionInfoEntity subId: " + subId); 764 final Bundle extra = new Bundle(); 765 extra.putInt(Settings.EXTRA_SUB_ID, subId); 766 new SubSettingLauncher(context) 767 .setTitleText(info.uniqueName) 768 .setDestination(MobileNetworkSettings.class.getCanonicalName()) 769 .setSourceMetricsCategory(Instrumentable.METRICS_CATEGORY_UNKNOWN) 770 .setArguments(extra) 771 .launch(); 772 } 773 774 /** 775 * Shows authentication screen to confirm credentials (pin/pattern/password) for the current 776 * user of the device. 777 * 778 * <p>Similar to WifiDppUtils.showLockScreen(), but doesn't check for the existence of 779 * SIM PIN lock, only screen PIN lock. 780 * 781 * @param context The {@code Context} used to get {@link KeyguardManager} service 782 * @param onSuccess The {@code Runnable} which will be executed if the user does not setup 783 * device security or if lock screen is unlocked 784 */ showLockScreen(@onNull Context context, @NonNull Runnable onSuccess)785 public static void showLockScreen(@NonNull Context context, @NonNull Runnable onSuccess) { 786 final KeyguardManager keyguardManager = 787 context.getSystemService(KeyguardManager.class); 788 789 if (keyguardManager.isDeviceSecure()) { 790 final BiometricPrompt.AuthenticationCallback authenticationCallback = 791 new BiometricPrompt.AuthenticationCallback() { 792 @Override 793 public void onAuthenticationSucceeded( 794 BiometricPrompt.AuthenticationResult result) { 795 onSuccess.run(); 796 } 797 798 @Override 799 public void onAuthenticationError(int errorCode, CharSequence errString) { 800 // Do nothing 801 } 802 }; 803 804 final int userId = UserHandle.myUserId(); 805 final BiometricPrompt biometricPrompt = new BiometricPrompt.Builder(context) 806 .setTitle(context.getText(R.string.wifi_dpp_lockscreen_title)) 807 .setDeviceCredentialAllowed(true) 808 .setTextForDeviceCredential( 809 /* title= */ null, 810 Utils.getConfirmCredentialStringForUser( 811 context, userId, Utils.getCredentialType(context, userId)), 812 /* description= */ null) 813 .build(); 814 final Handler handler = new Handler(Looper.getMainLooper()); 815 biometricPrompt.authenticate( 816 new CancellationSignal(), 817 handler::post, 818 authenticationCallback); 819 } else { 820 onSuccess.run(); 821 } 822 } 823 } 824