1概述
前言
本文檔作為一個學習輸出丽旅,通過分析STK不同層級間命令的傳遞方式,總結(jié)STK模塊在Telephony部分的知識鹏溯。涉及framework與package目錄下的代碼它碎。
背景
STK即SIM?Tool Kit券敌,它提供一系列用于移動設備與SIM卡間交互的機制岖研。通過這些機制卿操,支持STK的手機可以操作SIM卡里的應用。其Framework層負責消息的轉(zhuǎn)化和傳遞孙援,實現(xiàn)RIL層與應用層間的交互诺核。
簡介
圖解
目前的手機硬件結(jié)構(gòu)大多分AP端與BP端恢氯。AP端運行操作系統(tǒng)以及各種基于操作系統(tǒng)的應用索守,BP端負責無線通信基本能力糊啡,Telephony的業(yè)務跨越了AP與BP夭咬,是做協(xié)議開發(fā)主要涉及的內(nèi)容蓬坡,具體如下圖轿衔。
從這個結(jié)構(gòu)圖可以看出:
- Telephony主要運行在Linux Kernel上的用戶空間毕箍。
- 與Android系統(tǒng)分層對應腮鞍。
- BP端是處理無線通信部分值骇,即modem部分。
組成
1. RIL層指的是工程目錄下hardware/ril部分移国,又稱RILC吱瘩,由C代碼組成,與上層java進行通信迹缀,具體的消息傳遞方式在2.3部分講述使碾。
2. Telephony Framework分為telecom和telephony,相關(guān)目錄為:
.../frameworks/base/telephony/java/com/android/internal/telephony/
.../frameworks/base/telecomm/java/com/android/internal/telecom/
.../frameworks/opt/telephony/src/java/com/android/internal/telephony/
? Stk模塊相關(guān)代碼為第三個目錄下的CatService.java以及RIL.java祝懂,其中CatService.java負責RIL層與應用層間的消息傳遞票摇;RIL.java為RIL層的上層結(jié)構(gòu),又稱RILJ砚蓬,與Telephony做最直接的交互矢门,作為RIL的消息入口。
3.Apps層存放在目錄/packages/services/Telephony/與/packages/services/Telecom/灰蛙,不涉及STK模塊祟剔。
接下來根據(jù)Telephony的分層結(jié)構(gòu)進行單獨的分析。
Telephony Framework
這一部分實現(xiàn)了STK APP與RIL層的信息同步摩梧,在modem端上傳SIM卡命令后物延,通過廣播的方式將命令發(fā)送至應用層處理,同時將應用層下發(fā)的TR傳至RILC仅父,最終實現(xiàn)消息的轉(zhuǎn)換與傳遞(下發(fā)的還有Envelope Command以及Terminal Profile)叛薯。負責處理STK業(yè)務邏輯的實類為CatService浑吟。
CatService
類圖
CatSerice繼承Handler,實現(xiàn)了AppInterface接口耗溜。
通過handler接收應用層消息
STK APP通過調(diào)用接口函數(shù)onCmdResponse()發(fā)送消息至CatService买置,具體代碼實現(xiàn):
??? public synchronized void onCmdResponse(CatResponseMessage resMsg) {
??????? if (resMsg == null) {
??????????? return;
??????? }
??????? // queue a response message.
??????? Message msg =obtainMessage(MSG_ID_RESPONSE, resMsg);
??????? msg.sendToTarget();
??? }
可以看到最終通過sendToTarget將消息發(fā)至自身的消息隊列。(這里用法與直接new Message 再調(diào)用sendMessage不同强霎,跟進前者代碼發(fā)現(xiàn)最終走的還是sendMessage忿项,區(qū)別在于沒有新構(gòu)造一個消息對象,根據(jù)注釋解釋城舞,前者更有效率)
通過Handler接收RILJ的消息
回到CatService的構(gòu)建函數(shù)轩触,可以看到:
???? // Register ril events handling.
????? mCmdIf.setOnCatSessionEnd(this,MSG_ID_SESSION_END, null);
????? mCmdIf.setOnCatProactiveCmd(this,MSG_ID_PROACTIVE_COMMAND, null);
????? mCmdIf.setOnCatEvent(this,MSG_ID_EVENT_NOTIFY, null);
????? mCmdIf.setOnCatCallSetUp(this,MSG_ID_CALL_SETUP, null);
???? //mCmdIf.setOnSimRefresh(this,MSG_ID_REFRESH, null);
這里調(diào)用了CommandInterface的函數(shù)去注冊實現(xiàn)觀察者模式,具體實現(xiàn)在RILJ的父類BaseCommands當中:
??? public void setOnCatProactiveCmd(Handler h,int what, Object obj) {
??????? mCatProCmdRegistrant = new Registrant(h, what, obj);
??? }
這里的Handler即為CatService自身家夺,即將自身所需接收的消息注冊給RILJ脱柱。
關(guān)鍵函數(shù)
1. getInstance::向外提供CatService的單例。
2. handlemessage:處理來自APP以及RILJ的消息拉馋。
3. sendStartDecodingMessageParams:接口函數(shù)榨为,將RIL Message解碼成CommandParams Objects。
4. handleRilMsg:處理解碼器轉(zhuǎn)換后的RIL層消息煌茴。
5. handleCommand:處理具體的命令随闺。
6. handleCmdResponse:處理APP發(fā)來的Repsonse。
7. sendTerminalResponse:發(fā)送TR蔓腐。矩乐、
消息處理
完整的CatService消息處理機制如下圖:
RILJ
類圖
關(guān)鍵函數(shù)
BaseCommands: 實現(xiàn)CommandsInterface的大部分接口函數(shù),涉及多個RegistrantList與Registrant回论,向外提供多個事件的觀察者模式注冊函數(shù)散罕。查看Registrant文件,發(fā)現(xiàn)就是對Handler的封裝傀蓉,由注冊的一方指定一個需要通知的事件欧漱,另一方只需要調(diào)用notifyRegistrant()來實現(xiàn)message的創(chuàng)建、填充葬燎、發(fā)送過程误甚。RegistrantList實現(xiàn)多個注冊對象的同時通知。
如: setOnCatProactiveCmd:主動式命令的監(jiān)聽注冊萨蚕,RILJ在接收到RILC的主動式命令消息后靶草,由Registrant向注冊時指定的Handler(這里指CatService)發(fā)送消息通知。
RILJ:將Telephony的業(yè)務封裝成RILRequest并發(fā)至RILC岳遥。
1. sendTerminalResponse:將主動式命令的處理結(jié)果封裝后發(fā)送至RILC奕翔。
2. getRadioProxy: 初始化IRadio相關(guān)服務。
3. obtainRequest:創(chuàng)建RILRequest對象浩蓉。
4. processResponse: 處理RILC請求返回的結(jié)果派继。
5. processIndication: 處理RILC上報的消息宾袜。
完整流程
貼上完整的主動式命令處理時Telephony部分的傳輸過程:
RIL層
這一部分主要是參考《Android Telephony 原理解析與開發(fā)指南》一書進行學習。
進入RILC的目錄驾窟,可以看到代碼結(jié)構(gòu)分為四部分庆猫,分別是include、libril绅络、reference-ril月培、rild。
- Include:ril.h定義了RIL_Init恩急、RIL_register等函數(shù)和其他結(jié)構(gòu)體杉畜,以及RIL消息RIL_REQUEST_XXX和RIL_UNSOL_XXX。
- libril:ril_commands.h衷恭、ril_unsol_commands.h此叠、ril.cpp、ril_service.cpp随珠。編譯輸出libril.so灭袁。
- reference_ril:編譯輸出libreference-ril.so,實現(xiàn)了RIL AT命令的交互機制窗看。
- rild:主要關(guān)注點茸歧,編譯生成可執(zhí)行文件rild。
RILC運行機制
消息分類
RIL中的消息類型烤芦,按照發(fā)起方分為Solicited以及UnSolicited举娩。
1. Solicited是由AP端發(fā)起的消息,又根據(jù)傳遞方向分為Solicited Request和Solicited Response构罗。
2. UnSolicited是由BP側(cè)主動發(fā)起的消息。
這兩種消息命名方式為RIL_REQUEST_ XXX智玻、RIL_REQUEST_XXX遂唧,具體的消息定義查看frameworks/base/telephony/java/com/android/internal/telephony/RILConstants.java。
通過Radio log可以查看具體的消息類型與傳遞方向吊奢,其中’<’盖彭,’>’表示發(fā)送的方向。:
Solicitesd:
RILJ: [4041]> RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING [SUB0]
RILJ: [4041]< RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING [SUB0]
UnSolicited:
RILJ: [UNSL]< UNSOL_STK_PROACTIVE_COMMAND [SUB0]
消息處理
1. Solicited消息
以2.2.3發(fā)送TR為例页滚,RILJ最終調(diào)用的IRadio. sendTerminalResponseToSim()召边,該函數(shù)最終在
hardware/ril/libril/ril_service.cpp中實現(xiàn):
Return<void> RadioImpl::sendTerminalResponseToSim(int32_t serial,
? ? ? ? ? ? ? ? ? ? const hidl_string& commandResponse) {
#if VDBG
? ? RLOGD("sendTerminalResponseToSim: serial %d", serial);
#endif
? ? dispatchString(serial, mSlotId, RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE,
? ? ? ? ? ? commandResponse.c_str());
? ? return Void();
}
其中dispatchString函數(shù):
bool dispatchString(int serial, int slotId, int request, const char * str) {
? ? RequestInfo *pRI = android::addRequestToList(serial, slotId, request);
? ? if (pRI == NULL) {
? ? ? ? return false;
? ? }
? ? char *pString;
? ? if (!copyHidlStringToRil(&pString, str, pRI)) {
? ? ? ? return false;
? ? }
? ? CALL_ONREQUEST(request, pString, sizeof(char *), pRI, slotId);
? ? memsetAndFreeStrings(1, pString);
? ? return true;
}
在RadioImpl中可看到所有的Solicited消息處理最終都調(diào)用了dispatchString()。
主要關(guān)注addRequestToList與CALL_ONREQUEST
addRequestToList:
RequestInfo *
addRequestToList(int serial, int slotId, int request) {
? ? RequestInfo *pRI;
? ? int ret;
? ? RIL_SOCKET_ID socket_id = (RIL_SOCKET_ID) slotId;
? ? /* Hook for current context */
? ? /* pendingRequestsMutextHook refer to &s_pendingRequestsMutex */
? ? pthread_mutex_t* pendingRequestsMutexHook = &s_pendingRequestsMutex;
? ? /* pendingRequestsHook refer to &s_pendingRequests */
? ? RequestInfo**? ? pendingRequestsHook = &s_pendingRequests;
? ? ......
? ? pRI = (RequestInfo *)calloc(1, sizeof(RequestInfo));
? ? if (pRI == NULL) {
? ? ? ? RLOGE("Memory allocation failed for request %s", requestToString(request));
? ? ? ? return NULL;
? ? }
? ? pRI->token = serial;
? ? pRI->pCI = &(s_commands[request]);
? ? pRI->socket_id = socket_id;
? ? ret = pthread_mutex_lock(pendingRequestsMutexHook);
? ? assert (ret == 0);
? ? pRI->p_next = *pendingRequestsHook;
? ? *pendingRequestsHook = pRI;
? ? ret = pthread_mutex_unlock(pendingRequestsMutexHook);
? ? assert (ret == 0);
? ? return pRI;
}
其中s_commands[]定義為ril_commands.h裹驰,該頭文件定義了RIL請求類型和回調(diào)函數(shù)隧熙,如{RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE, radio::sendTerminalResponseToSimResponse}
即RILC會調(diào)用sendTerminalResponseToSimResponse作為Solicited Request消息的Response。
CALL_ONREQUEST:
ril_service.cpp的宏定義為:
#define CALL_ONREQUEST(a, b, c, d, e) s_vendorFunctions->onRequest((a), (b), (c), (d))
即CALL_ONREQUEST將調(diào)用s_vendorFunctions->onRequest, 進入該函數(shù)幻林,發(fā)現(xiàn)沒有對RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE做具體的處理贞盯,只調(diào)用RIL_onRequestComplete音念。
進入該函數(shù)的定義:
extern "C" void
RIL_onRequestComplete(RIL_Token t, RIL_Errno e, void *response, size_t responselen) {
? ? RequestInfo *pRI;
? ? int ret;
? ? RIL_SOCKET_ID socket_id = RIL_SOCKET_1;
? ? pRI = (RequestInfo *)t;
? ? if (!checkAndDequeueRequestInfoIfAck(pRI, false)) {
? ? ? ? RLOGE ("RIL_onRequestComplete: invalid RIL_Token");
? ? ? ? return;
? ? }
? ? socket_id = pRI->socket_id;
? ? appendPrintBuf("[%04d]< %s",
? ? ? ? pRI->token, requestToString(pRI->pCI->requestNumber));
? ? if (pRI->cancelled == 0) {
? ? ? ? int responseType;
? ? ? ? ......
? ? ? ? int rwlockRet = pthread_rwlock_rdlock(radioServiceRwlockPtr);
? ? ? ? assert(rwlockRet == 0);
? ? ? ? ret = pRI->pCI->responseFunction((int) socket_id,
? ? ? ? ? ? ? ? responseType, pRI->token, e, response, responselen);
? ? ? ? rwlockRet = pthread_rwlock_unlock(radioServiceRwlockPtr);
? ? ? ? assert(rwlockRet == 0);
? ? }
? ? free(pRI);
}
以上的主要處理流程是:調(diào)用checkAndDequeueRequestInfoIfAck找到RIL請求對應的RequestInfo,通過pRI->pCI->responseFunction發(fā)起XXXResponse的調(diào)用躏敢,即上一步addRequestToList指定的Solicited消息對應的Response函數(shù)闷愤。
跟進Telephony framework中RadioResponse里的sendTerminalResponseToSimResponse,可看到最終調(diào)用了RILJ的processResponseDone件余,完成response的發(fā)送讥脐。
2. UnSolicited消息
UnSolicited類型的消息不確定發(fā)起點,從RILJ往下跟進啼器,在RadioIndication.java中可看到函數(shù)stkProactiveCommand攘烛,用于通知CAT并調(diào)用RILJ進行處理。
RadioIndication繼承IRadioIndication.Stub镀首,進入ril_service.cpp坟漱,radio::stkProactiveCommandInd調(diào)用了對應的stkProactiveCommand,實現(xiàn)了消息的傳遞更哄。
消息傳遞圖
下圖即為RIL的消息處理機制芋齿,其中RILJ通過rild提供的IRadio服務發(fā)送消息,RILC通過phone提供的IRadioResponse和IRadioIndication發(fā)送消息成翩,這一部分統(tǒng)稱為HIDL觅捆,是基于Binder通信實現(xiàn)的。(AIDL麻敌,HIDL運作原理還需要進一步學習)
以IRadio為例:IRadio.hal中定義了多個函數(shù)接口栅炒,如sendTerminalResponseToSim。在ril_servise中RadioImpl再實現(xiàn)該函數(shù)术羔。RILJ通過調(diào)用IRadio的接口實現(xiàn)消息的傳遞赢赊。
圖中的3是RILC與Modem的交互方式,是由廠商實現(xiàn)级历,下文了解一下高通的實現(xiàn)方式释移。
與Modem通信
結(jié)構(gòu)圖
關(guān)于Telephony與Modem直接的通信,高通實現(xiàn)方式是QMI+QCRIL寥殖,QCRIL為RILC的組成部分玩讳,QMI屬于Modem。
.該部分涉及高通文檔嚼贡,不確定是否為開源熏纯,暫時不貼圖
QCRIL+QMI
Vendor RIL初始化
rild.c主函數(shù)main中獲取了vendor RIL(QCRIL)的庫以及初始化入口方法,并進行初始化與注冊:
int main(int argc, char **argv) {?
? ? .......
? ? int i;
? ? const char *clientId = NULL;
? ? RLOGD("**RIL Daemon Started**");
? ? RLOGD("**RILd param count=%d**", argc);
? ? umask(S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH);
//1.獲取QCRIL路徑
? ? for (i = 1; i < argc ;) {
? ? ? ? if (0 == strcmp(argv[i], "-l") && (argc - i > 1)) {
? ? ? ? ? ? rilLibPath = argv[i + 1];
? ? ? ? ? ? i += 2;
? ? ? ? } else if (0 == strcmp(argv[i], "--")) {
? ? ? ? ? ? i++;
? ? ? ? ? ? hasLibArgs = 1;
? ? ? ? ? ? break;
? ? ? ? } else if (0 == strcmp(argv[i], "-c") &&? (argc - i > 1)) {
? ? ? ? ? ? clientId = argv[i+1];
? ? ? ? ? ? i += 2;
? ? ? ? } else {
? ? ? ? ? ? usage(argv[0]);
? ? ? ? }
? ? }
? ? .......
//2.打開vendor ril庫
? ? dlHandle = dlopen(rilLibPath, RTLD_NOW);
? ? //3.啟動消息循環(huán)
? ? RIL_startEventLoop();
? ? //4.獲取vendor RIL的初始化入口方法
? ? rilInit =
? ? ? ? (const RIL_RadioFunctions *(*)(const struct RIL_Env *, int, char **))
? ? ? ? dlsym(dlHandle, "RIL_Init");
? ? ......
//5.調(diào)用vendor RIL的RIL_Init方法粤策,此方法會返回RIL_RadioFunctions
? ? funcs = rilInit(&s_rilEnv, argc, rilArgv);
? ? RLOGD("RIL_Init rilInit completed");
//6.注冊vendor RIL的處理方法
? ? RIL_register(funcs);
? ? ......
}
傳入回調(diào)函數(shù)
上文中提到的funcs = rilInit(&s_rilEnv, argc, rilArgv)實際上調(diào)用了QCRIL的RIL_Init樟澜,傳入了s_rilEnv,具體結(jié)構(gòu)為:
static struct RIL_Env s_rilEnv = {
? ? RIL_onRequestComplete,
? ? RIL_onUnsolicitedResponse,
? ? RIL_requestTimedCallback,
? ? RIL_onRequestAck
};;
即傳入rilc的方法供QCRIL回調(diào)掐场,通過回調(diào)實現(xiàn)消息傳遞往扔。
獲取QCRIL的函數(shù)接口
查看QCRIL的RIL_Init贩猎,主要執(zhí)行了以下幾個函數(shù):
qmi_ril_set_thread_name( pthread_self() , QMI_RIL_QMI_RILD_THREAD_NAME);
// Initialize the event thread */
qcril_event_init();
// Initialize QCRIL
qcril_init();
// Start event thread
qcril_event_start();
// start bootup if applicable
qmi_ril_initiate_bootup();
return &qcril_request_api[ QCRIL_DEFAULT_INSTANCE_ID ];
即初始化以及返回接口函數(shù)。
1. 其中qcril_event_int為初始化EventLoop線程萍膛,通過查看qcril_event_main的注釋可知EventLoop用于讀取并處理QMI的消息吭服。
2. 通過return &qcril_request_api[ QCRIL_DEFAULT_INSTANCE_ID ]返回函數(shù)列表供RILC使用。
消息傳遞
以RILC到QMI為例
在QCRIL初始化時提供的qcril_request_api具體定義如下:
static const RIL_RadioFunctions qcril_request_api[] = {
? { QCRIL_RIL_VERSION, onRequest_rid, currentState_rid, onSupports_rid, onCancel_rid, getVersion_rid }
};
當RIL層有請求時蝗罗,RILC調(diào)用其中的onRequest_rid:
static void onRequest_rid
(
? int request,
? void *data,
? size_t datalen,
? RIL_Token t
)
{
? onRequest( qmi_ril_process_instance_id, request, data, datalen, t );
}
實際上調(diào)用了onRequest艇棕,查看onRequest發(fā)現(xiàn)調(diào)用qcril_dispatch_event將消息發(fā)至對應的handler:
/* Dispatch the request to the appropriate handler */
(entry_ptr->handler)(params_ptr, &ret);
這里entry_ptr指的是qcril_dispatch_table_entry_type,該結(jié)構(gòu)定義了各種消息類型下對應的調(diào)用方法串塑,以發(fā)送TR為例:
{ QCRIL_REG_ALL_STATES( RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE, qcril_gstk_qmi_request_stk_send_terminal_response ) }
查看該方法發(fā)現(xiàn)調(diào)用了QMI的qmi_client_send_msg_async沼琉,進一步調(diào)用qmi_service_msg發(fā)送消息至modem。
具體時序圖如下:
消息從QMI到QCRIL的處理過程與上圖類似:將消息發(fā)至QCRIL的隊列桩匪、QCRIL調(diào)用qcril_dispatch_event將消息發(fā)至對應的handler打瘪,qcril_dispatch_table_entry_type這個結(jié)構(gòu)不僅定義了上層消息對應的處理方法,還涉及modem發(fā)至QCRIL的各種消息傻昙。
總結(jié)
Telephony層涉及到的STK部分基本都是消息的傳遞闺骚,包括廣播,Handler妆档,HIDL僻爽。
RILJ以上的部分源碼相對來說不難閱讀和理解,且資料多贾惦。QCRIL+QMI部分僅根據(jù)log跟進了消息的傳遞胸梆。
以一張圖總結(jié)整體的消息傳遞方式: