Android7.0 Rild工作流程
一、基於Rild的通訊架構
一般智慧手機的硬體架構都是兩個處理器:
一個處理器用來執行作業系統,上面執行應用程式,這個處理器稱作Application Processor,簡稱AP;另一個處理負責和射頻無線通訊相關的工作,叫Baseband Processor,簡稱BP。
在Android系統中,Rild執行在AP上,它是AP和BP在軟體層上通訊的中樞。
目前通過Rild,AP和BP的通訊方式可以分為兩種:
第一種是AP傳送請求給BP,BP響應並回復AP。此時,BP通過Rild回覆的請求被稱為solicited Response。
第二種是BP主動傳送資訊給AP。在這種情況下,BP通過Rild傳送的請求被稱為unsolicited Response。

基於Rild程序的整個通訊架構,基本上如上圖所示。
從圖中我們可以看出:
1、Android框架部分,是通過Phone程序與Rild程序通訊的。它們之間的通訊方式採用的是socket。
在前面介紹PhoneApp啟動時,我們知道Phone程序中有兩個Phone物件。每個Phone物件持有一個socket,與對應的Rild程序通訊。因此,我們知道手機中實際上啟動了兩個Rild程序(雙卡手機)。
我們通過usb連線手機後,通過adb shell進入終端,通過ps和grep命令,可以得到上述結果。
明顯可以看到一個Phone程序對應者兩個Rild程序;同時Rild程序由init程序載入,Phone程序由zygote程序載入。
2、Rild與BP之間並沒有直接通訊,而是引入了廠商的動態庫。
這種設計應該是為了保證靈活性吧。
用面向物件的思想來看,我們可以認為Rild是一個介面,定義了AP、BP雙向通訊時需要使用的最基本的函式。不同的廠商都需要滿足這個介面,以提供手機最基本的通訊功能。
至於具體如何實現,是完全獨立和自由的。
二、Rild的啟動
在hardware/ril/rild/rild.rc中定義了Rild啟動時對應的選項:
在Android 7.0之前的版本中,該檔案的內容是被定義在init.rc中的。
到了Android7.0 之後,init.rc檔案中的許多內容均被移出,新增到各個程序中。如前面分析Vold程序時,對應的啟動檔案定義於vold.rc中。
個人猜測這些檔案應該會在編譯時,重新整合起來,畢竟在在rild對應的Android.mk中增加了下述欄位:
目前手邊沒有Android7.0的機器,還不好驗證,以後有機會再做嘗試。
init程序根據rild.rc檔案啟動一個Rild程序,還需要根據廠商定義的rc檔案啟動另一個Rild程序。
廠商定義的rc檔案中,與Rild程序相關的主要內容與rild.rc相似,就是socket名稱不同。對於第二個Rild程序,其socket名應該為rild2。
現在我們看看Rild程序的main函式,定義於rild.c中:
int main(int argc, char **argv) { //rilLibPath用於指定動態庫的位置 const char * rilLibPath = NULL; ........ //Rild規定動態庫必須實現一個叫做Ril_init的函式,這個函式的第一個引數指向結構體RIL_Env //而它的返回值指向結構體RIL_RadioFunctions const RIL_RadioFunctions *(*rilInit)(const struct RIL_Env *, int, char **); ........ const RIL_RadioFunctions *funcs; char libPath[PROPERTY_VALUE_MAX]; //解析引數 ........ if (strncmp(clientId, "0", MAX_CLIENT_ID_LENGTH)) { strlcat(rild, clientId, MAX_SOCKET_NAME_LENGTH); //注意此處呼叫了ril.cpp中的函式,儲存了Rild程序對應socket的名字,後文還會提到 RIL_setRilSocketName(rild); } if (rilLibPath == NULL) { //讀取系統屬性,LIB_PATH_PROPERTY的值為rild.libpath //原生的屬性值定義於build/target/board/generic/system.prop檔案中 //實際的手機中將會使用廠商指定的system.prop檔案 if ( 0 == property_get(LIB_PATH_PROPERTY, libPath, NULL)) { // No lib sepcified on the command line, and nothing set in props. // Assume "no-ril" case. goto done; } else { rilLibPath = libPath; } } .......... //根據動態庫位置,利用dlopen開啟動態庫 dlHandle = dlopen(rilLibPath, RTLD_NOW); .......... //1、啟動EventLoop,事件處理 RIL_startEventLoop() //從動態庫中的到RIL_Init函式的地址 rilInit = (const RIL_RadioFunctions *(*)(const struct RIL_Env *, int, char **)) dlsym(dlHandle, "RIL_Init"); ...... //2、呼叫RIL_init函式 funcs = rilInit(&;s_rilEnv, argc, rilArgv); RLOGD("RIL_Init rilInit completed"); //3、註冊funcs到Rild中 RIL_register(funcs); ........done: RLOGD("RIL_Init starting sleep loop"); while (true) { sleep(UINT32_MAX); }}根據Rild的main函式,我們可以看出主要就進行了三件事:啟動Event Loop、呼叫RIL_Init函式和註冊庫函式。
接下來我們分別分析一下主要事件對應的流程。
1、 RIL_startEventLoop
RIL_startEventLoop定義於hardware/ril/libril/ril.cpp中:
我們需要跟進eventLoop函式:
static void *eventLoop(void *param) { int ret; int filedes[2]; //1、初始化內部資料結構 ril_event_init(); pthread_mutex_lock(&;s_startupMutex); //通知RIL_startEventLoop本執行緒已經建立併成功運行了 s_started = 1; pthread_cond_broadcast(&;s_startupCond); pthread_mutex_unlock(&;s_startupMutex); //建立匿名管道 ret = pipe(filedes); ........ s_fdWakeupRead = filedes[0]; s_fdWakeupWrite = filedes[1]; //設定讀埠為非阻塞的 fcntl(s_fdWakeupRead, F_SETFL, O_NONBLOCK); //2、建立一個ril_event ril_event_set (&;s_wakeupfd_event, s_fdWakeupRead, true, processWakeupCallback, NULL); //3、將創建出的ril_event加入到event佇列中 rilEventAddWakeup (&;s_wakeupfd_event); //4、進入事件等待迴圈中 ril_event_loop(); ......... }1.1 初始化內部資料結構
我們先看看ril_event_init函式:
可以看出ril_event_init就是初始化readFds、timer_list、pending_list和watch_table,其中後三種資料結構均是用來存放ril_event的。
根據前文的程式碼,我們知道Rild的main函式中,通過呼叫RIL_startEventLoop單獨啟動了一個執行緒執行eventLoop,這是一個工作執行緒。
這個工作執行緒就是靠ril_event結構體來描述自己需要執行的任務,並且它將多個任務按時間順序組織起來,儲存在任務佇列中。
ril_event的資料結構如下:
如果從設計模式的角度來理解Rild的工作執行緒,易於看出,這其實是比較典型的命令模式。
就如同之前部落格分析vold程序一樣,CommandListener收到資料後,呼叫對應Command的runCommand方法進行處理。
此處,工作執行緒收到ril_event後,加入佇列中,當需要處理時,呼叫ril_event對應的處理函式func。
1.2 建立wakeupfd ril_event
工作執行緒完成資料結構的初始化後,建立了第一個ril_event:
從上面的程式碼可以看出,建立的第一個ril_event的fd為管道的讀端、回撥函式為processWakeupCallback,同時persist屬性為true。
1.3 將創建出的ril_event加入到event佇列中
static void rilEventAddWakeup(struct ril_event *ev) { ril_event_add(ev); triggerEvLoop();}// Add event to watch listvoid ril_event_add(struct ril_event * ev){ dlog("~~~~ +ril_event_add ~~~~"); MUTEX_ACQUIRE(); for (int i = 0; i < MAX_FD_EVENTS; i++) { //找到第一個空閒索引加入 if (watch_table[i] == NULL) { watch_table[i] = ev; //ril ev->index = i; dlog("~~~~ added at %d ~~~~", i); dump_event(ev); //將ril_event對應的fd加入到readFds FD_SET(ev->fd, &;readFds); //select的限制,第一個引數為監聽總數+1 if (ev->fd >= nfds) nfds = ev->fd+1; dlog("~~~~ nfds = %d ~~~~", nfds); break; } } MUTEX_RELEASE(); dlog("~~~~ -ril_event_add ~~~~");}static void triggerEvLoop() { int ret; //pthread_self返回呼叫執行緒的執行緒ID //這裡呼叫triggerEvLoop的就是eventLoop,因此不進入該分支 if (!pthread_equal(pthread_self(), s_tid_dispatch)) { /* trigger event loop to wakeup. No reason to do this, * if we're in the event loop thread */ do { //但看程式碼我們知道,如果其它執行緒呼叫rilEventAddWakeup加入ril_event時,就會向pipe的寫端寫入資料 ret = write (s_fdWakeupWrite, " ", 1); } while (ret < 0 &;&; errno == EINTR); }}1.4 進入事件等待迴圈中
接下來工作執行緒進入到事件等待迴圈中:
至此,RIL_startEventLoop的工作介紹完畢,雖然還沒有實際開始工作,但它搭建出了整個事件的處理框架。這裡涉及的程式碼比較繁雜,我們還是藉助於圖形總結一下整個過程: 
1.4.1 整體架構
如上圖所示,Rild的main函式中呼叫RIL_startEventLoop。在RIL_startEventLoop中創建出工作執行緒,執行eventLoop函式:
Step 1、利用ril_event_init函式初始化資料結構,主要包括readFds、timer_list、pending_list和watch_table;
Step 2、創建出一個pipe物件;
Step 3、建立s_wakeupfd_event,該event的fd指定為pipe的讀端;這個event將被加入到watch_table,同時pipe的讀端將被加入到readFds中;注意這個event的persist屬性為true,於是將永遠存在於watch_table中;
Step 4、呼叫ril_event_loop開始監聽事件的到來。
先在我們結合圖形,舉幾個例子看看,整個事件處理框架是如何工作的。
注意到初始時,timer_list為空,因此ril_event_loop中將不限時地等待readFds。
1.4.2 ril_event加入到timer_list
當其它執行緒呼叫ril_timer_add函式(定義於ril_event.cpp中)填加ril_event事件時:
Step 1、新到來的ril_event將按超時時間,由小到大加入到timer_list中;同時,其它執行緒一般會呼叫triggerEvLoop,該函式將會向pipe的寫端寫入資料。
Step 2、於是,pipe的讀端將會收到資料;由於初始時pipe讀端已經加入到來readFds,因此ril_event_loop將從等待中喚醒。
Step 3、此時,ril_event_loop將執行timer_list和watch_table中儲存的事件。注意到在timer_list中,只有超時的事件才會被處理;在watch_table中,只有對應fd已經存入readFds(此時使用的是拷貝物件)的事件才會被執行。
注意到初始時加入watch_table的s_wakeupfd_event,永遠滿足執行條件;因此,每次ril_event_loop被喚醒時,該事件都被新增到pending_list。
s_wakeupfd_event對應的執行函式,將會清空pipe的buffer。
Step 4、 處理完加入到pending_list中的事件後,ril_event_loop將根據timer_list中事件的超時時間,決定等待readFds的時間。
如果在等待超時之前,沒有其它事件到來,那麼ril_event_loop將在等待超時後處理timer_list中的事件;否則,僅會處理新到來的事件,不會處理timer_list事件。
1.4.3 ril_event加入到watch_table
當其它執行緒呼叫ril_event_add函式(定義於ril_event.cpp中)增加ril_event事件時:
Step 1、當watch_table有空位時,新加入的ril_event將被加入到watch_table中,同時對應的fd被新增到readFds;同時,其它執行緒可能會呼叫triggerEvLoop,以喚醒ril_event_loop。
Step 2、 ril_event_loop被喚醒後,並不會執行新加入到watch_table中的ril_event,因為它們的fd才剛被加入到readFds中。
從程式碼裡我們可以看到,ril_event_loop當次迴圈處理的是readFds的拷貝對應的資料,因此新加入watch_table的ril_event在下次喚醒時才能夠被處理。
Step 3、由於加入ril_event對應的fd被加入到readFds中,因此如果對應的fd寫入資料時,也會喚醒ril_event_loop。
至此,RIL_startEventLoop的主要流程介紹完畢,可以看到它的主要工作就是啟動工作執行緒,然後等待事件的新增
那麼接下來我們可以看看下一個Rild中下一個重要操作,即呼叫RIL_Init函式。
2、 RIL_Init
RIL_Init定義於動態庫中,考慮到廠商的保密性,我們只能分析Android原生的Reference-ril庫。
在Android的原生庫中,RIL_Init定義於hardware/ril/reference-ril/reference-ril.c中。
從程式碼上來看RIL_Init函式比較簡單,就幹了三件事:儲存Rild傳入的RIL_Env結構體;建立s_tid_mainloop執行緒,執行函式為mainLoop;返回RIL_RadioFunctions結構體。
這裡需要注意的是:RIL_Env和RIL_RadioFunctions結構體,就是Rild架構中用來隔離通用程式碼和廠商相關程式碼的介面。即動態庫通過RIL_Env呼叫Rild中的介面,Rild通過RIL_RadioFunctions呼叫動態庫中的介面。
2.1 通訊介面
我們先看看RIL_RadioFunctions結構體:
這裡需要重點關注的函式是onRequest,它被Rild用來向動態庫提交一個請求。
Rild架構採用的是非同步請求/處理的通訊方式,Rild通過onRequest向動態庫提交一個請求,然後返回進行自己的工作;動態庫處理這個請求,當處理完請求後,通過回撥的方式將結果通知給Rild。
我們再來看看RIL_Env結構體:
struct RIL_Env { //動態庫完成一個請求後,通過OnRequestComplete通知處理結果,RIL_Token用於標明是哪個請求的處理結果 void (*OnRequestComplete)(RIL_Token t, RIL_Errno e, void *response, size_t responselen); //動態庫主動上報時,呼叫的介面#if defined(ANDROID_MULTI_SIM) void (*OnUnsolicitedResponse)(int unsolResponse, const void *data, size_t datalen, RIL_SOCKET_ID socket_id);#else void (*OnUnsolicitedResponse)(int unsolResponse, const void *data, size_t datalen);#endif //給rild提交一個超時任務 void (*RequestTimedCallback) (RIL_TimedCallback callback, void *param, const struct timeval *relativeTime); //對於同步的請求,傳送應答訊息時,使用該介面,目前沒看到使用 void (*OnRequestAck) (RIL_Token t);}在RIL_Env的結構體中,主要需要關注的是OnRequestComplete和OnUnsolicitedResponse。
2.2 mainLoop
動態庫的RIL_Init被呼叫後,將會建立一個工作執行緒,其執行函式為mainLoop:
從上面的程式碼可以看出,mainLoop的工作其實就是初始化並監控AT模組,一但AT模組被關閉,那麼mainLoop就要重新開啟並初始化它。
2.2.1 at_open
int at_open(int fd, ATUnsolHandler h) { ........ pthread_attr_init (&;attr); pthread_attr_setdetachstate(&;attr, PTHREAD_CREATE_DETACHED); ret = pthread_create(&;s_tid_reader, &;attr, readerLoop, &;attr); .........}在at_open中建立了一個工作執行緒,執行函式為readLoop:
static void *readerLoop(void *arg){ for (;;) { const char * line; //從串列埠裝置讀取資料 line = readline(); ...... if(isSMSUnsolicited(line)) { ....... //呼叫回撥函式 if (s_unsolHandler != NULL) { s_unsolHandler (line1, line2); } ....... } else { //根據line中的資料,呼叫不同的回撥函式 processLine(line); } } //如果從for迴圈退出,則通知mainLoop AT裝置關閉 onReaderClosed(); ...........}從上面的程式碼,我們知道at_open函式其實就是啟動一個工作執行緒,用於接收AT裝置的資料,然後進行處理。
2.2.1 initializeCallback
呼叫at_open後,main利用RIL_requestTimedCallback向Rild傳送一個超時任務。
可以看到RIL_requestTimedCallback是一個巨集,實際上還是通過RIL_Env中RequestTimedCallback函式傳送超時任務。
我們看看ril.cpp中,該函式的實現:
extern "C" voidRIL_requestTimedCallback (RIL_TimedCallback callback, void *param, const struct timeval *relativeTime) { internalRequestTimedCallback (callback, param, relativeTime);}static UserCallbackInfo *internalRequestTimedCallback (RIL_TimedCallback callback, void *param, const struct timeval *relativeTime){ struct timeval myRelativeTime; UserCallbackInfo *p_info; p_info = (UserCallbackInfo *) calloc(1, sizeof(UserCallbackInfo)); ......... //回撥 p_info->p_callback = callback; //引數 p_info->userParam = param; if (relativeTime == NULL) { /* treat null parameter as a 0 relative time */ memset (&;myRelativeTime, 0, sizeof(myRelativeTime)); } else { /* FIXME I think event_add's tv param is really const anyway */ //時間 memcpy (&;myRelativeTime, relativeTime, sizeof(myRelativeTime)); } //構造ril_event ril_event_set(&;(p_info->event), -1, false, userTimerCallback, p_info); //加入到timer_list中 ril_timer_add(&;(p_info->event), &;myRelativeTime); //觸發EventLoop處理 triggerEvLoop(); return p_info;}當Rild中的eventLoop處理該超時任務時,就會回撥Reference-ril庫中的initializeCallback:
static void initializeCallback(void *param __unused){ ........ //同步radio狀態 setRadioState (RADIO_STATE_OFF); //不斷地嘗試傳送AT指令給BP並等待回覆,以確定AT channel正常 at_handshake(); //下發一系列的AT指令,完成modem初始化 ..........}
至此,我們以Reference-ril庫為例,分析了RIL_Init函式的基本功能。
如上圖所示,RIL_Init的主要工作包括:
1、建立一個mainLoop工作執行緒,該執行緒用於完成實際的工作。
2、在mainLoop執行緒中,通過at_open開啟AT模組,同時啟動readLoop工作執行緒。readLoop工作執行緒負責從AT裝置中讀取資訊,並執行對應的函式呼叫。
3、呼叫at_open後,mainLoop執行緒向Rild程序傳送一個超時事件,該事件被Rild處理後,將呼叫initializeCallback函式。initializeCallback函式,將完成Modem的初始化工作。
其實上,mainLoop可以直接進行Modem初始化的工作;這裡傳送超時事件給Rild,通過回撥進行初始化,可能是為了確保RIL_startEventLoop已經執行成功。
4、mainLoop最後通過waitForClose監控AT模組,一旦AT模組被關閉,mainLoop將重新進行初始化AT模組的工作。
3、RIL_register
現在我們分析一下Rild的main函式中,最後一個關鍵函式RIL_register:
從上面的程式碼可以看出,RIL_register實際就是初始化監聽socket所需的引數,然後開始監聽socket是否有資料到來。
在前面已經提到過,Android中會創建出兩個Rild程序,每個Rild程序均會呼叫RIL_register函式。
雖然s_registerCalled為一個靜態變數,但在程序的維度上,它是私有的。因此,每個Rild程序均會成功呼叫一次RIL_register。
接下來,我們看看startListen函式:
static void startListen(RIL_SOCKET_ID socket_id, SocketListenParam* socket_listen_p) { ......... switch(socket_id) { case RIL_SOCKET_1: //注意此處的RIL_getRilSocketName strncpy(socket_name, RIL_getRilSocketName(), 9); break; .......... } //根據socket_name獲取對應的檔案描述符 fdListen = android_get_control_socket(socket_name); .............. //使Rild程序變成被動服務程序 ret = listen(fdListen, 4); .............. socket_listen_p->fdListen = fdListen; /* note: non-persistent so we can accept only one connection at a time */ //構造一個非超時任務加,注意persist為false,處理函式為listenCallback ril_event_set (socket_listen_p->listen_event, fdListen, false, listenCallback, socket_listen_p); //加入佇列,並trigger rilEventAddWakeup (socket_listen_p->listen_event);}3.1 RIL_getRilSocketName
startListen函式中,通過呼叫RIL_getRilSocketName得到需監聽的socket的名稱。
RIL_getRilSocketName的內容很簡單,就是返回變數rild。
那麼rild變數又是何時設定的呢?
對於第一個Rild程序,在ril.cpp中,定義了rild的內容為“rild”。
.........extern "C"char rild[MAX_SOCKET_NAME_LENGTH] = SOCKET_NAME_RIL;........#define SOCKET_NAME_RIL "rild"對於第二個Rild程序,在Rild啟動後,對應的main函式中,利用RIL_setRilSocketName修改rild的內容:
int main(int argc, char **argv) { ........ //第二個Rild程序,clientId不為0 if (strncmp(clientId, "0", MAX_CLIENT_ID_LENGTH)) { strlcat(rild, clientId, MAX_SOCKET_NAME_LENGTH); RIL_setRilSocketName(rild); } ........} extern "C"void RIL_setRilSocketName(const char * s) { strncpy(rild, s, MAX_SOCKET_NAME_LENGTH);}從上面的程式碼,我們可以看出兩個Rild程序確實監聽的是不同的socket。
3.2 listenCallback
利用listen函式將Rild變成監聽程序後,start_listen通過ril_event_set構造了一個非超時的任務,並利用rilEventAddWakeup將該任務加入到watch_table中。
注意到ril_event的fd為待監聽的socket,因此當ril_event被加入到watch_table後,該socket對應的fd將被加入到readFds中。
一旦該socket可讀(即客戶端connect成功),那麼eventLoop中的select函式將會返回,執行listenCallback函式。
實際上,由於該任務的persist屬性為false,因此執行完畢後,ril_event將從watch_table中移除,socket對應的fd也將被從readFds中移除。
這表明,Rild程序不會再監聽socket對應的connect請求,只支援一個客戶端的連線,僅會呼叫一次listenCallback函式。
至此,RIL_register的主要工作介紹完畢。從上述分析,我們可以看出RIL_register其實就是創建出與RIL.java通訊的服務端socket,然後監聽客戶端請求。一旦監聽到客戶端請求後,利用accept分配出對應的通訊用socket。然後,再監聽該分配出的socket,以處理客戶端發來的資料。
4、 Rild main函式總結
現在我們已經分析完Rild main函式的主要流程了,回過頭來看看Rild整體的設計思路: 
1、利用RIL_startEventLoop,初始化通訊框架。不論是初始化AT裝置,還是接收來自RIL.java的請求,都依賴於Rild程序的通訊架構,因此在main函式的最開始,對通訊框架進行了初始化。
2、利用RIL_Init開啟AT裝置,並完成modem的初始化。AP側利用RIL.java下發指令,最終還是需要利用AT傳給modem來執行。因此,在通訊框架初始化完畢後,首先就要完成AT和modem的配置。
3、利用RIL_register將Rild程序變成服務程序,等待RIL.java中socket的連線;連線成功後,開始處理來自RIL.java的資料。
三、例項分析
我們已經分析了Rild程序的工作,現在來結合資料業務撥號,看看實際過程中,Rild的工作情況。
1、RIL.java
首先,在之前的部落格中,介紹資料業務基礎類的建立時,我們提到過RIL.java在PhoneFactory的makeDefaultPhone中建立:
我們看看RIL的建構函式:
public RIL(Context context, int preferredNetworkType, int cdmaSubscription) { this(context, preferredNetworkType, cdmaSubscription, null);}public RIL(Context context, int preferredNetworkType, int cdmaSubscription, Integer instanceId) { ........ mSenderThread = new HandlerThread("RILSender" + mInstanceId); mSenderThread.start(); Looper looper = mSenderThread.getLooper(); //負責向Rild傳送訊息 mSender = new RILSender(looper); ConnectivityManager cm = (ConnectivityManager)context.getSystemService( Context.CONNECTIVITY_SERVICE); if (cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE) == false) { riljLog("Not starting RILReceiver: wifi-only"); } else { ........ //負責接收Rild傳送的訊息 mReceiver = new RILReceiver(); mReceiverThread = new Thread(mReceiver, "RILReceiver" + mInstanceId); mReceiverThread.start(); ........ } ..........}2、 RILReceiver
我們看看RILReceiver相關的函式:
從RILReceiver的程式碼可以看出,其主要功能就是完成與Rild程序中server socket的連線,然後接收並處理Rild程序發來的資料。
3、 setupDataCall
之前的部落格介紹資料業務撥號流程時,我們知道在DataConnection中,最終將通過呼叫RIL的setupDataCall函式,將訊息發往modem:
我們看看RILSender:
class RILSender extends Handler implements Runnable { ....... @Override public void handleMessage(Message msg) { RILRequest rr = (RILRequest)(msg.obj); RILRequest req = null; switch (msg.what) { case EVENT_SEND: case EVENT_SEND_ACK: try { LocalSocket s; s = mSocket; //將資料打包到data,發往Rild程序 ......... s.getOutputStream().write(dataLength); s.getOutputStream().write(data); ..... catch(IOException ex) { ....... } catch (RuntimeException exc) { ....... } break; ........ } }}4、processCommandsCallback
根據前面對Rild程序的分析,我們知道當Rild程序收到RIL.java中傳送來的資料後,將利用processCommandsCallback進行處理:
上面的程式碼中,出現了一個s_commands陣列,它儲存了一些CommandInfo結構,這個結構封裝了Rild對AT指令的處理函式。另外,Rild還定義了一個s_unsolResponses陣列,它封裝了unsolicited Response對應的一些處理函式。
static CommandInfo s_commands[] = {#include "ril_commands.h"};static UnsolResponseInfo s_unsolResponses[] = {#include "ril_unsol_commands.h"};typedef struct { //請求號 int requestNumber; //請求處理函式 void (*dispatchFunction) (Parcel &;p, struct RequestInfo *pRI); //結果處理函式 int(*responseFunction) (Parcel &;p, void *response, size_t responselen);} CommandInfo;typedef struct { int requestNumber; int (*responseFunction) (Parcel &;p, void *response, size_t responselen); WakeType wakeType;} UnsolResponseInfo;這裡我們重點看一下s_commands陣列。
CommandInfo按照requestNumber的先後順序加入到s_commands中,因此requestNumber就是對應CommandInfo的索引。
我們看看ril_commands.h:
在RIL.java中,指定了撥號對應的請求號為RIL_REQUEST_SETUP_DATA_CALL,因此Rild中對應的處理函式為dispatchDataCall。
5、dispatchDataCall
static void dispatchDataCall(Parcel&; p, RequestInfo *pRI) { ......... //轉換輸入引數後處理 if (s_callbacks.version < 4 &;&; numParams > numParamsRilV3) { .......... dispatchStrings(p2, pRI); } else { ......... dispatchStrings(p, pRI); }}static voiddispatchStrings (Parcel &;p, RequestInfo *pRI) { ........ startRequest; //處理輸入引數 ......... removeLastChar; closeRequest; //CALL_ONREQUEST是個巨集,實際上呼叫s_callbacks的onRequest函式 //s_callbacks的型別為RIL_RadioFunctions,在rild.c的main函式中,利用動態庫的RIL_Init函式得到;利用RIL_register函式儲存 CALL_ONREQUEST(pRI->pCI->requestNumber, pStrings, datalen, pRI, pRI->socket_id); .............}6、onRequest
此處,我們以Reference-ril中的onRequest為例,進行分析:
7、RIL_onRequestComplete
當指令處理完畢後,Reference-ril將呼叫RIL_onRequestComplete通知RIL.java處理結果。
可以看到RIL_onRequestComplete是一個巨集,實際上呼叫的是s_rilenv的OnRequestComplete函式。
在Rild程序的main函式中,呼叫RIL_Init時傳入了s_rilEnv,我們看看ril.cpp中的RIL_onRequestComplete:
8、processResponse
在前面介紹RILReceiver時,我們知道RILReceiver與Rild連線成功後,將會一直監聽發來的資料,並呼叫processResponse進行處理。
在理解了Rild搭建的通訊架構後,分析底層撥號的流程還是比較簡單的。
