Android RILD運行機制詳解


前言

在上一篇文章里(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 and LIBRIL

從這里可以看到,RILD在啟動時,負責將LibRil以及Vendor RIL進行初始化,將相應的回調函數以及調用接口進行注冊,LibRIL向vendor RIL提供了接口RIL_Env,當Vendor有消息時,利用該回調返回;而Vendor RIL 同樣提供了接口RIL_RadioFunctions,給LibRIl調用。這里涉及到3個主要問題:

  1. RILD是如何啟動?
  2. RILD是如何進行初始化操作的?
  3. 初始完成后,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的調試。

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的一個簡化流程:

RILD init process

  • 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將數據寫入socket RILD;
  • 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_events_listen_events_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

參考文獻

版權聲明:本文為博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/wang2119/article/details/53392526


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM