Android运营商名称显示之PLMN与SPN显示规则(原)

https://blog.csdn.net/u010961631/article/details/50682406

 前面的两节分别介绍了PLMN和SPN的读取方法,那么在锁屏、状态栏、通知栏这些地方的运营商名称究竟是来自于PLMN呢?还是来自于SPN呢?
        在3GPP中规定的运营商名称显示规则如下:
        1、名称可以为SPN或PLMN
        2、如果没有SPN文件,那么就显示PLMN
        3、若有SPN,并且注册的PLMN是HPLMN或者注册的PLMN在SIM卡文件EF_SPDI中,那么:
            (1)如果有SPN就要显示SPN
            (2)如果SPN的bit1 = 1, 则需要同时显示PLMN,如果SPN的bit1=0,则不需要同时显示PLMN
        4、若有SPN,注册的PLMN是Roaming PLMN且注册的PLMN也不在SIM卡文件EF_SPDI中,那么
            (1)显示PLMN
            (2)如果SPN的bit2=0,则需要同时显示SPN,如果SPN的bit2=1,则不需要同时显示SPN

        下面我们用代码来梳理上面的规则。
        在GsmServiceStateTracker中,接收到EVENT_SIM_RECORDS_LOADED消息或者ACTION_LOCALE_CHANGED广播后,就会触发SPN的更新显示机制。其入口为updateSpnDisplay():
        @GsmServiceStateTracker.java
        protected void updateSpnDisplay() {
            IccRecords iccRecords = mIccRecords;
            String plmn = null;
            boolean showPlmn = false;
            int rule = (iccRecords != null) ? iccRecords.getDisplayRule(mSS.getOperatorNumeric()) : 0;
            if (mSS.getVoiceRegState() == ServiceState.STATE_OUT_OF_SERVICE || mSS.getVoiceRegState() == ServiceState.STATE_EMERGENCY_ONLY) {
                //当前无网络
                showPlmn = true;
                if (mEmergencyOnly) {
                    // No service but emergency call allowed
                    plmn = Resources.getSystem().  getText(com.android.internal.R.string.emergency_calls_only).toString();
                } else {
                    // No service at all
                    plmn = Resources.getSystem().  getText(com.android.internal.R.string.lockscreen_carrier_default).toString();
                }
            } else if (mSS.getVoiceRegState() == ServiceState.STATE_IN_SERVICE) {
                //当前注册网络OK
                plmn = mSS.getOperatorAlphaLong();
                showPlmn = !TextUtils.isEmpty(plmn) && ((rule & SIMRecords.SPN_RULE_SHOW_PLMN) == SIMRecords.SPN_RULE_SHOW_PLMN);
            } else {
                if (DBG) log("updateSpnDisplay: radio is off w/ showPlmn=" + showPlmn + " plmn=" + plmn);
            }
 
 
            String spn = (iccRecords != null) ? iccRecords.getServiceProviderName() : "";
            boolean showSpn = !TextUtils.isEmpty(spn)
                && ((rule & SIMRecords.SPN_RULE_SHOW_SPN)
                        == SIMRecords.SPN_RULE_SHOW_SPN);
 
 
            //发送Intent通知
            if (showPlmn != mCurShowPlmn
                    || showSpn != mCurShowSpn
                    || !TextUtils.equals(spn, mCurSpn)
                    || !TextUtils.equals(plmn, mCurPlmn)) {
                Intent intent = new Intent(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION);
                intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
                intent.putExtra(TelephonyIntents.EXTRA_SHOW_SPN, showSpn);
                intent.putExtra(TelephonyIntents.EXTRA_SPN, spn);
                intent.putExtra(TelephonyIntents.EXTRA_SHOW_PLMN, showPlmn);
                intent.putExtra(TelephonyIntents.EXTRA_PLMN, plmn);
                mPhone.getContext().sendStickyBroadcastAsUser(intent, UserHandle.ALL);
            }
 
 
            mCurShowSpn = showSpn;
            mCurShowPlmn = showPlmn;
            mCurSpn = spn;
            mCurPlmn = plmn;
        }


上面的更新过程分为两步,分别完成SPN的读取和广播的发送,我们主要来看读取SPN的过程。
        在读取过程中,先对当前的网络状态进行分类:
        1、如果当前网络处于STATE_OUT_OF_SERVICE或者STATE_EMERGENCY_ONLY状态,则分别显示“No service”和“Emergency calls only”字串。
        2、对于当前网络注册成功的情况(STATE_IN_SERVICE),则根据3GPP协议来确定当前显示的PLMN显示规则

        我们主要关注第二种情况下显示规则的确认。
        我们先来看一下显示的rule,他是通过以下调用来定义的:
        int rule = (iccRecords != null) ? iccRecords.getDisplayRule(mSS.getOperatorNumeric()) : 0;
        也就时说,这里的rule是通过SIMRecords的getDisplayRule()方法得到的:

        @SIMRecords.java
        public int getDisplayRule(String plmn) {
            int rule;
            if (TextUtils.isEmpty(mSpn) || mSpnDisplayCondition == -1) {
                //如果SPN为空,则显示PLMN(Rule 2)
                rule = SPN_RULE_SHOW_PLMN;
            } else if (isOnMatchingPlmn(plmn)) {
                //如果当前注册的PLMN为HPLMN或者注册的PLMN存在于SIM中的EF_SPDI字段内,则显示SPN(Rule 3.1)
                rule = SPN_RULE_SHOW_SPN;
                if ((mSpnDisplayCondition & 0x01) == 0x01) {
                    //如果SPN的bit1=1,则需要同时显示SPN和PLMN(Rule 3.2)
                    rule |= SPN_RULE_SHOW_PLMN;
                }
            } else {
                //如果注册的PLMN为Roaming PLMN,并且注册的PLMN不在EF_SPDI中,则显示PLMN(Rule 4.1)
                rule = SPN_RULE_SHOW_PLMN;
                if ((mSpnDisplayCondition & 0x02) == 0x00) {
                    //如果注册的PLMN为Roaming PLMN,并且注册的PLMN不在EF_SPDI中,并且SPN的bit2=0,则要同时显示PLMN和SPN(Rule 4.2)
                    rule |= SPN_RULE_SHOW_SPN;
                }
            }
            return rule;
        }


上面的过程刚好匹配3GPP对PLMN的显示规则,其中的mSpnDisplayCondition就是在SIMRecords获取到SPN时解析出来的data的bit1数据。当该方法结束时,返回出来了int类型的rule变量,该变量只有前两位bit有效,其中bit1=1代表显示SPN,bit2=1代表显示PLMN。
        然后回到updateSpnDisplay()方法中,此时将会通过mSS.getOperatorAlphaLong()得到当前的PLMN值,以及通过rule得到当前是否需要显示PLMN(showPlmn),然后通过iccRecords.getServiceProviderName()得到当前的SPN以及通过rule得到当前是否需要显示SPN。
        拿到上面的数据之后,就通过广播(SPN_STRINGS_UPDATED_ACTION)的形式通知其他模块SPN的变化。
        这就是SPN的显示机制,以下是该机制的逻辑图:

猜你喜欢

转载自blog.csdn.net/keyanting_2018/article/details/86759528
今日推荐