1、问题背景
在手机开发过程中,经常遇见各种运营商名称显示问题,总结下PLMN SPN来源。
2、PLMN显示
网络名称显示这部分比较复杂,Spec对这也有明确的规定,根据其优先级由高往低介绍(其优先级参考TS 22.101)
2.1、Eons
Enhanced Operator Name String此种方式的名称是存放在EF_PNN(PLMN Network Name, fid: 6FC5)和EF_OPL(Operator PLMN List, fid: 6FC6)中。
EF_OPL中存放的是LAC和EF_PNN中的Record Identifier
EF_PNN中存放的是Network Name,也就是具体的名称了。
如果注册上的网络是HPLMN,那么EF_OPL返回的Record Identifier就是1。
如果不是HPLMN的话,就根据LAC在EF_OPL中寻找对应的Record Identifier。
然后根据OPL的Record Identifier,在PNN中找对应的Network Name。
这里需要声明一下,Record Identifier是基于1的,而EF_PNN的记录是基于0的。也就是说,Record Identifier是1,那匹配的是EF_PNN中的第0条记录。 这个分的代码可以参考SIMRecords.java中的getEonsIfExist()方法
相关Log打印如下
03-30 15:06:11.836991 2247 2426 D MtkSIMRecords: [SIMRecords] EONS getEonsIfExist: plmn is 405867 nLac is *9 bLongNameRequired: true (slot 0)
03-30 15:06:11.840562 2247 2426 D MtkSIMRecords: [SIMRecords] EONS getEonsIfExist: plmn is 405867 nLac is *9 bLongNameRequired: false (slot 0)
注意:有些新的5G卡对应的Eons目录如下,不是EF_PNN和EF_OPL
static public final int EF_OPL5G = 0x4F08; // ADF(USIM)/5FC0/4F08
2.2、CPHS ONS
Common PCN(Personal Communications Network) Handset Specification Operator Name String
需要当前注册的是HPLMN网络
a.如果SIM中的CPHS ONS的长格式文件(fid:6F14, long name)读取成功,用此当作网络名称。
b.如果SIM中的CPHS ONS的短格式文件(fid:6F18, short name)读取成功,用此当作网络名称。
这个分的代码可以参考SIMRecords.java中的读取CPHSOns文件的部分
注意此处CHPS ONS只存在于GSM文件目录,使用
static final int EF_SPN_CPHS = 0x6F14; static final int EF_SPN_SHORT_CPHS = 0x6F18;
@Override
protected String getEFPath(int efid) {
switch(efid) {
case EF_MAILBOX_CPHS:
case EF_VOICE_MAIL_INDICATOR_CPHS:
case EF_CFF_CPHS:
case EF_SPN_CPHS:
case EF_SPN_SHORT_CPHS:
// CPHS is SIM card's enhancement but not USIM card, so we should return DF_GSM.
//case EF_FDN:
//case EF_MSISDN:
//case EF_EXT2:
case EF_INFO_CPHS:
case EF_CSP_CPHS:
return MF_SIM + DF_GSM;
case MtkIccConstants.EF_SMSP: // [ALPS01206315] Support EF_SMSP
case EF_SDN:
case MtkIccConstants.EF_ECC:
return MF_SIM + DF_ADF;
case MtkIccConstants.EF_PSISMSC:
return MF_SIM + DF_TELECOM;
case MtkIccConstants.EF_RAT: // ALPS00302702 RAT balancing (ADF(USIM)/7F66/5F30/EF_RAT)
return DF_ADF + "7F66" + "5F30";
case MtkIccConstants.EF_OPL5G:
return MF_SIM + DF_ADF + "5FC0";
case MtkIccConstants.EF_USIM_GBABP:
case MtkIccConstants.EF_USIM_GBANL:
return MF_SIM + DF_ADF;
default:
Rlog.d(LOG_TAG_EX, "Usim aosp default getEFPath.");
return super.getEFPath(efid);
}
}
2.3、NITZ
此名称是由所注册的网络下发给手机的,参考TS22.042。如果网络有下发这个数据给手机,modem会通过”+CIEV: 10”把数据传给AP端,AP端会用这个数据来当成网络名称,另外AP端还会把这个数据和对应的网络PLMN一同记下来,当之后注册上的网络仍是这个PLMN,这显示的网络名称还会是这个网络名称。
这部分的代码可以参考ril_nw.c中的onNitzOperNameReceived()
2.4、ROM
这一个是存储在手机flash中的,目前的存储方式是xml文件。如果是有打开支持MVNO的option,那么以下xml都依次读取,如果读取到就终止往下读了。如果没有MVNO,那么仅仅读取spn-conf.xml。如果没有读取到那么显示plmn号了。
Virtual-spn-conf-by-efspn.xml
Virtual-spn-conf-by-imsi.xml
Virtual-spn-conf-by-efpnn.xml
Virtual-spn-conf-by-efgid1.xml
Spn-conf.xml
这个分的代码可以参考ril.java中的 lookupOperatorName()方法和SpnOverride类
3、SPN
接下来看另一个重要的名称,Service Provider Name,也就是SPN。SPN是卡中的一个文件,fid是6F46。读取SPN首先要看看SST中是否有这个栏位,并且这个栏位是否是activity的。
经常遇到有时候在keyguard上显示的名称和状态栏上的不一样,或者明明是这个运营商的却显示另外一个运营商的名称等等奇怪的现象,这些现象有些是和SPN是有关系的。所以我们需要了解一下SPN的显示规则。SPN有一个字节来规定其显示规则(参考Spec TS 31.102)
这一个字节仅用到第一和第二位,
(1)当注册到网络是HPLMN或者是在plmn属于EF_SPDI中的网络时(默认显示SPN),该字节的第一位的取值意义:
1:需要显示PLMN
0:不需要显示PLMN
(2)当注册到网络不是HPLMN也不在EF_SPDI中的网络时(默认显示PLMN),该字节的第二位取值意义:
1:不需要显示SPN
0:要显示SPN
这两个bit组成的值叫做display condition。这里的plmn指的是网络名称哦,不是指plmn号。所以会有遇到只显示spn而不显示网络名称的情况,并且spn的名称是sim文件中读出来的,其内容不确定的。