Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android7.0 Rild工作流程

Android7.0 Rild工作流程

編輯:關於Android編程

一、基於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進程(雙卡手機)。

shell:/ $ ps | grep rild
radio     572   1     113732 14792 hrtimer_na 0000000000 S /system/bin/rild
radio     869   1     109604 13944 hrtimer_na 0000000000 S /system/bin/rild

shell:/ $ ps | grep phone
radio     2621  605   2019984 74424 SyS_epoll_ 0000000000 S com.android.phone

shell:/ $ ps | grep init
root      1     0     9648   1712  SyS_epoll_ 0000000000 S /init

shell:/ $ ps | grep zygote
root      605   1     2195280 70956 poll_sched 0000000000 S zygote64
root      606   1     1610708 59144 poll_sched 0000000000 S zygote

我們通過usb連接手機後,通過adb shell進入終端,通過ps和grep命令,可以得到上述結果。
明顯可以看到一個Phone進程對應者兩個Rild進程;同時Rild進程由init進程加載,Phone進程由zygote進程加載。

2、Rild與BP之間並沒有直接通信,而是引入了廠商的動態庫。
這種設計應該是為了保證靈活性吧。
用面向對象的思想來看,我們可以認為Rild是一個接口,定義了AP、BP雙向通信時需要使用的最基本的函數。不同的廠商都需要滿足這個接口,以提供手機最基本的通信功能。
至於具體如何實現,是完全獨立和自由的。

二、Rild的啟動
在hardware/ril/rild/rild.rc中定義了Rild啟動時對應的選項:

service ril-daemon /system/bin/rild
    class main
    socket rild stream 660 root radio
    socket sap_uim_socket1 stream 660 bluetooth bluetooth
    socket rild-debug stream 660 radio system
    user root
    group radio cache inet misc audio log readproc wakelock

在Android 7.0之前的版本中,該文件的內容是被定義在init.rc中的。
到了Android7.0 之後,init.rc文件中的許多內容均被移出,添加到各個進程中。如前面分析Vold進程時,對應的啟動文件定義於vold.rc中。
個人猜測這些文件應該會在編譯時,重新集成起來,畢竟在在rild對應的Android.mk中增加了下述字段:

.......
LOCAL_MODULE:= rild
LOCAL_MODULE_TAGS := optional
//新增字段
LOCAL_INIT_RC := rild.rc
.......

目前手邊沒有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中:

extern "C" void
RIL_startEventLoop(void) {
    /* spin up eventLoop thread and wait for it to get started */
    s_started = 0;
    .........
    //創建工作線程,線程ID存入s_tid_dispatch,對應執行函數為eventLoop
    int result = pthread_create(&s_tid_dispatch, &attr, eventLoop, NULL);
    .........
    //工作線程eventLoop運行後,會設置s_started為1,並觸發s_startupCond
    //這裡的等待的目的是保證RIL_startEventLoop返回前,工作線程創建並運行成功
    while (s_started == 0) {
        pthread_cond_wait(&s_startupCond, &s_startupMutex);
    }
    .........
}

我們需要跟進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函數:

void ril_event_init()
{
    MUTEX_INIT();

    FD_ZERO(&readFds);
    //初始化timer_list,任務插入時按時間排序
    init_list(&timer_list);
    //初始化pending_list,保存每次需要執行的任務
    init_list(&pending_list);
    //初始化監控表
    memset(watch_table, 0, sizeof(watch_table));
}

static void init_list(struct ril_event * list)
{
    memset(list, 0, sizeof(struct ril_event));
    list->next = list;
    list->prev = list;
    list->fd = -1;
}

//MAX_FD_EVENTS為8
//watchtable將用於保存FD加入到readFDs中的ril_event
static struct ril_event * watch_table[MAX_FD_EVENTS];

可以看出ril_event_init就是初始化readFds、timer_list、pending_list和watch_table,其中後三種數據結構均是用來存放ril_event的。

根據前文的代碼,我們知道Rild的main函數中,通過調用RIL_startEventLoop單獨啟動了一個線程運行eventLoop,這是一個工作線程。
這個工作線程就是靠ril_event結構體來描述自己需要執行的任務,並且它將多個任務按時間順序組織起來,保存在任務隊列中。
ril_event的數據結構如下:

struct ril_event {
    struct ril_event *next;
    struct ril_event *prev;

    int fd;
    int index;
    bool persist;
    struct timeval timeout;
    ril_event_cb func;
    void *param;
};

