mmi处理流程

mmi概述

mmi是啥见点击打开链接

MMI是Man-Machine-Interface的缩写。从功能上来看,有一些从拨号盘拨出去的字符串,它的实际作用不是建立一个通话而是为了完成某种服务,这个就是MMI;从格式上来看,每一个从拨号盘输入的字符串,如果包含有*或者#,都可以称之为MMI.
 
MMI包括SS, USSD,Manufacturer defined MMI codes, SIM control codes等等。
Manufacturer defined MMI codes
一些嵌在手机里的服务码,比如你要查询一个手机的IMEI号码,你可以在拨号键盘输入*#06#,手机的IMEI码会自动弹出来。
SIM control codes
比如用来修改SIM卡PIN码的命令,在拨号键盘输入**04*1234*6789*6789#,这将会将SIM卡的PIN码从1234修改成为6789.
SS
Supplementary Service,补充服务,比如控制号码显示,呼叫转移等服务的号码串。这些号码串是每部GSM/UMTS/LTE手机中的固定服务号码,运营商是不能对它进行修改的。当手机系统接受到这些服务号码后,先在手机内部进行处理后,再传给网络。比如你随便拿个手机,在拨号盘输入*21*123456789#然后点击发送,这个时候你所有的来电都会直接转移到123456789。
USSD
与SS相对应的是Unstructured Supplementary Service Data。从字面上看,是非结构化的补充字符,曾经我以为字符串格式上的不同是SS和USSD间最大的区别,后来发现自己大错特错了:单从字符串格式上是没法区分的。那他俩到底有啥区别?一,USSD基本都是运营商根据自己的服务自己定制的,而SS就像上面说的是每个手机固有的;二,SS在发送给网络前,手机首先会自己处理一下,而USSD是完全透明的传送给网络。
SS和USSD都必须点击发送后才能生效,而Manufacturer defined MMI codes和SIM control codes都不需要点击,输入后直接生效。
 
MMI格式
手机支持如下格式的MMI。
Activation : *SC*SI#
Deactivation : #SC*SI#
Interrogation : *#SC*SI#
Registration :  *SC*SI# and **SC*SI#
Erasure : ##SC*SI#
 
Action:*,#,*#,**,##。
SC: Service Code, 由2-3位数字组成
SI:  Supplementary Information。
MMI串通常以*,#,*#,**,##等开头,以#结束。各个部分间以*隔开。


mmi framework流程

frameworks/opt/telephony/src/java/com/android/internal/telephony/MmiCode.java

frameworks/opt/telephony/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java

MmiCode是个接口,实现类有多个,例如Gsm相关的就是GsmMmiCode

frameworks/opt/telephony/src/java/com/android/internal/telephony/gsm/GSMPhone.java

    ArrayList <GsmMmiCode> mPendingMMIs = new ArrayList<GsmMmiCode>();
GsmPhone中定义了一个GsmMmiCode的队列,这样可以依次处理多个请求。

ss响应的处理流程

       mCi.setOnSs(this, EVENT_SS, null);
GsmPhone构造函数中定义了对SS消息的监听。

            case EVENT_SS:
                ar = (AsyncResult)msg.obj;
                Rlog.d(LOG_TAG, "Event EVENT_SS received");
                // SS data is already being handled through MMI codes.
                // So, this result if processed as MMI response would help
                // in re-using the existing functionality.
                GsmMmiCode mmi = new GsmMmiCode(this, mUiccApplication.get());
                mmi.processSsData(ar);
                break;
