1 /* 2 * Copyright (C) 2019 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.internal.telephony.cdnr; 18 19 import static com.android.internal.telephony.cdnr.EfData.EF_SOURCE_CARRIER_API; 20 import static com.android.internal.telephony.cdnr.EfData.EF_SOURCE_CARRIER_CONFIG; 21 import static com.android.internal.telephony.cdnr.EfData.EF_SOURCE_CSIM; 22 import static com.android.internal.telephony.cdnr.EfData.EF_SOURCE_DATA_OPERATOR_SIGNALLING; 23 import static com.android.internal.telephony.cdnr.EfData.EF_SOURCE_ERI; 24 import static com.android.internal.telephony.cdnr.EfData.EF_SOURCE_MODEM_CONFIG; 25 import static com.android.internal.telephony.cdnr.EfData.EF_SOURCE_RUIM; 26 import static com.android.internal.telephony.cdnr.EfData.EF_SOURCE_SIM; 27 import static com.android.internal.telephony.cdnr.EfData.EF_SOURCE_USIM; 28 import static com.android.internal.telephony.cdnr.EfData.EF_SOURCE_VOICE_OPERATOR_SIGNALLING; 29 30 import android.annotation.NonNull; 31 import android.content.Context; 32 import android.content.res.Resources; 33 import android.os.PersistableBundle; 34 import android.telephony.CarrierConfigManager; 35 import android.telephony.ServiceState; 36 import android.text.TextUtils; 37 import android.util.LocalLog; 38 import android.util.SparseArray; 39 40 import com.android.internal.telephony.GsmCdmaPhone; 41 import com.android.internal.telephony.Phone; 42 import com.android.internal.telephony.cdnr.EfData.EFSource; 43 import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState; 44 import com.android.internal.telephony.uicc.IccRecords; 45 import com.android.internal.telephony.uicc.IccRecords.CarrierNameDisplayConditionBitmask; 46 import com.android.internal.telephony.uicc.IccRecords.OperatorPlmnInfo; 47 import com.android.internal.telephony.uicc.IccRecords.PlmnNetworkName; 48 import com.android.internal.telephony.uicc.RuimRecords; 49 import com.android.internal.telephony.uicc.SIMRecords; 50 import com.android.internal.util.IndentingPrintWriter; 51 import com.android.telephony.Rlog; 52 53 import java.util.Arrays; 54 import java.util.Collections; 55 import java.util.List; 56 import java.util.Locale; 57 import java.util.Objects; 58 59 /** Carrier display name resolver. */ 60 public class CarrierDisplayNameResolver { 61 private static final boolean DBG = true; 62 private static final String TAG = "CDNR"; 63 64 /** 65 * Only display SPN in home network, and PLMN network name in roaming network. 66 */ 67 @CarrierNameDisplayConditionBitmask 68 private static final int DEFAULT_CARRIER_NAME_DISPLAY_CONDITION_BITMASK = 0; 69 70 private static final CarrierDisplayNameConditionRule DEFAULT_CARRIER_DISPLAY_NAME_RULE = 71 new CarrierDisplayNameConditionRule(DEFAULT_CARRIER_NAME_DISPLAY_CONDITION_BITMASK); 72 73 private final SparseArray<EfData> mEf = new SparseArray<>(); 74 75 private final LocalLog mLocalLog; 76 private final Context mContext; 77 private final GsmCdmaPhone mPhone; 78 private final CarrierConfigManager mCCManager; 79 80 private CarrierDisplayNameData mCarrierDisplayNameData; 81 82 /** 83 * The priority of ef source. Lower index means higher priority. 84 */ 85 private static final List<Integer> EF_SOURCE_PRIORITY = 86 Arrays.asList( 87 EF_SOURCE_CARRIER_API, 88 EF_SOURCE_CARRIER_CONFIG, 89 EF_SOURCE_ERI, 90 EF_SOURCE_USIM, 91 EF_SOURCE_SIM, 92 EF_SOURCE_CSIM, 93 EF_SOURCE_RUIM, 94 EF_SOURCE_VOICE_OPERATOR_SIGNALLING, 95 EF_SOURCE_DATA_OPERATOR_SIGNALLING, 96 EF_SOURCE_MODEM_CONFIG); 97 CarrierDisplayNameResolver(GsmCdmaPhone phone)98 public CarrierDisplayNameResolver(GsmCdmaPhone phone) { 99 mLocalLog = new LocalLog(32); 100 mContext = phone.getContext(); 101 mPhone = phone; 102 mCCManager = (CarrierConfigManager) mContext.getSystemService( 103 Context.CARRIER_CONFIG_SERVICE); 104 } 105 106 /** 107 * Update the ef from Ruim. If {@code ruim} is null, the ef records from this source will be 108 * removed. 109 * 110 * @param ruim Ruim records. 111 */ updateEfFromRuim(RuimRecords ruim)112 public void updateEfFromRuim(RuimRecords ruim) { 113 int key = getSourcePriority(EF_SOURCE_RUIM); 114 if (ruim == null) { 115 mEf.remove(key); 116 } else { 117 mEf.put(key, new RuimEfData(ruim)); 118 } 119 } 120 121 /** 122 * Update the ef from Usim. If {@code usim} is null, the ef records from this source will be 123 * removed. 124 * 125 * @param usim Usim records. 126 */ updateEfFromUsim(SIMRecords usim)127 public void updateEfFromUsim(SIMRecords usim) { 128 int key = getSourcePriority(EF_SOURCE_USIM); 129 if (usim == null) { 130 mEf.remove(key); 131 } else { 132 mEf.put(key, new UsimEfData(usim)); 133 } 134 } 135 136 /** 137 * Update the ef from carrier config. If {@code config} is null, the ef records from this source 138 * will be removed. 139 * 140 * @param config carrier config. 141 */ updateEfFromCarrierConfig(PersistableBundle config)142 public void updateEfFromCarrierConfig(PersistableBundle config) { 143 int key = getSourcePriority(EF_SOURCE_CARRIER_CONFIG); 144 if (config == null) { 145 mEf.remove(key); 146 } else { 147 mEf.put(key, new CarrierConfigEfData(config)); 148 } 149 } 150 151 /** 152 * Update the ef for CDMA eri text. The ef records from this source will be set all of the 153 * following situation are satisfied. 154 * 155 * 1. {@code eriText} is neither empty nor null. 156 * 2. Current network is CDMA or CdmaLte 157 * 3. ERI is allowed. 158 * 159 * @param eriText 160 */ updateEfForEri(String eriText)161 public void updateEfForEri(String eriText) { 162 PersistableBundle config = getCarrierConfig(); 163 int key = getSourcePriority(EF_SOURCE_ERI); 164 if (!TextUtils.isEmpty(eriText) && (mPhone.isPhoneTypeCdma() || mPhone.isPhoneTypeCdmaLte()) 165 && config.getBoolean(CarrierConfigManager.KEY_ALLOW_ERI_BOOL)) { 166 mEf.put(key, new EriEfData(eriText)); 167 } else { 168 mEf.remove(key); 169 } 170 } 171 172 /** 173 * Update the ef for brandOverride. If {@code operatorName} is empty or null, the ef records 174 * from this source will be removed. 175 * 176 * @param operatorName operator name from brand override. 177 */ updateEfForBrandOverride(String operatorName)178 public void updateEfForBrandOverride(String operatorName) { 179 int key = getSourcePriority(EF_SOURCE_CARRIER_API); 180 if (TextUtils.isEmpty(operatorName)) { 181 mEf.remove(key); 182 } else { 183 mEf.put(key, 184 new BrandOverrideEfData(operatorName, getServiceState().getOperatorNumeric())); 185 } 186 } 187 188 /** Get the resolved carrier display name. */ getCarrierDisplayNameData()189 public CarrierDisplayNameData getCarrierDisplayNameData() { 190 resolveCarrierDisplayName(); 191 return mCarrierDisplayNameData; 192 } 193 194 @Override toString()195 public String toString() { 196 StringBuilder sb = new StringBuilder(); 197 for (int i = 0; i < mEf.size(); i++) { 198 EfData p = mEf.valueAt(i); 199 sb.append("{spnDisplayCondition = " + p.getServiceProviderNameDisplayCondition() 200 + ", spn = " + p.getServiceProviderName() 201 + ", spdiList = " + p.getServiceProviderDisplayInformation() 202 + ", pnnList = " + p.getPlmnNetworkNameList() 203 + ", oplList = " + p.getOperatorPlmnList() 204 + ", ehplmn = " + p.getEhplmnList() 205 + "}, "); 206 } 207 sb.append(", roamingFromSS = " + getServiceState().getRoaming()); 208 sb.append(", registeredPLMN = " + getServiceState().getOperatorNumeric()); 209 return sb.toString(); 210 } 211 212 /** 213 * Dumps information for carrier display name resolver. 214 * @param pw information printer. 215 */ dump(IndentingPrintWriter pw)216 public void dump(IndentingPrintWriter pw) { 217 pw.println("CDNR:"); 218 pw.increaseIndent(); 219 pw.println("fields = " + toString()); 220 pw.println("carrierDisplayNameData = " + mCarrierDisplayNameData); 221 pw.decreaseIndent(); 222 223 pw.println("CDNR local log:"); 224 pw.increaseIndent(); 225 mLocalLog.dump(pw); 226 pw.decreaseIndent(); 227 } 228 229 @NonNull getCarrierConfig()230 private PersistableBundle getCarrierConfig() { 231 PersistableBundle config = mCCManager.getConfigForSubId(mPhone.getSubId()); 232 if (config == null) config = CarrierConfigManager.getDefaultConfig(); 233 return config; 234 } 235 236 @NonNull getDisplayRule()237 private CarrierDisplayNameConditionRule getDisplayRule() { 238 for (int i = 0; i < mEf.size(); i++) { 239 if (mEf.valueAt(i).getServiceProviderNameDisplayCondition() 240 != IccRecords.INVALID_CARRIER_NAME_DISPLAY_CONDITION_BITMASK) { 241 return new CarrierDisplayNameConditionRule( 242 mEf.valueAt(i).getServiceProviderNameDisplayCondition()); 243 } 244 } 245 return DEFAULT_CARRIER_DISPLAY_NAME_RULE; 246 } 247 248 @NonNull getEfSpdi()249 private List<String> getEfSpdi() { 250 for (int i = 0; i < mEf.size(); i++) { 251 if (mEf.valueAt(i).getServiceProviderDisplayInformation() != null) { 252 return mEf.valueAt(i).getServiceProviderDisplayInformation(); 253 } 254 } 255 return Collections.EMPTY_LIST; 256 } 257 258 @NonNull getEfSpn()259 private String getEfSpn() { 260 for (int i = 0; i < mEf.size(); i++) { 261 if (!TextUtils.isEmpty(mEf.valueAt(i).getServiceProviderName())) { 262 return mEf.valueAt(i).getServiceProviderName(); 263 } 264 } 265 return ""; 266 } 267 268 @NonNull getEfOpl()269 private List<OperatorPlmnInfo> getEfOpl() { 270 for (int i = 0; i < mEf.size(); i++) { 271 if (mEf.valueAt(i).getOperatorPlmnList() != null) { 272 return mEf.valueAt(i).getOperatorPlmnList(); 273 } 274 } 275 return Collections.EMPTY_LIST; 276 } 277 278 @NonNull getEfPnn()279 private List<PlmnNetworkName> getEfPnn() { 280 for (int i = 0; i < mEf.size(); i++) { 281 if (mEf.valueAt(i).getPlmnNetworkNameList() != null) { 282 return mEf.valueAt(i).getPlmnNetworkNameList(); 283 } 284 } 285 return Collections.EMPTY_LIST; 286 } 287 getCarrierDisplayNameFromEf()288 private CarrierDisplayNameData getCarrierDisplayNameFromEf() { 289 CarrierDisplayNameConditionRule displayRule = getDisplayRule(); 290 291 String registeredPlmnName = getServiceState().getOperatorAlpha(); 292 String registeredPlmnNumeric = getServiceState().getOperatorNumeric(); 293 List<String> efSpdi = getEfSpdi(); 294 295 // Currently use the roaming state from ServiceState. 296 // EF_SPDI is only used when determine the service provider name and PLMN network name 297 // display condition rule. 298 // All the PLMNs will be considered HOME PLMNs if there is a brand override. 299 boolean isRoaming = getServiceState().getRoaming() 300 && !efSpdi.contains(registeredPlmnNumeric); 301 String spn = getEfSpn(); 302 303 // Resolve the PLMN network name 304 List<OperatorPlmnInfo> efOpl = getEfOpl(); 305 List<PlmnNetworkName> efPnn = getEfPnn(); 306 307 String plmn = null; 308 if (isRoaming) { 309 plmn = registeredPlmnName; 310 } else { 311 if (efOpl.isEmpty()) { 312 // If the EF_OPL is not present, then the first record in EF_PNN is used for the 313 // default network name when registered in the HPLMN or an EHPLMN(if the EHPLMN 314 // list is present). 315 plmn = efPnn.isEmpty() ? "" : getPlmnNetworkName(efPnn.get(0)); 316 } else { 317 // TODO: Check the TAC/LAC & registered PLMN numeric in OPL list to determine which 318 // PLMN name should be used to override the current one. 319 } 320 } 321 322 // If no PLMN override is present, then the PLMN should be displayed numerically. 323 if (TextUtils.isEmpty(plmn)) { 324 plmn = TextUtils.isEmpty(registeredPlmnName) ? registeredPlmnNumeric 325 : registeredPlmnName; 326 } 327 328 boolean showSpn = displayRule.shouldShowSpn(isRoaming, spn); 329 boolean showPlmn = TextUtils.isEmpty(spn) || displayRule.shouldShowPlmn(isRoaming, plmn); 330 331 return new CarrierDisplayNameData.Builder() 332 .setSpn(spn) 333 .setShowSpn(showSpn) 334 .setPlmn(plmn) 335 .setShowPlmn(showPlmn) 336 .build(); 337 } 338 getCarrierDisplayNameFromWifiCallingOverride( CarrierDisplayNameData rawCarrierDisplayNameData)339 private CarrierDisplayNameData getCarrierDisplayNameFromWifiCallingOverride( 340 CarrierDisplayNameData rawCarrierDisplayNameData) { 341 PersistableBundle config = getCarrierConfig(); 342 boolean useRootLocale = config.getBoolean(CarrierConfigManager.KEY_WFC_SPN_USE_ROOT_LOCALE); 343 Resources r = mContext.getResources(); 344 if (useRootLocale) r.getConfiguration().setLocale(Locale.ROOT); 345 String[] wfcSpnFormats = r.getStringArray(com.android.internal.R.array.wfcSpnFormats); 346 WfcCarrierNameFormatter wfcFormatter = new WfcCarrierNameFormatter(config, wfcSpnFormats, 347 getServiceState().getState() == ServiceState.STATE_POWER_OFF); 348 349 // Override the spn, data spn, plmn by wifi-calling 350 String wfcSpn = wfcFormatter.formatVoiceName(rawCarrierDisplayNameData.getSpn()); 351 String wfcDataSpn = wfcFormatter.formatDataName(rawCarrierDisplayNameData.getSpn()); 352 List<PlmnNetworkName> efPnn = getEfPnn(); 353 String plmn = efPnn.isEmpty() ? "" : getPlmnNetworkName(efPnn.get(0)); 354 String wfcPlmn = wfcFormatter.formatVoiceName( 355 TextUtils.isEmpty(plmn) ? rawCarrierDisplayNameData.getPlmn() : plmn); 356 357 CarrierDisplayNameData result = rawCarrierDisplayNameData; 358 if (!TextUtils.isEmpty(wfcSpn) && !TextUtils.isEmpty(wfcDataSpn)) { 359 result = new CarrierDisplayNameData.Builder() 360 .setSpn(wfcSpn) 361 .setDataSpn(wfcDataSpn) 362 .setShowSpn(true) 363 .build(); 364 } else if (!TextUtils.isEmpty(wfcPlmn)) { 365 result = new CarrierDisplayNameData.Builder() 366 .setPlmn(wfcPlmn) 367 .setShowPlmn(true) 368 .build(); 369 } 370 return result; 371 } 372 373 /** 374 * Override the given carrier display name data {@code data} by out of service rule. 375 * @param data the carrier display name data need to be overridden. 376 * @return overridden carrier display name data. 377 */ getOutOfServiceDisplayName(CarrierDisplayNameData data)378 private CarrierDisplayNameData getOutOfServiceDisplayName(CarrierDisplayNameData data) { 379 // Out of service/Power off/Emergency Only override 380 // 1) In flight mode(service state is ServiceState.STATE_POWER_OFF), or the service 381 // state is ServiceState.STATE_OUT_OF_SERVICE but emergency call is not allowed. 382 // showPlmn = true 383 // Only show "No Service" as PLMN 384 // 385 // 2) Out of service but emergency call is allowed. 386 // showPlmn = true 387 // Only show "Emergency call only" as PLMN 388 String plmn = null; 389 boolean isSimReady = mPhone.getUiccCardApplication() != null 390 && mPhone.getUiccCardApplication().getState() == AppState.APPSTATE_READY; 391 boolean forceDisplayNoService = 392 mPhone.getServiceStateTracker().shouldForceDisplayNoService() && !isSimReady; 393 ServiceState ss = getServiceState(); 394 if (ss.getState() == ServiceState.STATE_POWER_OFF 395 || forceDisplayNoService || !Phone.isEmergencyCallOnly()) { 396 plmn = mContext.getResources().getString( 397 com.android.internal.R.string.lockscreen_carrier_default); 398 } else { 399 plmn = mContext.getResources().getString( 400 com.android.internal.R.string.emergency_calls_only); 401 } 402 return new CarrierDisplayNameData.Builder() 403 .setSpn(data.getSpn()) 404 .setDataSpn(data.getDataSpn()) 405 .setShowSpn(data.shouldShowSpn()) 406 .setPlmn(plmn) 407 .setShowPlmn(true) 408 .build(); 409 } 410 resolveCarrierDisplayName()411 private void resolveCarrierDisplayName() { 412 CarrierDisplayNameData data = getCarrierDisplayNameFromEf(); 413 if (DBG) Rlog.d(TAG, "CarrierName from EF: " + data); 414 if (getCombinedRegState(getServiceState()) == ServiceState.STATE_IN_SERVICE) { 415 if (mPhone.isWifiCallingEnabled()) { 416 data = getCarrierDisplayNameFromWifiCallingOverride(data); 417 if (DBG) { 418 Rlog.d(TAG, "CarrierName override by wifi-calling " + data); 419 } 420 } 421 } else { 422 data = getOutOfServiceDisplayName(data); 423 if (DBG) Rlog.d(TAG, "Out of service carrierName " + data); 424 } 425 426 if (!Objects.equals(mCarrierDisplayNameData, data)) { 427 mLocalLog.log(String.format("ResolveCarrierDisplayName: %s", data.toString())); 428 } 429 430 mCarrierDisplayNameData = data; 431 } 432 433 /** 434 * Get the PLMN network name from the {@link PlmnNetworkName} object. 435 * @param name the {@link PlmnNetworkName} object may contain the full and short version of PLMN 436 * network name. 437 * @return full/short version PLMN network name if one of those is existed, otherwise return an 438 * empty string. 439 */ getPlmnNetworkName(PlmnNetworkName name)440 private static String getPlmnNetworkName(PlmnNetworkName name) { 441 if (name == null) return ""; 442 if (!TextUtils.isEmpty(name.fullName)) return name.fullName; 443 if (!TextUtils.isEmpty(name.shortName)) return name.shortName; 444 return ""; 445 } 446 447 /** 448 * Get the priority of the source of ef object. If {@code source} is not in the priority list, 449 * return {@link Integer#MAX_VALUE}. 450 * @param source source of ef object. 451 * @return the priority of the source of ef object. 452 */ getSourcePriority(@FSource int source)453 private static int getSourcePriority(@EFSource int source) { 454 int priority = EF_SOURCE_PRIORITY.indexOf(source); 455 if (priority == -1) priority = Integer.MAX_VALUE; 456 return priority; 457 } 458 459 private static final class CarrierDisplayNameConditionRule { 460 private int mDisplayConditionBitmask; 461 CarrierDisplayNameConditionRule(int carrierDisplayConditionBitmask)462 CarrierDisplayNameConditionRule(int carrierDisplayConditionBitmask) { 463 mDisplayConditionBitmask = carrierDisplayConditionBitmask; 464 } 465 shouldShowSpn(boolean isRoaming, String spn)466 boolean shouldShowSpn(boolean isRoaming, String spn) { 467 //Check if show SPN is required when roaming. 468 Boolean showSpnInRoaming = ((mDisplayConditionBitmask 469 & IccRecords.CARRIER_NAME_DISPLAY_CONDITION_BITMASK_SPN) 470 == IccRecords.CARRIER_NAME_DISPLAY_CONDITION_BITMASK_SPN); 471 472 return !TextUtils.isEmpty(spn) && (!isRoaming || showSpnInRoaming); 473 } 474 shouldShowPlmn(boolean isRoaming, String plmn)475 boolean shouldShowPlmn(boolean isRoaming, String plmn) { 476 // Check if show PLMN is required when not roaming. 477 Boolean showPlmnInNotRoaming = ((mDisplayConditionBitmask 478 & IccRecords.CARRIER_NAME_DISPLAY_CONDITION_BITMASK_PLMN) 479 == IccRecords.CARRIER_NAME_DISPLAY_CONDITION_BITMASK_PLMN); 480 481 return !TextUtils.isEmpty(plmn) && (isRoaming || showPlmnInNotRoaming); 482 } 483 484 @Override toString()485 public String toString() { 486 return String.format("{ SPN_bit = %d, PLMN_bit = %d }", 487 mDisplayConditionBitmask 488 & IccRecords.CARRIER_NAME_DISPLAY_CONDITION_BITMASK_SPN, 489 mDisplayConditionBitmask 490 & IccRecords.CARRIER_NAME_DISPLAY_CONDITION_BITMASK_PLMN); 491 } 492 } 493 getServiceState()494 private ServiceState getServiceState() { 495 return mPhone.getServiceStateTracker().getServiceState(); 496 } 497 498 /** 499 * WiFi-Calling formatter for carrier name. 500 */ 501 private static final class WfcCarrierNameFormatter { 502 final String mVoiceFormat; 503 final String mDataFormat; 504 WfcCarrierNameFormatter(@onNull PersistableBundle config, @NonNull String[] wfcFormats, boolean inFlightMode)505 WfcCarrierNameFormatter(@NonNull PersistableBundle config, 506 @NonNull String[] wfcFormats, boolean inFlightMode) { 507 int voiceIdx = config.getInt(CarrierConfigManager.KEY_WFC_SPN_FORMAT_IDX_INT); 508 int dataIdx = config.getInt(CarrierConfigManager.KEY_WFC_DATA_SPN_FORMAT_IDX_INT); 509 int flightModeIdx = config.getInt( 510 CarrierConfigManager.KEY_WFC_FLIGHT_MODE_SPN_FORMAT_IDX_INT); 511 512 if (voiceIdx < 0 || voiceIdx >= wfcFormats.length) { 513 Rlog.e(TAG, "updateSpnDisplay: KEY_WFC_SPN_FORMAT_IDX_INT out of bounds: " 514 + voiceIdx); 515 voiceIdx = 0; 516 } 517 518 if (dataIdx < 0 || dataIdx >= wfcFormats.length) { 519 Rlog.e(TAG, "updateSpnDisplay: KEY_WFC_DATA_SPN_FORMAT_IDX_INT out of bounds: " 520 + dataIdx); 521 dataIdx = 0; 522 } 523 524 if (flightModeIdx < 0 || flightModeIdx >= wfcFormats.length) { 525 // KEY_WFC_FLIGHT_MODE_SPN_FORMAT_IDX_INT out of bounds. Use the value from 526 // voiceIdx. 527 flightModeIdx = voiceIdx; 528 } 529 530 // flight mode 531 if (inFlightMode) { 532 voiceIdx = flightModeIdx; 533 } 534 535 mVoiceFormat = voiceIdx != -1 ? wfcFormats[voiceIdx] : ""; 536 mDataFormat = dataIdx != -1 ? wfcFormats[dataIdx] : ""; 537 } 538 539 /** 540 * Format the given {@code name} using wifi-calling voice name formatter. 541 * @param name the string need to be formatted. 542 * @return formatted string if {@code name} is not empty, otherwise return {@code name}. 543 */ formatVoiceName(String name)544 public String formatVoiceName(String name) { 545 if (TextUtils.isEmpty(name)) return name; 546 return String.format(mVoiceFormat, name.trim()); 547 } 548 549 /** 550 * Format the given {@code name} using wifi-calling data name formatter. 551 * @param name the string need to be formatted. 552 * @return formatted string if {@code name} is not empty, otherwise return {@code name}. 553 */ formatDataName(String name)554 public String formatDataName(String name) { 555 if (TextUtils.isEmpty(name)) return name; 556 return String.format(mDataFormat, name.trim()); 557 } 558 } 559 560 /** 561 * Consider dataRegState if voiceRegState is OOS to determine SPN to be displayed. 562 * @param ss service state. 563 */ getCombinedRegState(ServiceState ss)564 private static int getCombinedRegState(ServiceState ss) { 565 if (ss.getState() != ServiceState.STATE_IN_SERVICE) return ss.getDataRegistrationState(); 566 return ss.getState(); 567 } 568 } 569