2013年9月11日 星期三

android note

文章出處:http://wukiandroid.blogspot.tw/2012/05/ril-event.html

RIL daemon


RILD
rild.c, ril.cpp, ril_event.cpp, reference-ril.c(),qcril.c(以Qualcomm verdor 提供的qcril.c取代reference_ril.c)以上是相關的.c or .cpp檔
RILD上承RILJ,也就是ril.java,往下是將AT command我們用QMI送給modem
所以簡單來說,RILD的功能就是將RIL request轉成AT command

這個daemon在開機的時候就會init起來,其參數的定義放在init.rc裡面
class main
    socket rild stream 660 root radio
    socket rild-debug stream 660 radio system
    user root

首先,先看rild.c中的main()
RIL_startEventLoop()這個function的實作放在ril.cpp中
RIL_startEventLoop(void) {
    ....
    ret = pthread_create(&s_tid_dispatch, &attr, eventLoop, NULL);
    ...
}

他會create一個thread, 入口是eventLoop(),同樣實作在ril.cpp
static void *
eventLoop(void *param) {
    ...
    ril_event_init();
    ....
    ril_event_loop();
    ...
}

RILD裡頭有一套event的機制來處理上層送下來的request.
ril_event_init()用來init所需的event queue,總共有三種: timer list, pending list, watch_table

main()中的rilInit(&s_rilEnv, argc, rilArgv)
這個function會參照到reference-ril.c中的RIL_Init()
這裡頭又會create "mainLoop"這個thread
ret = pthread_create(&s_tid_mainloop, &attr, mainLoop, NULL);

看一下mainLoop裡頭做的事情
有一部分的事情是要透過socket跟qemud溝通?? 這一部分還沒看熟
另外
ret = at_open(fd, onUnsolicited);
這個function內部會create "readLoop"這個thread,它用來讀從channel送上來的AT command,並且註冊用來處理unsolicited AT command的function

還有
RIL_requestTimedCallback(initializeCallback, NULL, &TIMEVAL_0)這個function會將會新增一event,並且填入callback function: initializeCallback(),看一下此callback做的事情

    at_send_command("ATE0Q0V1", NULL);
    ... 
    /*  No auto-answer */
    at_send_command("ATS0=0", NULL);
    ... 
    /*  Extended errors */
    at_send_command("AT+CMEE=1", NULL);
    ... 

看樣子是要intialize AT command channel

rilInit()所回傳的pointer是指向s_callbacks,定義在reference-ril.c



static const RIL_RadioFunctions s_callbacks = {
    RIL_VERSION,
    onRequest,
    currentState,
    onSupports,
    onCancel,
    getVersion
};


此pointer接著會被丟到RIL_register(),定義在ril.cpp中
看一下RIL_register()中所做的事情

s_fdListen = android_get_control_socket(SOCKET_NAME_RIL);
...
ret = listen(s_fdListen, 4);
...
ril_event_set (&s_listen_event, s_fdListen, false,
                listenCallback, NULL);
...
rilEventAddWakeup (&s_listen_event);

android_get_control_socket()取得rild socket的file descriptor,這邊的rild也就是之前提到定義在init.rc內的rild

接著,透過ril_event_set()新增一event,填入此rild的fd與callback function

    ril_event_set (&s_listen_event, s_fdListen, false,
                listenCallback, NULL);

rilEventAddWakeup()會把event放入watch_table裡面

下圖是關於以上說明的簡單示意圖






























所以會有3個thread被create出來,mainLoop, readLoop, eventLoop
藍色部分是會被set的RIL event. 到時候會被排入ril event loop中執行

event說明
1. ril_event_set (&s_listen_event, s_fdListen, false,listenCallback, NULL);
    s_fdCommand = accept(s_fdListen, (sockaddr *) &peeraddr, &socklen);
    // accept a connection並且取得new socket的file descriptor,此s_fd_Command接著會被傳入
    // s_commands_event裡面,用來read從RIL.java送下來的ril request 

    ret = fcntl(s_fdCommand, F_SETFL, O_NONBLOCK);
    // 設定s_fdCommand的屬性 (??)

     p_rs = record_stream_new(s_fdCommand, MAX_COMMAND_BYTES);
     // read data from s_fdCommand 

2. ril_event_set (&s_commands_event, s_fdCommand, 1,
        processCommandsCallback, p_rs);
for (;;) {
        ret = record_stream_get_next(p_rs, &p_record, &recordlen);

        // read data from s_fdCommand
       ...
        processCommandBuffer(p_record, recordlen);
        // data 在此function裡面會被打包成RequestInfo,也就是pRI
}

看一下processCommandBuffer()內所做的事情,它定義在ril.cpp中
先介紹兩種資料結構,Parcel與RequestInfo
1. Parcel: http://developer.android.com/reference/android/os/Parcel.html
用來放data的容器(資料結構),RILJ要往RILD送的data都會先封裝在Parcel裡面再透過socket傳送

2. RequestInfo:
    typedef struct RequestInfo {
        int32_t token;      //this is not RIL_Token
        CommandInfo *pCI;
        struct RequestInfo *p_next;
        char cancelled;
        char local;         // responses to local commands do not go back to command process
    } RequestInfo;

token: ril request (from RILJ)的sequence number
pCI: callback function for this ril request

    typedef struct {
        int requestNumber;
        void (*dispatchFunction) (Parcel &p, struct RequestInfo *pRI);
        int(*responseFunction) (Parcel &p, void *response, size_t responselen);
    } CommandInfo;

requestNumber: ril request ID
dipspatchFunction: callback function to handle request
responseFunction: callback function to handle response

首先,拿ril request ID與sequence number出來

    status = p.readInt32(&request);
    status = p.readInt32 (&token);

將token與查表取得callback function填入

    pRI->token = token;
    pRI->pCI = &(s_commands[request]);

s_commands內的值定義在ril_commands.h
example:
    {RIL_REQUEST_OPERATOR, dispatchVoid, responseStrings},
    {RIL_REQUEST_RADIO_POWER, dispatchInts, responseVoid},
    {RIL_REQUEST_DTMF, dispatchString, responseVoid},

接下來便會去執行dispatch function

    pRI->pCI->dispatchFunction(p, pRI);

以send SMS為例
{RIL_REQUEST_SEND_SMSdispatchStringsresponseSMS}

ril request ID: RIL_REQUEST_SEND_SMS
dispatch function: dispatchStrings
response function: responseSMS

進入dispatchStrings後Parcel內的data會被取出,最後會呼叫
s_callbacks.onRequest(pRI->pCI->requestNumber, pStrings, datalen, pRI);

onRequest定義在reference-ril.c,此時就會轉成AT command往modem送

** ril request的interface定義在ril.h中