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