前言
在上一篇文章里(http://blog.csdn.net/jason_wzn/article/details/53232022),簡要介紹了Android RIL的架構。這一篇文章,就來看一看RILD(RIL Daemon)相關的內容。Android RIL在HAL(Hardware Abstract Layer)層(C++層)由三個部分組成:
- RILD是系統的守護進程,主要用於初始化LIBRIL以及啟動廠商自定義的Vendor RIL;
- LIBRIL被RILD初始化完成后,用於與Vendor RIL之間進行交互,負責接收、發送指令;
- Vendor RIL是第三方廠商自定義的一個庫,用於向Modem發送指令或者接收來自LIBRIL或者Modem的指令。
三者之間的關系圖如下所示:
從這里可以看到,RILD在啟動時,負責將LibRil以及Vendor RIL進行初始化,將相應的回調函數以及調用接口進行注冊,LibRIL向vendor RIL提供了接口RIL_Env
,當Vendor有消息時,利用該回調返回;而Vendor RIL 同樣提供了接口RIL_RadioFunctions
,給LibRIl調用。這里涉及到3個主要問題:
- RILD是如何啟動?
- RILD是如何進行初始化操作的?
- 初始完成后,LIBRIL是如何進行消息的接收與發送的?
RILD是如何啟動的
RILD(RIL Daemon)是系統的守護進程,系統已啟動,就會一直運行。手機開機時,kernel完成初始化后,Android啟動一個初始化進程Init用於加載系統基礎服務,如文件系統,zygote進程,服務管家ServiceManager,以及RILD:
service ril-daemon /system/bin/rild class main socket rild stream 660 root radio socket rild-debug stream 660 radio system user root group radio cache inet misc audio log
這里,init進程從手機文件系統目錄system/bin/rild
中讀取RILD的可執行文件,加載到內存運行;同時,創建兩個socket端口:rild和rild-debug,其中rild用於RILJ與RILD之間的數據通信,而rild-debug則用於RILJ與RILD的調試。
- 開機流程可參考:http://blog.csdn.net/jason_wzn/article/details/52278533
- Android初始化語言init可參考:http://blog.csdn.net/jason_wzn/article/details/50790810
RILD是如何進行初始化的
RILD啟動后,一方面會去初始化Vendor RIL,將LIBRIL的回調接口RIL_Env
傳遞給Vendor RIL;同時將Vendor RIL的接口RIL_RadioFunctions
注冊到LIBRIL中,這樣一旦初始化完成,LIBRIL與Vendor RIL就可以進行數據的交換了。
來看一看RILD的代碼:
1 int main(int argc, char **argv) 2 { 3 ... 4 // Vendor RIL接口函數 5 const RIL_RadioFunctions *(*rilInit)(const struct RIL_Env *, int, char **); 6 const RIL_RadioFunctions *funcs; 7 ... 8 9 OpenLib: 10 11 //從指定路徑加載RILD可執行文件 12 dlHandle = dlopen(rilLibPath, RTLD_NOW); 13 14 if (dlHandle == NULL) { 15 RLOGE("dlopen failed: %s", dlerror()); 16 exit(EXIT_FAILURE); 17 } 18 19 // 啟動LIBRIL的事件處理線程 20 RIL_startEventLoop(); 21 // Vendor RIL初始化函數,返回一個RIL_RadioFunctions 22 rilInit = (const RIL_RadioFunctions *(*)(const struct RIL_Env *, int, char **))dlsym(dlHandle, "RIL_Init"); 23 ... 24 funcs = rilInit(&s_rilEnv, argc, rilArgv); 25 RLOGD("RIL_Init rilInit completed"); 26 // 將 RIL_RadioFunctions注冊到LIBRIL中 27 RIL_register(funcs); 28 29 RLOGD("RIL_Init RIL_register completed"); 30 }
RILD初始化主要完成兩件事:(1) 加載Vendor RIL的代碼,並對其進行初始化操作,將LIBRIL的接口RIL_Env
傳遞給Vendor RIL,用於回調;(2)開始RIL事件處理線程;(3)將Vendor RIL的接口注冊到LIBRIL中,這樣LIBRIL就可以將消息發送給Vendor RIL了。
下圖是RILD初始化LIBRIL以及Vendor RIL的一個簡化流程:
RIL_startEventLoop()
啟動RIL事件處理線程:
1 extern "C" void RIL_startEventLoop(void) { 2 /* spin up eventLoop thread and wait for it to get started */ 3 s_started = 0; 4 pthread_mutex_lock(&s_startupMutex); 5 ... 6 // eventLoop函數才是真正開始啟動事件處理線程的地方 7 int result = pthread_create(&s_tid_dispatch, &attr, eventLoop, NULL); 8 if (result != 0) { 9 RLOGE("Failed to create dispatch thread: %s", strerror(result)); 10 goto done; 11 } 12 13 while (s_started == 0) { 14 pthread_cond_wait(&s_startupCond, &s_startupMutex); 15 } 16 17 done: 18 pthread_mutex_unlock(&s_startupMutex); 19 } 20 21 // evetLoop 22 23 static void *eventLoop(void *param) { 24 int ret; 25 int filedes[2]; 26 //初始化事件隊列 27 ril_event_init(); 28 29 pthread_mutex_lock(&s_startupMutex); 30 31 s_started = 1; 32 pthread_cond_broadcast(&s_startupCond); 33 34 pthread_mutex_unlock(&s_startupMutex); 35 36 ret = pipe(filedes); 37 // 用於監聽wakeup事件的pipe端口 38 s_fdWakeupRead = filedes[0]; 39 s_fdWakeupWrite = filedes[1]; 40 //設置線程喚醒事件,喚醒時,回調processWakeupCallback函數 41 ril_event_set (&s_wakeupfd_event, s_fdWakeupRead, true, 42 processWakeupCallback, NULL); 43 44 rilEventAddWakeup (&s_wakeupfd_event); 45 46 // 真正干活的函數 47 ril_event_loop(); 48 // kill self to restart on error 49 kill(0, SIGKILL); 50 51 return NULL; 52 }
- RILD初始化vendor RIL之后,將返回的
RIL_RadioFunctions
返回給RILD,RILD接着將其注冊到LIBRIL中:
1 extern "C" void RIL_register (const RIL_RadioFunctions *callbacks) { 2 ... 3 /* Initialize socket1 parameters */ 4 s_ril_param_socket = { 5 RIL_SOCKET_1, /* socket_id */ 6 -1, /* fdListen */ 7 -1, /* fdCommand */ 8 PHONE_PROCESS, /* processName */ 9 &s_commands_event, /* commands_event */ 10 &s_listen_event, /* listen_event */ 11 processCommandsCallback, /* processCommandsCallback */ 12 NULL /* p_rs */ 13 }; 14 .... 15 // back compatibility 16 if (s_started == 0) { 17 RIL_startEventLoop(); 18 } 19 20 // start listen socket1 21 startListen(RIL_SOCKET_1, &s_ril_param_socket); 22 } 23 24 // startListen 25 static void startListen(RIL_SOCKET_ID socket_id, SocketListenParam* socket_listen_p) { 26 int fdListen = -1; 27 int ret; 28 char socket_name[10]; 29 30 memset(socket_name, 0, sizeof(char)*10); 31 32 switch(socket_id) { 33 case RIL_SOCKET_1: 34 strncpy(socket_name, RIL_getRilSocketName(), 9); 35 break; 36 .... 37 // 獲取 Unix domain socket對應的FD 38 fdListen = android_get_control_socket(socket_name); 39 // 監聽端口 40 ret = listen(fdListen, 4); 41 42 socket_listen_p->fdListen = fdListen; 43 // 設置監聽回調事件 listenCallback,RILJ主動連接RILD時,處理該回調 44 /* note: non-persistent so we can accept only one connection at a time */ 45 ril_event_set (socket_listen_p->listen_event, fdListen, false, 46 listenCallback, socket_listen_p); 47 //添加到事件隊列中,並喚醒事件處理線程 48 rilEventAddWakeup (socket_listen_p->listen_event); 49 }
源代碼:
/hardware/ril/libril/ril.cpp
接下來,我們就來看一看LIBRIL與Vendor RIL各自提供的接口函數。 這兩個接口都在/hardware/ril/include/telephony/ril.h
中進行了聲明。
Vendor RIL主要提供了5個接口,供LIBRIL調用:
-
1 RIL_RequestFunc是最主要的一個,所有從RILJ發送過來的請求均由該接口發送給Vendor RIL; 2 RIL_RadioStateRequest從LIBRIL獲取modem的即時狀態; 3 RIL_Supports判斷Vendor RIL是否支持某個請求命令; 4 RIL_Cancel取消某個請求命令; 5 RIL_GetVersion獲取RIL的版本號; 6 7 typedef struct { 8 int version; /* set to RIL_VERSION */ 9 RIL_RequestFunc onRequest; 10 RIL_RadioStateRequest onStateRequest; 11 RIL_Supports supports; 12 RIL_Cancel onCancel; 13 RIL_GetVersion getVersion; 14 } RIL_RadioFunctions; 15 16 17 // 將從RILJ發送過來的請求發送給Vendor RIL 18 typedef void (*RIL_RequestFunc) (int request, void *data, 19 size_t datalen, RIL_Token t, RIL_SOCKET_ID socket_id); 20 // 獲取 modem 狀態 21 typedef RIL_RadioState (*RIL_RadioStateRequest)(RIL_SOCKET_ID socket_id);
LIBRIL則向Vendor RIL提供了3個接口:
OnRequestComplete
:RIL請求完成后,通過該接口將數據返回給LIBRIL,由LIBRIL將數據寫入socketRILD
;OnUnsolicitedResponse
:CP主動上報消息給Vendor RIL后,通過該接口將消息傳給LIBRIL;RequestTimedCallback
:在指定時間內,LIBRIL調用回調函數RequestTimedCallback
;
1 struct RIL_Env { 2 // 請求完成,返回給LIBRIL 3 void (*OnRequestComplete)(RIL_Token t, RIL_Errno e, 4 void *response, size_t responselen); 5 6 // Vendor RIL接收到從CP主動上報的消息后,傳給LIBRIL 7 #if defined(ANDROID_MULTI_SIM) 8 void (*OnUnsolicitedResponse)(int unsolResponse, const void *data, size_t datalen, RIL_SOCKET_ID socket_id); 9 #else 10 /** 11 * "unsolResponse" is one of RIL_UNSOL_RESPONSE_* 12 * "data" is pointer to data defined for that RIL_UNSOL_RESPONSE_* 13 */ 14 void (*OnUnsolicitedResponse)(int unsolResponse, const void *data, size_t datalen); 15 #endif 16 /** 17 * Call user-specifed "callback" function on on the same thread that 18 * RIL_RequestFunc is called. If "relativeTime" is specified, then it specifies 19 * a relative time value at which the callback is invoked. If relativeTime is 20 * NULL or points to a 0-filled structure, the callback will be invoked as 21 * soon as possible 22 */ 23 // 指定時間內LIBRIL調用回調函數RIL_TimedCallback 24 void (*RequestTimedCallback) (RIL_TimedCallback callback, 25 void *param, const struct timeval *relativeTime); 26 };
代碼路徑:
/hardware/ril/rild/rild.c
初始化完成了 ,那么RIL事件處理線程是從何時開始處理事件的了?RIL事件處理線程是怎么又是同時處理來自RILJ以及Vendor RIL的消息的?下面就來看一看LIBRIL如何處理RIL事件的。
LIBRIL如何處理RIL事件
為處理RIL事件,LIBRIL提供了3個事件隊列(由雙向列表組成):
static struct ril_event * watch_table[MAX_FD_EVENTS];
static struct ril_event timer_list;
static struct ril_event pending_list;
其中,watch_table
用於事件的監測,timer_list
保存定時事件,而pending_list
用於保存即將被處理的事件列表。對LIBRIL來講,有3種類型的RIL事件需要處理:
// RILJ請求事件 static struct ril_event s_commands_event; // 事件處理線程喚醒事件 static struct ril_event s_wakeupfd_event; // RILD socket端口監聽事件 static struct ril_event s_listen_event;
上一節我們了解到,在RIL事件處理線程開始時,LIBRIL會添加一個s_wakeupfd_event
的喚醒事件,必要時對線程進行喚醒操作;在注冊Vendor RIL的接口時,注冊一個監聽事件s_listen_event
,當RILJ嘗試通過socket連接RILD時,處理該事件;當RILJ與RILD連接成功后,處理回調函數listenCallback
時,會添加一個 s_commands_event
事件,用於處理RILD socket的數據。
那么,LIBRIL是從何時開始處理這些事件的?上一節我們了解到,初始化時,LIBRIL啟動了一個專門的線程來處理RIL事件:
1 void ril_event_loop() 2 { 3 int n; 4 fd_set rfds; 5 struct timeval tv; 6 struct timeval * ptv; 7 8 for (;;) { 9 10 // make local copy of read fd_set 11 memcpy(&rfds, &readFds, sizeof(fd_set)); 12 .... 13 // 從FD集合中選擇可用的端口 14 n = select(nfds, &rfds, NULL, NULL, ptv); 15 .... 16 // 處理timer隊列中超時的事件 17 processTimeouts(); 18 // 處理監測隊列中的事件: listenCallback, 19 processReadReadies(&rfds, n); 20 // OK,fire pending list 21 firePending(); 22 } 23 }
該線程,一直監聽FD(File Descriptor)集合readFds
,如果有數據時,就會立即返回,進而開始執行事件的處理:首先處理定時事件隊列中的event,如果發現有超時的事件,就將其加入pending隊列中;接着,查看監測表(保存了最多8個事件)中是否有readFds
對應的RIL事件,如果存在,則也將其放入到pending隊列。最后,就要開始處理pending隊列了:
static void firePending() { dlog("~~~~ +firePending ~~~~"); struct ril_event * ev = pending_list.next; while (ev != &pending_list) { struct ril_event * next = ev->next; removeFromList(ev); // 執行回調函數: processWakeupCallback,listenCallback,processCommandsCallback... ev->func(ev->fd, 0, ev->param); ev = next; } dlog("~~~~ -firePending ~~~~"); }
源碼:
/android/hardware/ril/libril/samsung/ril_event.cpp
LIBRIL事件處理線程開始時,只有兩個事件:s_wakeupfd_event
與s_listen_event
,s_wakeupfd_event
事件在添加s_listen_event
事件,需要喚醒RIL事件處理線程被執行:
static void triggerEvLoop() { int ret; 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 { ret = write (s_fdWakeupWrite, " ", 1); } while (ret < 0 && errno == EINTR); } }
接着,開始執行s_listen_event
事件,調用回調函數listenCallback
:
1 static void listenCallback (int fd, short flags, void *param) { 2 .... 3 // 接受RILJ的鏈接請求 4 fdCommand = accept(fd, (sockaddr *) &peeraddr, &socklen); 5 6 /* check the credential of the other side and only accept socket from 7 * phone process 8 */ 9 is_phone_socket = 0; 10 11 err = getsockopt(fdCommand, SOL_SOCKET, SO_PEERCRED, &creds, &szCreds); 12 13 .... 14 15 ret = fcntl(fdCommand, F_SETFL, O_NONBLOCK); 16 .... 17 18 p_info->fdCommand = fdCommand; 19 20 p_rs = record_stream_new(p_info->fdCommand, MAX_COMMAND_BYTES); 21 22 p_info->p_rs = p_rs; 23 ril_event_set (p_info->commands_event, p_info->fdCommand, 1, 24 p_info->processCommandsCallback, p_info); 25 // 添加指令事件`s_commands_event` 26 rilEventAddWakeup (p_info->commands_event); 27 // 建立新的連接,告知RILJ鏈接成功,並上報radio狀態 28 onNewCommandConnect(p_info->socket_id); 29 }
下次處理pending事件隊列時,處理s_commands_event
,調用回調函數processCommandsCallback
:
1 static void processCommandsCallback(int fd, short flags, void *param) { 2 // 循環讀 RILD socket接口數據流 3 for (;;) { 4 /* loop until EAGAIN/EINTR, end of stream, or other error */ 5 // 讀取 socket數據流 6 ret = record_stream_get_next(p_rs, &p_record, &recordlen); 7 8 if (ret == 0 && p_record == NULL) { 9 /* end-of-stream */ 10 break; 11 } else if (ret < 0) { 12 break; 13 } else if (ret == 0) { /* && p_record != NULL */ 14 processCommandBuffer(p_record, recordlen, p_info->socket_id); 15 } 16 } 17 ....
// processCommandBuffer static int processCommandBuffer(void *buffer, size_t buflen, RIL_SOCKET_ID socket_id) { RequestInfo *pRI; ... pRI = (RequestInfo *)calloc(1, sizeof(RequestInfo)); pRI->token = token; // 根據 RILJ的REQUEST類型來獲取CommandInfo pRI->pCI = &(s_commands[request]); pRI->socket_id = socket_id; ... // 將請求分配給對應的函數處理 pRI->pCI->dispatchFunction(p, pRI); return 0; } }
上述代碼中,s_commands
將所有RILJ的請求命令,對應的請求函數以及響應處理函數組成一個類型為commandInfo
的結構體數組,等請求從CP返回時,就可以調用對應的響應函數來處理返回的結果了:
1 static CommandInfo s_commands[] = { 2 #include "ril_commands.h" 3 }; 4 5 {0, NULL, NULL}, //none 6 {RIL_REQUEST_GET_SIM_STATUS, dispatchVoid, responseSimStatus}, 7 {RIL_REQUEST_ENTER_SIM_PIN, dispatchStrings, responseInts}, 8 {RIL_REQUEST_ENTER_SIM_PUK, dispatchStrings, responseInts}, 9 {RIL_REQUEST_ENTER_SIM_PIN2, dispatchStrings, responseInts}, 10 {RIL_REQUEST_ENTER_SIM_PUK2, dispatchStrings, responseInts}, 11 {RIL_REQUEST_CHANGE_SIM_PIN, dispatchStrings, responseInts}, 12 {RIL_REQUEST_CHANGE_SIM_PIN2, dispatchStrings, responseInts}, 13 ....
源碼:
/android/hardware/ril/libril/samsung/ril_commands.h