然后消息处理中调用processSsData。

 processSsData(AsyncResult data) {
        ...
        mIsSsInfo = true;
        try {
            SsData ssData = (SsData)data.result;
            parseSsData(ssData);
        ...
 }

取出SsData然后继续调用同名方法

   void parseSsData(SsData ssData) {
        CommandException ex;

        ex = CommandException.fromRilErrno(ssData.result);
        mSc = getScStringFromScType(ssData.serviceType);
        mAction = getActionStringFromReqType(ssData.requestType);
        Rlog.d(LOG_TAG, "parseSsData msc = " + mSc + ", action = " + mAction + ", ex = " + ex);

        switch (ssData.requestType) {
            case SS_ACTIVATION:
            case SS_DEACTIVATION:
            case SS_REGISTRATION:
            case SS_ERASURE:
                if ((ssData.result == RILConstants.SUCCESS) &&
                      ssData.serviceType.isTypeUnConditional()) {
                    ...
                    boolean cffEnabled = ((ssData.requestType == SsData.RequestType.SS_ACTIVATION ||
                            ssData.requestType == SsData.RequestType.SS_REGISTRATION) &&
                            isServiceClassVoiceorNone(ssData.serviceClass));
                    ...
                    mIccRecords.setVoiceCallForwardingFlag(1, cffEnabled, null);
                    ...
                }
                onSetComplete(null, new AsyncResult(null, ssData.cfInfo, ex));
                break;
            case SS_INTERROGATION:
                if (ssData.serviceType.isTypeClir()) {
                    Rlog.d(LOG_TAG, "CLIR INTERROGATION");
                    onGetClirComplete(new AsyncResult(null, ssData.ssInfo, ex));
                } else if (ssData.serviceType.isTypeCF()) {
                    Rlog.d(LOG_TAG, "CALL FORWARD INTERROGATION");
                    onQueryCfComplete(new AsyncResult(null, ssData.cfInfo, ex));
                } else {
                    onQueryComplete(new AsyncResult(null, ssData.ssInfo, ex));
                }
                break;
            default:
                Rlog.e(LOG_TAG, "Invaid requestType in SSData : " + ssData.requestType);
                break;
        }
    }
SS_ACTIVATION等枚举定义在 frameworks/opt/telephony/src/java/com/android/internal/telephony/gsm/SsData.java,表示请求类型。有两个分支,第一个是无条件呼叫转移相关信息的,第二个分支又有三个小分支:一是Clir;二是呼叫转移,三是默认处理。第一个大分支是主动设置后返回的结果,第二个大分支是查询interrogation后返回的结果。

onGetClirComplete,返回clir的情况

onQueryCfComplete,返回呼叫转移的情况

onQueryComplete,其它一些情况,例如呼叫等待,呼叫限制和CNAP

拨号流程中的mmi处理

 dialInternal (String dialString, UUSInfo uusInfo, int videoState, Bundle intentExtras)
            throws CallStateException {
      ...
      GsmMmiCode mmi =
                GsmMmiCode.newFromDialString(networkPortion, this, mUiccApplication.get());
      if (mmi == null) {
            /// M: For 3G VT only @{
            //return mCT.dial(newDialString, uusInfo, intentExtras);
            if (videoState == VideoProfile.STATE_AUDIO_ONLY) {
                return mCT.dial(newDialString, uusInfo, intentExtras);
            } else {
                if (!is3GVTEnabled()) {
                    throw new CallStateException("cannot vtDial for non-3GVT-capable device");
                }
                return mCT.vtDial(newDialString, uusInfo, intentExtras);
            }
            /// @}
        } else if (mmi.isTemporaryModeCLIR()) {
            /// M: For 3G VT only @{
            //return mCT.dial(mmi.mDialingNumber, mmi.getCLIRMode(), uusInfo, intentExtras);
            if (videoState == VideoProfile.STATE_AUDIO_ONLY) {
                return mCT.dial(mmi.mDialingNumber, mmi.getCLIRMode(), uusInfo, intentExtras);
            } else {
                if (!is3GVTEnabled()) {
                    throw new CallStateException("cannot vtDial for non-3GVT-capable device");
                }
                return mCT.vtDial(mmi.mDialingNumber, mmi.getCLIRMode(), uusInfo, intentExtras);
            }
            /// @}
        } else {
            /* M: SS part */
            Rlog.d(LOG_TAG, "[dial]mPendingMMIs.add(mmi) + " + mmi);
            /* M: SS part end */
            mPendingMMIs.add(mmi);
            mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null));
            mmi.processCode();

            // FIXME should this return null or something else?
            return null;
        }
}
拨号函数中先分析传递进去的号码得到mmi。如果mmi为null则是普通拨号;mmi.isTemporaryModeCLIR ()为true,即设置本次拨号让对方无法看到我方号码,然后拨号,只不过传递的参数不同;最后一种情况把mmi加入到队列中,然后调用mmi的处理方法processCode。
GsmMmiCode的processCode函数有点长,有595行,不贴代码了,讲下大概的流程。代码长但是大体结构不复杂,就是if..else if...else if.........else的结构:
1.CNAP处理
Calling Name Presentation这种业务是用户在申请这项业务时,可以向业务部门提交一份号码与名字的对照表。在这个号码与名字对照的信息输入移动通信系统后,如果这个用户呼叫其它号码时,就在呼叫接通振铃时,被叫用户的手机或终端上就会显示来话者的名字。
2.CLIP处理
主叫线识别提供(CLIP)业务,该业务是提供给被叫的一种补充业务,网络在呼叫建立期间向被叫用户传送主叫方的电话号码,若有可能也将传送主叫方子地址信息,在被叫终端上显示这些信息。其实就是来电显示业务。
3.ClIR处理
主叫线识别限制(CLIR)业务:该业务是提供给主叫的补充业务,使主叫用户能够限制将其号码和子地址提供给被叫用户。其实就是主叫隐藏业务。设置后返回成功有4种情况,看下系统strings中的字符串就一目了然这个功能是干啥的:
    <string name="CLIRDefaultOnNextCallOn" msgid="429415409145781923">"默认不显示本机号码,在下一次通话中也不显示"</string>
    <string name="CLIRDefaultOnNextCallOff" msgid="3092918006077864624">"默认不显示本机号码,但在下一次通话中显示"</string>
    <string name="CLIRDefaultOffNextCallOn" msgid="6179425182856418465">"默认显示本机号码,但在下一次通话中不显示"</string>
    <string name="CLIRDefaultOffNextCallOff" msgid="2567998633124408552">"默认显示本机号码,在下一次通话中也显示"</string>