如果從設計模式的角度來理解Rild的工作線程,易於看出,這其實是比較典型的命令模式。
就如同之前博客分析vold進程一樣,CommandListener收到數據後,調用對應Command的runCommand方法進行處理。
此處,工作線程收到ril_event後,加入隊列中,當需要處理時,調用ril_event對應的處理函數func。

1.2 創建wakeupfd ril_event
工作線程完成數據結構的初始化後,創建了第一個ril_event:

........
ril_event_set (&s_wakeupfd_event, s_fdWakeupRead, true,
            processWakeupCallback, NULL);
........
// Initialize an event
void ril_event_set(struct ril_event * ev, int fd, bool persist, ril_event_cb func, void * param)
{
    dlog("~~~~ ril_event_set %x ~~~~", (unsigned int)ev);
    memset(ev, 0, sizeof(struct ril_event));
    ev->fd = fd;
    ev->index = -1;
    ev->persist = persist;
    ev->func = func;
    ev->param = param;
    fcntl(fd, F_SETFL, O_NONBLOCK);
}

從上面的代碼可以看出,創建的第一個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 list
void 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 進入事件等待循環中
接下來工作線程進入到事件等待循環中:

void ril_event_loop() {
    ........
    for (;;) {
        // make local copy of read fd_set
        memcpy(&rfds, &readFds, sizeof(fd_set));
        //根據timer_list來計算select函數等待的時間,timer_list已經按任務的執行時間排序
        if (-1 == calcNextTimeout(&tv)) {
            // no pending timers; block indefinitely
            dlog("~~~~ no timers; blocking indefinitely ~~~~");
            ptv = NULL;
        } else {
            dlog("~~~~ blocking for %ds + %dus ~~~~", (int)tv.tv_sec, (int)tv.tv_usec);
            ptv = &tv;
        }
        ............
        n = select(nfds, &rfds, NULL, NULL, ptv);
        //將timer_list中超時的任務加入到pending_list中
        processTimeouts();
        //將watchtables中收到的任務加入到pending_list中
        processReadReadies(&rfds, n);
        //處理pendinglist中的任務
        firePending();
    }
}

static int calcNextTimeout(struct timeval * tv) 
{
    struct ril_event * tev = timer_list.next;
    struct timeval now;

    //利用clock_gettime獲取當前時間
    getNow(&now);

    // Sorted list, so calc based on first node
    if (tev == &timer_list) {
        // no pending timers
        return -1;
    }

    if (timercmp(&tev->timeout, &now, >)) {
        //計算出等待時間
        timersub(&tev->timeout, &now, tv);
    } else {
        // timer already expired.
        tv->tv_sec = tv->tv_usec = 0;
    }
    return 0;
}

static void processTimeouts()
{
    ............
    struct timeval now;
    struct ril_event * tev = timer_list.next;
    struct ril_event * next;

    getNow(&now);
    ............
    //目前還沒提及timer_list,實際上調用ril_timer_add函數時,可以將對時間有要求的ril_event加入到timer_list中,按照超時時間從小到大排列
    while ((tev != &timer_list) && (timercmp(&now, &tev->timeout, >))) {
        // Timer expired
        dlog("~~~~ firing timer ~~~~");
        next = tev->next;
        removeFromList(tev);
        //輪詢timerlist表,將timer_list中的任務加入到pending_list中
        addToList(tev, &pending_list);
        tev = next;
    }
}

static void processReadReadies(fd_set * rfds, int n)
{
    ..........
    //前面代碼已提過,當調用ril_event_add時,新加入的ril_event將存入watch_table
    for (int i = 0; (i < MAX_FD_EVENTS) && (n > 0); i++) {
        struct ril_event * rev = watch_table[i];
        if (rev != NULL && FD_ISSET(rev->fd, rfds)) {
            addToList(rev, &pending_list);
            //persist值為false時,才會移除
            //記得麼?在eventLoop調用ril_event_loop前,加入了一個s_wakeupfd_event,其persist值為true,永不移除
            if (rev->persist == false) {
                removeWatch(rev, i);
            }
            n--;
        }
    }
    ..........
}

static void firePending() {
    ...........
    struct ril_event * ev = pending_list.next;
    while (ev != &pending_list) {
        struct ril_event * next = ev->next;
        removeFromList(ev);
        //執行對對應的執行函數
        //每次循環時s_wakeupfd_event的執行函數processWakeupCallback都會被調用
        ev->func(ev->fd, 0, ev->param);
        ev = next;
    }
    ..........
}

