1.在IDLE界面输入电话号码 IdleApp.c
在函数mmi_idle_entry_idle_screen()中,设置了用户按下按键时的处理函数:
SetGroupKeyHandler( HandleIdleScreenDigitEntry,
IdleScreenDigits,
MAX_IDLE_SCREEN_DIGITS - 1,
KEY_EVENT_DOWN);
就是设置全局变量:currKeyFuncPtrs[][]。把IdleScreenDigits[]里定义的KEY_0,KEY_1,…KEY_9,KEY_START,KEY_POUND这几个按键的响应函数都设置为函数:HandleIdleScreenDigitEntry()。
按下按键输入数字后,进入HandleIdleScreenDigitEntry(),首先获取输入的按键CODE,保存在g_idle_context.IdleScreenDigitKeyCode,然后调用IdleScreenDigitHandler(),处理输入的号码。此时screen id为:IDLE_SCREEN_DIGIT_HANDLER_ID。
SetLeftSoftkeyFunction(EntryScrDialOptions, KEY_EVENT_UP);
(按下方向键的center键,也是进入[选项]screen)
2.按左软件,进入选项screen.
1拨号 MENU_DIAL_GSM HighlightDialGSM()
2IP拨号 MENU_DIAO_GSM_IP HighlightDialGSMIP()
3保存 MENU_DIAL_SAVE HighlightDialSave()

(1)先学习直接拨号HighlightDialGSM()
IdleDialPadCall()
{
g_idle_context.AvoidHistoryofDialerScreen = 1;
SetTempUseIPNumber(FALSE);//设置gTempUseIPNum=0,没有使用IP拨号

然后把g_idle_context.DialPadCallBuffer保存的用户输入的号码与补充业务的字串作比较。
如果是某个补充业务的字串,就直接执行代码中与定义的处理函数;如果不是补充业务的字串,就MakeCall ()。
补充业务的字串在文件:SSCStringHandle.h中定义。


#ifdef __MMI_TOUCH_DIAL_SCREEN__
if (!SSCStringParsing2() && g_idle_context.DialPadCallBuffer[0] != '\0')
#else
if (!SSCStringParsing2())
#endif
MakeCall(g_idle_context.DialPadCallBuffer);
}
(2)IP拨号HighlightDialGSMIP()
Gsm_DialIPNumber()
{
首先判断IPSetting是否设置了。
如果没有设置IP号码,那么就弹出提示信息,然后返回到拨号screen。
如果设置了IP号码,则:
g_idle_context.AvoidHistoryofDialerScreen=1;
SetTempUseIPNumber(TRUE); //设置gTempUseIPNum=1,使用IP拨号

if(!SSCStringParsing2()) //分析补充业务字串
MakeCall(g_idle_context.DialPadCallBuffer);
}

以上都是在IDLE这个application中操作哦,然后进入MakeCall,进入CALL MANAGEMENT中操作。
在文件:MMI——features.h中,打开、关闭了所有的宏
void MakeCall(PS8 strNumber) //OutgoingCallHelper.c
{
由于宏__MMI_BG_SOUND_EFFECT__没有打开,因此直接执行:
g_phb_cntx.dial_from_list = FALSE;
MakeCallEx(strNumber, FALSE);
}

void MakeCallEx(PS8 strNumber, BOOL pre_check)
{
检测电量是否允许MAKE MO CALL。
ClearDtmfBuf()//清空:DTMFPadCallBuffer和UnicodeDTMFPadCallbuffer
FillDtmfBuff((PU8) strNumber);//把号码拷贝到UnicodeDTMFPadCallbuffer中。

检查号码是否是紧急号码,格式是否符合要求,然后:
CheckShortCutOrCall();
}

void CheckShortCutOrCall(void)
{
IsStandardGsmSS(dtmfBuf)这个函数是用来判断输入的字串是否为GSM系统的补充业务字串。
MakeMyCall((PS8) dtmfBuf); //dtmfBuf是ASCII码
}

void MakeMyCall(PS8 strNumber)
{
就是把UnicodeDTMFPadCallBuffer中的号码拷贝到OUTGOING_CALL结构的变量outCall的number中,然后调用:
MakeOutgoingcall(outCall);
}

typedef struct _outgoingcall
{
U8 Number[MAX_CM_NUMBER]; /* Calling Number. */
PS8 Name;
} OUTGOING_CALL;

void MakeOutgoingcall(OUTGOING_CALL MsgStruct)
{
OutgoingProcessCMEvent(CM_KB_OUTGOINGCALL, &MsgStruct);进入状态机处理
}

ACTION_RESULT ProcessKBOutgoingEvent(void *MsgStruct)
{
呼叫电话,不能在CM当前处于OUTGOING状态。
AllowMoreCalls()判断能否再呼出一通电话。当1当前已经大于或等于6通电话了,2存在紧急电话EmergencyCallPresent(),3当前存在正在挂断的电话GetCallAbortReqSentFlag(),4存在呼叫等待的电话并且在history screen中,存在ITEM_SCR_USSN_MSG。
gtmpOutgoingIndex = GetFirstFreeIndex();
AddNewCallInfo()注意:如果在IDLE状态下呼叫,这个电话当前状态设置为OUGTOING,如果不是在IDLE状态下呼叫电话,那么这个CALL的当前状态被设置为GetCurrentState()。
MakePsInitiateCall((PU8) DTMFPadCallBuffer, (void*)OutgoingCallConnected);
}

void MakePsInitiateCall(U8 *MsgStruct, void *callBack)
{
获取IP号码
MakePsSSVUSSDReq(MsgStruct, (PU8) IPNum, length, (void*)PsCBackSetCallFeatures);
length没有包括IP号码的长度
}
void MakePsSSVUSSDReq(U8 *MsgStruct, U8 *IPNum, U16 length, void *callBack)
{
Message.oslSrcId = MOD_MMI;
Message.oslDestId = MOD_L4C;
Message.oslMsgId = PRT_USSDVSS_REQ;
Message.oslPeerBuffPtr = NULL;

详细消息:
typedef struct
{
LOCAL_PARA_HDR
kal_uint8 input[MAX_DIGITS_USSD]; IP号码+拨号号码
kal_uint8 dcs; Ox0f
kal_uint8 length; IP+号码总长度
kal_uint8 ip_string[21]; 为空
} mmi_ss_parsing_string_req_struct;
}

然后L4返回消息PRT_USSDVSS_RSP,进入CBACK:
PsCBackSetCallFeatures()
{
首先判断L4返回的消息是否成功,并且判断是否是VOICE CALL。
如果失败,显示失败提示信息,然后恢复CM之前的状态,设置这个CALL为IDLE状态,并且如果之前有INCOMING或者OUTGOING CALL,那么要重新设置变量:SetCMPhoneBookStruct(&phb_data);
如果成功,并且gIsCCCall=1,那么就根据呼叫的号码到电话簿中找相关的信息:
PhoneNumberStruct = mmi_phb_call_get_data_for_call_mgnt(GetOutgoingNumber(), TRUE);
并且设置SetCMPhoneBookStruct(&PhoneNumberStruct);就是设置gPhoneNumberStruct变量。

然后根据CHISTGetDialFromCallLogFlag()来判断是否从CALL LOG中拨号:
如果不是,那么就用刚才从电话簿中获取的信息去设置cm_p中的AllCalls变量: SetOutgoingNamefromPhonebook();--把gPhoneNumberStruct里面的name,name_dcs复制 到cm_p->state_info.AllCalls[index]的pBname和name_dcs中。
如果是,并且CALL LOG中的名字与从电话簿中获取的名字一致,则什么都不做;如果不 一致,那么就使用CALL LOG中的名字,设置gPhoneNumberStruct的值全为0。

然后OutgoingProcessCMEvent(CM_PS_SHOW_OUTGOING_CALL_SCREEN, NULL);
CHISTSetDialFromCallLogFlag(0);//设置chis_p->dialFromCallLog=0
}

void ProcessShowOutgoingCallScreen(void)
{
根据GetPreviousState()作不同处理。思路是:
EntryScr1001OutgoingCall( );
AddMarkerToHistory( );//根据需要来调用
}

void EntryScr1001OutgoingCall(void)
{
TurnOnBacklight(1); //开背光,1-短时间
SetCbackAfterSS(NULL);
SetAbortCallRequestedFlag(FALSE); //gCallAbortRequested=0,
SetProtocolEventHandler(PsCBackNetworkCallDropped, PRT_NWRK_CALL_RELEASE);
SetCMScrnFlag(TRUE); //gCMScrnFlag=1

gPhoneNumberStruct中,如果pictureId=1,表示在呼叫界面中要显示文件中存储的图片。调用接口:
imgPath = mmi_phb_image_get_path_from_id(PhoneNumberStruct->record_index);
来获取图片的路径。参数是record_index。
注意:宏__MMI_PHB_CALL_SHOW_PICTURE_FROM_FILE__是打开的。
if (GetTotalCallCount() > 1) //大于1通电话
{
if (GetDisconnectingCallHandle() != -1) //存在正在挂断的电话
{
if (nOutGoingImgId == 1) //显示文件中的图片
{
}
else
{
}
}
else
{
if (nOutGoingImgId == 1)
{
}
else
{
}
}
}
else
{
if (GetDisconnectingCallHandle() != -1)
{
if (nOutGoingImgId == 1)
{
}
else
{
}
}
else
{
if (nOutGoingImgId == 1)
{
}
else
{
}
}
}
}

然后,如果L4处理成功,返回消息PRT_OUTGOINGCALL_EVENT_RSP,进入CBACK:
void PsCbackOutgoingCallIdSync(void *MsgStruct)
{
主要是设置OUTGOING CALL的call_id,也就是call_handle。
如果失败 设置失败原因:gCallEndCause
设置拨号标志:gCheckDialIndEndOutgoing=0
LogCallInfoForCallHistory(GetOutgoingCallHandle());
GetEndTimeAndLogUnconnectedMOCall();
if (IsRedialSet()) //自动重拨,设置重拨的号码
{
SetRedialNumber();
}
OutgoingProcessCMFailureEvent(CM_PS_CALLCONNECTED, response);
设置gCallEndedBeforeConnFlag=0
如果成功:
SetOutgoingCallHandle(handle); //设置AllCalls中的call_handle
设置标志:gCheckDialIndEndOutgoing=1
如果当前大于一通电话,就同步一下:SyncCallList()。
}

至此,呼出一通电话的处理就完成了。​

对方忙,启动了自动重拨​
void ProcessPSCallConnectFailEvent(U16 cause)
{
判断gIsCCCall。必须是voice call才能自动重拨
switch( GetCurrentState( ) ) //判断当前的CM状态
{
case CM_INCOMING_STATE:如果此时有来电,就不会自动重拨
PurgeOutgoingCallsStructure();
cause = 0;
break;
case CM_OUTGOING_STATE: //只有处于OUTGOING状态,才重拨
type = GetOutgoingCallType();.//获取呼出电话的类型
PurgeOutgoingCallsStructure();//设置OUTGOING CALL的状态为ILDE,清除AllCalls[index]中的变量,总的电话数目减1
SetCurrentState(GetPreviousState());
SetPreviousState(CM_OUTGOING_STATE);
if (GetTotalCallCount() == 0)
{
只有是VOICE CALL才能重拨;IsRedialNeeded()这个函数判断哪些情况下需要重拨,并且设置重拨的次数:SetMaxAttempts()。就是设置变量:cm_p->redial_info.MaxAttempts的值。在这个函数中,如果不符合重拨的条件,就会清空cm_p->reial_info中的变量内容,停止自动重拨的定时器:StopTimer(CM_REDIAL_TIMER)。
if ((type != CM_VOICE_CALL) || (IsRedialNeeded(cause) == FALSE))
{
ShowCallManagementErrorMessage(cause);
GetOutOfCMforAdjustHistory();
return;
}
else
{
if (IsRedialSet())读取NVRAM中保存的是否启动自动重拨标志。NVRAM_SETTING_AUTOREIDAL。如果取出的是Oxff,那么就默认为不启动自动重拨。设置变量:g_callset_context.AutoRedialStatus
{
SetCallEndedBeforeConnFlag(FALSE);
CheckRedialOrShowError(cause);
DeleteScreenIfPresent(ITEM_SCR_USSN_MSG);
return;
}
}
}
}
ShowCallManagementErrorMessage(cause); //显示信息:对方忙。如果启动了自动重拨,那么还启动了一个定时器,显示自动重拨的提示信息。定时器溢出的响应函数是:ShowRedialScreen()-----
ShowCategory141Screen(0, 0, 0, 0, STR_GLOBAL_ABORT, 0, gRedialMsgTemp, 0, NULL);

}