4.COLP处理
被接线识别提供(COLP)业务,该业务是提供给主叫的补充业务,能够提供给主叫用户最终连接到的用户号码,若有可能也将提供子地址信息。当使用数据传送业务时,该业务可以使用户确认数据发送的最终目的地址。当呼叫发生前转后,该业务尤其有意义,它可以将前转以后的最终目的号码提供给主叫用户。看意思是拨号后,如果对方设置了呼叫转移,那么主叫方能看到呼叫转移的号码而不是主叫方输入的号码。
5.COLR处理
被接线识别限制(COLR)业务,该业务是提供给被叫的补充业务,使被接用户可以限制将其用户号码和子地址提供给主叫用户。和COLP相对应,被叫方禁止号码让主叫方看到。
6.呼叫转移处理
7.呼叫限制处理
对于出呼叫,有如下三种类型的呼叫限制 :
    BAOC(Barring of All Outgoing Calls,限制所有的出呼叫)。设置此项功能后,该移动电话只能接听来话呼叫,而不能拨打电话。(只能打进,不能打出除紧急电话和充值电话之外的所有电话,呼出提示电话已停机)不能发短信 ,但可以接收短信、拨打免费电话.例如中国移动对于预付费用户,在手机欠费的情况下,会在系统中将用户置为限制所有出呼的状态,以避免用户继续恶意欠费。在这种情况下,用户仅可以拨打紧急呼叫, 而呼叫其他普通用户时,将会听到:“您的手机已欠费,请您续缴话费”之类的提示音。
    BOIC(Barring of Outgoing International Calls,限制所有的国际出呼叫)。签约此业务的情况下,用户将不能呼出任何国际号码,可以发短信和接收短信。通过BOIC用户可以灵活控制自己的移动电话的国际长途拨号权。避免被人盗打国际长途电话,造成经济损失。
    限制除归属PLMN外的国际出呼叫(Barring of Outgoing International calls except those directed to the Home PLMN country)。 漫游分为国内漫游和国际漫游。归属PLMN指用户签约并入网时所选择的运营商。比如中国移动网的用户,当位于国内中国移动网覆盖范围时,就位于归属PLMN内;当他去泰国旅游,到国外运营商网络覆盖范围时,称为漫游出归属PLMN。签约了该业务的用户在漫游出归属PLMN的情况下,只能呼叫归属PLMN的号码,即拨打到中国国内的呼叫,而不能呼叫其他国际号码。
8. PWD处理
设置呼叫限制密码
9. WAIT呼叫等待
10.pin码和puk码的处理
PIN是手机卡的个人识别码,初始一般是1234或0000,当连续3次输入错误,手机卡将被锁定,此时需要PUK码进行解锁,PUK码是PIN解锁码,可向运营商索取,也可通过短信或网络获取,但PUK码连续错误10次,手机卡将被彻底锁定,无法恢复,只能再换卡了。
11.其他情况
都是向ril发送ussd数据
整个归类为上述11个分支。除了最后一个分支,分支的处理最后都是调用ril的相应方法向modem发送请求。
上述缩写解释部分来源于http://www.52rd.com/bbs/Dispbbs.asp?BoardID=63&ID=178392,部分是百度百科。

GsmPhone中其它Mmi相关方法

public boolean handlePinMmi(String dialString)  供app层调用,核心处理函数就是上小节详细分析的processCode
public void sendUssdResponse(String ussdMessge)  向ril发送ussd数据,不对数据做任何处理
public void onMMIDone(GsmMmiCode mmi, Object obj)  供GsmMmiCode使用,通知上层mmi处理完毕
private void onNetworkInitiatedUssd(GsmMmiCode mmi)  功能同onMMIDone,只被onIncomingUSSD使用。
private void onIncomingUSSD (int ussdMode, String ussdMessage)   ril上报ussd数据的时候会调用此方法处理,不对数据做任何处理

总结

mmi其实和拨号盘的暗码在用户角度看是一个东西,但是处理的层次不同:例如*#06#在拨号盘代码中就可以实现,app层;设置呼叫转移代码必须要运行在在phone进程,而且也没有开放接口对外,所以通过拨号的方式发送到telephony framework层的代码处理,此种服务叫做Supplementary Service;如果暗码framework层也无法处理,那么就发送该数据到网络去处理,这时这个数据就叫做USSD了。

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值