static void processWakeupCallback(int fd, short flags, void *param) {
    .......
    /* empty our wakeup socket out */
    do {
        //當有其它線程調用triggerEvLoop時,會向s_fdWakeupWrite中寫入數據
        //s_wakeupfd_event被觸發時,負責清空緩存
        ret = read(s_fdWakeupRead, &buff, sizeof(buff));
    } while (ret > 0 || (ret < 0 && errno == EINTR));
}

至此,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中。

const RIL_RadioFunctions *RIL_Init(const struct RIL_Env *env, int argc, char **argv)
{
    .............
    s_rilenv = env;
    //參數處理
    ...........
    pthread_attr_init (&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    ret = pthread_create(&s_tid_mainloop, &attr, mainLoop, NULL);

    return &s_callbacks;
}

/*** Static Variables ***/
static const RIL_RadioFunctions s_callbacks = {
    RIL_VERSION,
    //以下皆是函數指針
    onRequest,
    currentState,
    onSupports,
    onCancel,
    getVersion
};

從代碼上來看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結構體:

//此處略去函數指針的定義
typedef struct {
    int version;        /* set to RIL_VERSION */
    //用於向BP提交一個請求
    RIL_RequestFunc onRequest;

    //用於查詢BP的狀態
    RIL_RadioStateRequest onStateRequest;

    //用於判斷動態庫是否支持某人requestCode
    RIL_Supports supports;

    //用於取消一個提交給BP的請求
    RIL_Cancel onCancel;

    //查詢動態庫版本
    RIL_GetVersion getVersion;
} 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:

static void *
mainLoop(void *param __unused)
{
    ........
    //為AT模塊設置一些回調函數。
    //對於Reference-Ril庫而言,AT模塊就是對串口設備通信的封裝,用於和BP通信
    at_set_on_reader_closed(onATReaderClosed);
    at_set_on_timeout(onATTimeout);

    for (;;) {
        fd = -1;
        while(fd < 0) {
            //得到串口設備的文件描述符
            .......
        }
        ......
        //打開AT設備,傳入回調函數
        ret = at_open(fd, onUnsolicited);
        .......
        //向Rild提交一個超時任務,該任務的處理函數為initializeCallback
        RIL_requestTimedCallback(initializeCallback, NULL, &TIMEVAL_0);
        .......
        //如果AT模塊被關閉,則waitForClose返回,但是該線程不會退出,而是從for循環那開始重新執行一次
        //因此AT模塊一旦被關閉,將會重新被打開
        waitForClose();
        .......
    }
}

從上面的代碼可以看出,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發送一個超時任務。

#define RIL_requestTimedCallback(a,b,c) s_rilenv->RequestTimedCallback(a,b,c)

可以看到RIL_requestTimedCallback是一個宏,實際上還是通過RIL_Env中RequestTimedCallback函數發送超時任務。

我們看看ril.cpp中,該函數的實現:

extern "C" void
RIL_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:

extern "C" void
RIL_register (const RIL_RadioFunctions *callbacks) {
    .........
    //判斷之前是否初始化過
    if (s_registerCalled > 0) {
        RLOGE("RIL_register has been called more than once. "
                "Subsequent call ignored");
        return;
    }
    .........
    /* Initialize socket1 parameters */
    s_ril_param_socket = {
                        RIL_SOCKET_1,             /* socket_id */
                        -1,                       /* fdListen */
                        -1,                       /* fdCommand */
                        PHONE_PROCESS,            /* processName */
                        &s_commands_event,        /* commands_event */
                        &s_listen_event,          /* listen_event */
                        processCommandsCallback,  /* processCommandsCallback */
                        NULL                      /* p_rs */
                        };
    ............
    s_registerCalled = 1;
    ............
    // start listen socket1
    startListen(RIL_SOCKET_1, &s_ril_param_socket);
    //代碼中的SIM_COUNT宏並未定義,略去下文
    ...........
}

typedef struct SocketListenParam {
    RIL_SOCKET_ID socket_id;
    int fdListen;
    int fdCommand;
    char* processName;
    struct ril_event* commands_event;
    struct ril_event* listen_event;
    void (*processCommandsCallback)(int fd, short flags, void *param);
    RecordStream *p_rs;
    RIL_SOCKET_TYPE type;
} SocketListenParam;

從上面的代碼可以看出,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的名稱。

static char * RIL_getRilSocketName() {
    return rild;
}

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函數。

static void listenCallback (int fd, short flags, void *param) {
    ........
    //保存配置的socket監聽參數
    SocketListenParam *p_info = (SocketListenParam *)param;
    ........
    //接收客戶端連接,並將返回的socket保存起來
    fdCommand = accept(fd, (sockaddr *) &peeraddr, &socklen);
    //進行權限控制
    ........
    //設置socket為非阻塞的
    ret = fcntl(fdCommand, F_SETFL, O_NONBLOCK);
    ........
    if(NULL == sapSocket) {
        ........
        p_info->fdCommand = fdCommand;
        //為socket分配一個接收緩存
        p_rs = record_stream_new(p_info->fdCommand, MAX_COMMAND_BYTES);
        p_info->p_rs = p_rs;

        //構造一個新的非超時任務,此時persist屬性為true(1),於是eventLoop將一致select監聽socket是否有數據到來
        //當有數據到來時,將調用processCommandsCallback進行處理
        ril_event_set (p_info->commands_event, p_info->fdCommand, 1,
        p_info->processCommandsCallback, p_info);
        rilEventAddWakeup (p_info->commands_event);

        //向客戶端發送主動上報信息,即向RIL.java上報信息
        onNewCommandConnect(p_info->socket_id);
    } else {
        ..........
    }
}

至此,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中創建:

public static void makeDefaultPhone(Context context) {
    .......
    for (int i = 0; i < numPhones; i++) {
        ........
        sCommandsInterfaces[i] = new RIL(context, networkModes[i],
                cdmaSubscription, i);
    }
    ......  
}

我們看看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相關的函數:

class RILReceiver implements Runnable {
    byte[] buffer;

    RILReceiver() {
        buffer = new byte[RIL_MAX_COMMAND_BYTES];
    }

    @Override
    public void run() {
        int retryCount = 0;
        String rilSocket = "rild";

        try {
            for(;;) {
                LocalSocket s = null;
                LocalSocketAddress l;

                //根據InstanceId決定連接哪個Rild進程的socket
                if (mInstanceId == null || mInstanceId == 0 ) {
                    rilSocket = SOCKET_NAME_RIL[0];
                } else {
                    rilSocket = SOCKET_NAME_RIL[mInstanceId];
                }

                try {
                    s = new LocalSocket();
                    l = new LocalSocketAddress(rilSocket,
                            LocalSocketAddress.Namespace.RESERVED);
                    //連接rild進程
                    s.connect(l);
                } catch(IOException ex) {
                    .........
                }

                retryCount = 0;

                //連接Rild進程socket後,保留創建出的socket
                mSocket = s;

                try {
                    InputStream is = mSocket.getInputStream();

                    for (;;) {
                        Parcel p;

                        //不斷讀取到來的數據
                        length = readRilMessage(is, buffer);
                        //解析字節流
                        ......
                        //進行處理
                        processResponse(p);
                        ......
                    } catch (java.io.IOException ex) {
                        .......
                    } catch (Throwable tr) {
                        .......
                    }

                    //異常斷開,執行關閉socket的操作
                    .......
            }
        } catch (Throwable tr) {
            ..........
        }
    }
    .........
}

從RILReceiver的代碼可以看出,其主要功能就是完成與Rild進程中server socket的連接,然後接收並處理Rild進程發來的數據。

3、 setupDataCall
之前的博客介紹數據業務撥號流程時,我們知道在DataConnection中,最終將通過調用RIL的setupDataCall函數,將消息發往modem:

@Override
public void setupDataCall(....) {
    //構造一個request,有唯一的serialNumber,RIL_REQUEST_SETUP_DATA_CALL表明該Request的目的
    RILRequest rr = RILRequest.obtain(RIL_REQUEST_SETUP_DATA_CALL, result);

    //將參數寫入到RILRequest的Parcel對象中
    ........

    send(rr);
}

private void send(RILRequest rr) {
    Message msg;

    //RILReceiver中已經創建出mSocket,同時連接了Rild進程
    if (mSocket == null) {
        rr.onError(RADIO_NOT_AVAILABLE, null);
        rr.release();
        return;
    }

    //構造消息發送給RILSender
    msg = mSender.obtainMessage(EVENT_SEND, rr);
    acquireWakeLock(rr, FOR_WAKELOCK);
    msg.sendToTarget();
}

我們看看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進行處理:

static void processCommandsCallback(int fd, short flags, void *param) {
    ............
    for (;;) {
        /* loop until EAGAIN/EINTR, end of stream, or other error */
        //record_stream_get_next將socket中接收的數據全部讀取到緩沖區
        //當緩沖區還有數據時,優先解析緩沖區數據;緩沖區無數據時,才從socket再次read
        ret = record_stream_get_next(p_rs, &p_record, &recordlen);

        if (ret == 0 && p_record == NULL) {
            /* end-of-stream */
            break;
        } else if (ret < 0) {
            break;
        } else if (ret == 0) { /* && p_record != NULL */
            //解析出的命令利用processCommandBuffer處理
            processCommandBuffer(p_record, recordlen, p_info->socket_id);
        }
    }
    //錯誤處理
    ............
}

static int
processCommandBuffer(void *buffer, size_t buflen, RIL_SOCKET_ID socket_id) {
    //判斷參數有效性,解析數據等操作
    ..........
    pRI = (RequestInfo *)calloc(1, sizeof(RequestInfo));
    .........
    pRI->token = token;
    //決定了對應的執行函數
    pRI->pCI = &(s_commands[request]);
    pRI->socket_id = socket_id;
    ...........
    pRI->pCI->dispatchFunction(p, pRI);
    return 0;
}

上面的代碼中,出現了一個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:

{0, NULL, NULL},
......
{RIL_REQUEST_SETUP_DATA_CALL, dispatchDataCall, responseSetupDataCall},
......

在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 void
dispatchStrings (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為例,進行分析:

static void
onRequest (int request, void *data, size_t datalen, RIL_Token t) {
    //判斷當前radio狀態,是否能夠下發AT指令
    .......
    switch (request) {
        .......
        case RIL_REQUEST_SETUP_DATA_CALL:
            //下發AT指令,完成撥號
            requestSetupDataCall(data, datalen, t);
            break;
        .......
    }
}

7、RIL_onRequestComplete
當指令處理完畢後,Reference-ril將調用RIL_onRequestComplete通知RIL.java處理結果。

#define RIL_onRequestComplete(t, e, response, responselen) s_rilenv->OnRequestComplete(t,e, response, responselen)

可以看到RIL_onRequestComplete是一個宏,實際上調用的是s_rilenv的OnRequestComplete函數。
在Rild進程的main函數中,調用RIL_Init時傳入了s_rilEnv,我們看看ril.cpp中的RIL_onRequestComplete:

extern "C" void
RIL_onRequestComplete(RIL_Token t, RIL_Errno e, void *response, size_t responselen) {
    ........
    //從參數中,知道請求是從哪個socket發過來的
    socket_id = pRI->socket_id;
    fd = findFd(socket_id);
    ........
    if (pRI->cancelled == 0) {
        ........
        if (response != NULL) {
            // there is a response payload, no matter success or not.
            //調用responseDataCall,在返回結果中加入ip地址等信息
            ret = pRI->pCI->responseFunction(p, response, responselen);
            ........
        }
        ........
        sendResponse(p, socket_id);
    }
    ........
}

static int
sendResponse (Parcel &p, RIL_SOCKET_ID socket_id) {
    printResponse;
    //利用write函數,將數據發往RIL.java
    return sendResponseRaw(p.data(), p.dataSize(), socket_id);
}

8、processResponse
在前面介紹RILReceiver時,我們知道RILReceiver與Rild連接成功後,將會一直監聽發來的數據,並調用processResponse進行處理。

private void
processResponse (Parcel p) {
    int type;

    type = p.readInt();

    if (type == RESPONSE_UNSOLICITED || type == RESPONSE_UNSOLICITED_ACK_EXP) {
        //處理主動上報
        processUnsolicited (p, type);
    } else if (type == RESPONSE_SOLICITED || type == RESPONSE_SOLICITED_ACK_EXP) {
        RILRequest rr = processSolicited (p, type);
        .......
    } else if (type == RESPONSE_SOLICITED_ACK) {
        .......
    }
}

private RILRequest
processSolicited (Parcel p, int type) {
    .......
    //根據serialNumber,將隊列中的記錄移除
    rr = findAndRemoveRequestFromList(serial);
    .......
    if (error == 0 || p.dataAvail() > 0) {
        try {switch (rr.mRequest) {
        ........
        case RIL_REQUEST_SETUP_DATA_CALL: ret =  responseSetupDataCall(p); break;
        ........
        }
        ....
    }
    .......
    if (error == 0) {
        .......
        if (rr.mResult != null) {
            AsyncResult.forMessage(rr.mResult, ret, null);
            //將結果返回給DataConnection
            rr.mResult.sendToTarget();
        }
    }
    .......
}

在理解了Rild搭建的通信架構後,分析底層撥號的流程還是比較簡單的。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved