當有 incoming call 時,GSM module 會一直傳進
+CRING: VOICE
這時候以 CResponse::ParseNotification() parse 得知有 incoming call
else if (MatchStringBeginning(szPointer, "+CRING: ", szPointer)) { // Ring notification //NKDbgPrintfW(TEXT("Derek: Ringing...\r\n")); // Need to ask for the call list to send the differences. The new incoming call better // be in the call list of the radio. SetupCallListEvaluation (); if (!ParseExtRing(szPointer)) { goto Error; } }
SetupCallListEvaluation() 將 AT+CLCC 放進 queue 並指定 callback function 以請求 modem 將目前有的 call list 傳回
QueueCmdIgnoreRsp(APIID_GETCALLLIST, "AT+CLCC\r", CMDOPT_NONE /* CMDOPT_IGNORERSP */, g_TimeoutCmdInit, (PFN_CMD_PARSE) PreParseGetCallList, NULL, 0, 0, 0);
在接到 AT+CLCC 的 response 時 function PreParseGetCallList() 會負責以 LocalParseGetCallList() 處理,並將 parse 的結果 - call list 存入 global table 中,然後送出 call list 改變的通知。
HRESULT PreParseGetCallList(LPCSTR szRsp, void*& pBlob, UINT& cbBlob) { HRESULT hReturnValue; FUNCTION_TRACE(PreParseGetCallList); RETAILMSG(1, (TEXT("PreParseGetCallList : i : \r\n"))); EnterCriticalSection(&g_csCallStates); hReturnValue = LocalParseGetCallList (true, szRsp, pBlob, cbBlob); SendCallListChangedNotification (); LeaveCriticalSection(&g_csCallStates); return (hReturnValue); }
通知發出後返回 CResponse::ParseNotification(),接著呼叫 CResponse::ParseExtRing() 取得 call type (在這個例子是 voice call,其他還有像是 fax, data 之類的)
if (MatchStringBeginning(rszPointer, "AUX ", rszPointer)) { // The AUX indicates the alternate line. rri.dwAddressId = 1; } if (MatchStringBeginning(rszPointer, "ASYNC\r", rszPointer)) { rri.dwCallType = RIL_CALLTYPE_DATA; rri.rsiServiceInfo.fSynchronous = FALSE; rri.rsiServiceInfo.fTransparent = TRUE; } else if (MatchStringBeginning(rszPointer, "SYNC\r", rszPointer)) { rri.dwCallType = RIL_CALLTYPE_DATA; rri.rsiServiceInfo.fSynchronous = TRUE; rri.rsiServiceInfo.fTransparent = TRUE; } else if (MatchStringBeginning(rszPointer, "REL ASYNC\r", rszPointer)) { rri.dwCallType = RIL_CALLTYPE_DATA; rri.rsiServiceInfo.fSynchronous = FALSE; rri.rsiServiceInfo.fTransparent = FALSE; } else if (MatchStringBeginning(rszPointer, "REL SYNC\r", rszPointer)) { rri.dwCallType = RIL_CALLTYPE_DATA; rri.rsiServiceInfo.fSynchronous = TRUE; rri.rsiServiceInfo.fTransparent = FALSE; } else if (MatchStringBeginning(rszPointer, "FAX\r", rszPointer)) { rri.dwCallType = RIL_CALLTYPE_FAX; } else if (MatchStringBeginning(rszPointer, "VOICE\r", rszPointer)) { rri.dwCallType = RIL_CALLTYPE_VOICE; // default value if (g_rfExternalCalltypeDetermination) { if (!DetermineRingingCalltype(&rri.dwCallType)) { // Delay the notification until we know the calltype // Walk back over therszPointer -= 1; // Make sure we don't try to broadcast a null response m_fUnsolicited = TRUE; return TRUE; } } } else { // Skip to the next fPostfixFound = FindRspPostfix(rszPointer, rszPointer); DEBUGCHK(FALSE != fPostfixFound); rri.dwCallType = RIL_CALLTYPE_UNKNOWN; }
如果 parse 不到合適的 type 就會被設成 RIL_CALLTYPE_UNKNOWN。
AT+CLCC 的 modem response 範例如下
+CLCC: 1,1,4,0,0,"0227583788",129 OK
PreParseGetCallList() 會以 LocalParseGetCallList() 處理之。第 1 個欄位是 call ID
// Parse "" if (!ParseUInt(szRsp, TRUE, nValue, szRsp)) { goto Continue; } rgrci[nUsed].dwID = nValue; rgrci[nUsed].dwParams |= RIL_PARAM_CI_ID;
第 2 個欄位是 direction,也就是這通電話是接進來的還是打出去的
// Parse "," if (!MatchStringBeginning(szRsp, ",", szRsp) || !ParseUIntAndVerifyAbove(szRsp, TRUE, 2, nValue, szRsp)) { goto Continue; } rgrci[nUsed].dwDirection = (nValue ? RIL_CALLDIR_INCOMING : RIL_CALLDIR_OUTGOING); rgrci[nUsed].dwParams |= RIL_PARAM_CI_DIRECTION;
第 3 個欄位是 status
// Parse "," if (!MatchStringBeginning(szRsp, ",", szRsp) || !ParseUIntAndVerifyAbove(szRsp, TRUE, NUM_CALLSTATS, nValue, szRsp)) { goto Continue; } rgrci[nUsed].dwStatus = g_rgdwCallStats[nValue]; rgrci[nUsed].dwParams |= RIL_PARAM_CI_STATUS; switch (g_rgdwCallStats[nValue]) { case RIL_CALLSTAT_INCOMING: case RIL_CALLSTAT_WAITING: bIncomingCall = TRUE; } if (true == bSaveDataInGlobals) { g_rgfCallStates[nCid].dwStatus = g_rgdwCallStats[nValue]; g_rgfCallStates[nCid].dwParams |= RIL_PARAM_CI_CPISTATUS; switch (g_rgfCallStates[nCid].dwStatus) { case RIL_CALLSTAT_ALERTING: case RIL_CALLSTAT_DIALING: g_rgfCallStates[nCid].dwStatus = RIL_CPISTAT_NEW_OUTGOING; break; case RIL_CALLSTAT_INCOMING: case RIL_CALLSTAT_WAITING: g_rgfCallStates[nCid].dwStatus = RIL_CPISTAT_NEW_INCOMING; break; case RIL_CALLSTAT_ACTIVE: g_rgfCallStates[nCid].dwStatus = RIL_CPISTAT_CONNECTED; break; case RIL_CALLSTAT_ONHOLD: g_rgfCallStates[nCid].dwStatus = RIL_CPISTAT_ONHOLD; break; default: g_rgfCallStates[nCid].dwStatus = RIL_CPISTAT_UNKNOWN; g_rgfCallStates[nCid].dwParams &= ~RIL_PARAM_CI_CPISTATUS; break; } }
第 4 個欄位是 type
// Parse "," if (!MatchStringBeginning(szRsp, ",", szRsp) || !ParseUIntAndVerifyAbove(szRsp, TRUE, NUM_CALLTYPES, nValue, szRsp)) { goto Continue; } rgrci[nUsed].dwType = g_rgdwCallTypes[nValue]; rgrci[nUsed].dwParams |= RIL_PARAM_CI_TYPE; if (true == bSaveDataInGlobals) { g_rgfCallStates[nCid].dwType = g_rgdwCallTypes[nValue]; g_rgfCallStates[nCid].dwParams |= RIL_PARAM_CI_TYPE; }
第 5 個欄位是 parse multiparty。1 為 multiparty,0 為 single party
// Parse "," if (!MatchStringBeginning(szRsp, ",", szRsp) || !ParseUIntAndVerifyAbove(szRsp, TRUE, 2, nValue, szRsp)) { goto Continue; } rgrci[nUsed].dwMultiparty = (nValue ? RIL_CALL_MULTIPARTY : RIL_CALL_SINGLEPARTY); rgrci[nUsed].dwParams |= RIL_PARAM_CI_MULTIPARTY; if (true == bSaveDataInGlobals) { g_rgfCallStates[nCid].dwMultiparty = (nValue ? RIL_CALL_MULTIPARTY : RIL_CALL_SINGLEPARTY); g_rgfCallStates[nCid].dwParams |= RIL_PARAM_CI_MULTIPARTY; }
最後取得 address 及其 type,並將 AT address 轉成 RIL address
// Parse "," if (MatchStringBeginning(szRsp, ",", szRsp)) { // Parse " ," if (ParseString(szRsp, szAddress, MAXLENGTH_ADDRESS, szRsp)) { if (!MatchStringBeginning(szRsp, ",", szRsp) || !ParseUIntAndVerifyAbove(szRsp, FALSE, 0x100, nValue, szRsp) || !StringToRILAddress(szAddress, (BYTE)nValue, rgrci[nUsed].raAddress)) { goto Continue; } rgrci[nUsed].dwParams |= RIL_PARAM_CI_ADDRESS; if (true == bSaveDataInGlobals) { g_rgfCallStates[nCid].dwParams |= RIL_PARAM_CI_ADDRESS; StringToRILAddress(szAddress, (BYTE)nValue, g_rgfCallStates[nCid].raAddress); } } else { // If we couldn't parse an address, then it might be empty, // meaning the ID is blocked. Since the address parameter // is present, make sure the type also exists before continuing. if (!MatchStringBeginning(szRsp, ",", szRsp) || !ParseUIntAndVerifyAbove(szRsp, FALSE, 0x100, nValue, szRsp)) { goto Continue; } } // Parse "," if (MatchStringBeginning(szRsp, ",", szRsp)) { // Parse " " if (!ParseQuotedEncodedString(g_rppPDDParams->etEncodingTECharset, szRsp, rgrci[nUsed].wszDescription, rgrci[nUsed].wszDescription + MAXLENGTH_DESCRIPTION)) { goto Continue; } rgrci[nUsed].dwParams |= RIL_PARAM_CI_DESCRIPTION; if (true == bSaveDataInGlobals) { g_rgfCallStates[nCid].dwParams |= RIL_PARAM_CI_DESCRIPTION; StringCchCopyNW(g_rgfCallStates[nCid].wszDescription, MAXLENGTH_DESCRIPTION, rgrci[nUsed].wszDescription, MAXLENGTH_DESCRIPTION-1); } } }
在這個 function return 前,會執行
SetBacklightIncomingCall(bIncomingCall);
閃動畫面提醒使用者有 incoming call。
以下為執行時記錄下來的 log
SetupCallListEvaluation : i RILDrv : t : CRilHandle::BroadcastNotification : Broadcasting notification 0x10001 RIL_IOControl: dwCode=IOCTL_RIL_GETNEXTNOTIFICATION (5), dwLenIn=0x0, dwLenOut=0x100 CResponse::ParseNotification(): Before ParseRspPrefix(): <0d><0a>+CLCC: 1,1,4,0,0,"0227583788",129<0d><0a><0d><0a>OK<0d><0a> RIL_IOControl: dwCode=IOCTL_RIL_GETNEXTNOTIFICATION (5), dwLenIn=0x0, dwLenOut=0x100 RIL_IOControl: dwCode=IOCTL_RIL_GETNEXTNOTIFICATION (5), dwLenIn=0x0, dwLenOut=0x100 RIL_IOControl: dwCode=IOCTL_RIL_GETNEXTNOTIFICATION (5), dwLenIn=0x0, dwLenOut=0x100 PreParseGetCallList : i : RILDrv : i : CALL_LIST : cid = 1, status = 2, params = bb RILDrv : i : CLCC : CallInfoSize = 1 RILDrv : i : CLCC : cid = 1, status = 5, params = 3f RILDrv : i : CALL_LIST : cid = 1, status = 2, params = bb RILDrv : i : ParseGetCallList : sizeof(rgfNewCallsInProgress) = 40 RILDrv : t : CRilHandle::BroadcastNotification : Broadcasting notification 0x1000b