学习一下这个函数:
void CheckRedialOrShowError(U16 response)
{
result = CalculateSetBackOff();在这个函数中,处理了自动重拨的一些设置
ShowCallManagementErrorMessage(response);
if (result == TRUE)
SetCurrentState(GetPreviousState());
return;
}

pBOOL CalculateSetBackOff(void)
{
cm_p->redial_info.CurrentAttempt++; //对重拨次数的控制就是在此实现的
if (cm_p->redial_info.CurrentAttempt > cm_p->redial_info.MaxAttempts)
{
ResetRedialAttempts();重拨次数结束,就要清空redial_info。
return FALSE;
}

然后根据重拨的次数,动态的设置距离下次拨号的时间,也就是定时器的溢出时间。并设置要显示的字串:gReialMsgTemp。

cm_p->redial_info.RedialTimer = TRUE;
StartTimer(CM_REDIAL_TIMER, (timer * 1000), (FuncPtr) RedailAttemptNoArg); 启动定时间,注册响应函数
return TRUE;
}

重拨定时器溢出后,进入响应函数:
void RedailAttemptNoArg(void *info)
{
StopTimer(CM_REDIAL_TIMER); 首先停止定时器
cm_p->redial_info.RedialTimer = FALSE;

SetPreviousState(CM_IDLE_STATE);
SetCurrentState(CM_OUTGOING_STATE);

AddNewCallInfo(
(U8*) cm_p->redial_info.RedialNum,
(CALL_STATE) GetCurrentState(),
CM_IDLE_STATE,
CM_CALL_MO,
(CM_CALL_HANDLE) (index + 1),
CM_VOICE_CALL);

UnicodeToAnsii(TempMsgStruct, (PS8) cm_p->redial_info.RedialNum);
MakePsInitiateCall((PU8) TempMsgStruct, (void*)OutgoingCallConnected);
}

小结:自动重拨的控制变量就是cm_p->redial_info。
自动重拨之后,又进入OUTGOING的状态,下面的步骤与刚开始拨号的步骤是一样的,只不过CurrentAttempt的值增加了1。如果已经达到了最大次数,仍没有拨通,那么在函数CalculateSetBackOff()中会调用ResetRedialAttempts()把这组变量都清为0。以便以后自动重拨的使用。
如果在某次自动重拨时成功接通了,这时也要调用ResetRedialAttempts()函数把这组变量清为0,否则以后自动重拨就收到影响了。那么成功接通时,是在哪里清0这组变量的呢??
解答:在模拟器上跟踪发现,并不是在电话接通时清0的,而是在挂断电话时,显示“通话已结束”这个信息之后,进入函数:GoBackfromNotifyScr()中,调用GetOutOfCMApplication();其中又有:
/* reset redial data */
if (IsRedialSet())
{
ResetRedialAttempts(); 在这里对redial_info进行了清0操作
}