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。
2.CLIP处理
3.ClIR处理
<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处理
5.COLR处理
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码的处理
整个归类为上述11个分支。除了最后一个分支,分支的处理最后都是调用ril的相应方法向modem发送请求。
上述缩写解释部分来源于http://www.52rd.com/bbs/Dispbbs.asp?BoardID=63&ID=178392,部分是百度百科。
GsmPhone中其它Mmi相关方法
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数据的时候会调用此方法处理,不对数据做任何处理