天天看點

Android——RIL 機制源碼分析

Android 電話系統架構介紹

在android系統中rild運作在AP上,AP上的應用通過rild發送AT指令給BP,BP接收到資訊後又通過rild傳送給AP。AP與BP之間有兩種通信方式:

1.Solicited Response:Ap向Bp發送請求,Bp給Ap發送回複,該類型的AT指令及其回調函數以數組的形式存放在Ril_commands.h檔案中:

    {數組中的索引号,請求回調函數,響應回調函數}

{0, NULL, NULL},                   //none
    {RIL_REQUEST_GET_SIM_STATUS, dispatchVoid, responseSimStatus},
    {RIL_REQUEST_ENTER_SIM_PIN, dispatchStrings, responseInts},
    {RIL_REQUEST_ENTER_SIM_PUK, dispatchStrings, responseInts},
    {RIL_REQUEST_ENTER_SIM_PIN2, dispatchStrings, responseInts},
    {RIL_REQUEST_ENTER_SIM_PUK2, dispatchStrings, responseInts},
    {RIL_REQUEST_CHANGE_SIM_PIN, dispatchStrings, responseInts},
    {RIL_REQUEST_CHANGE_SIM_PIN2, dispatchStrings, responseInts},
    {RIL_REQUEST_ENTER_NETWORK_DEPERSONALIZATION, dispatchStrings, responseInts},
    {RIL_REQUEST_GET_CURRENT_CALLS, dispatchVoid, responseCallList},
    {RIL_REQUEST_DIAL, dispatchDial, responseVoid},
    {RIL_REQUEST_GET_IMSI, dispatchStrings, responseString},
    {RIL_REQUEST_HANGUP, dispatchInts, responseVoid},
    {RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND, dispatchVoid, responseVoid},
 ...
           

不同手機廠商使用的AT指令不完全相同,為了保密,AP與BP之間通過各廠商自己的相關動态庫來通信。

Android——RIL 機制源碼分析
Android——RIL 機制源碼分析

RIL子產品由rild守護程序、libril.so、librefrence.so三部分組成:

  1.rild子產品被編譯為一個可執行檔案,實作一個main函數作為整個ril子產品的入口點。在初始化時使用dlopen打開librefrence_ril.so,從中取出并執行RIL_Init函數,得到RIL_RadioFunctions指針,通過RIL_register()函數注冊到libril.so庫中,其源碼結構如下:

Android——RIL 機制源碼分析

 2.libril.so是共享庫,主要負責同上層的通信工作,接收ril的請求,并傳遞給librefrence_ril.so,同時将librefrence_ril.so傳回的消息送給調用程序,源碼結構如下所示:

Android——RIL 機制源碼分析

3.librefrence_ril.so是由各手機廠商自己實作,在rild程序運作中通過dlopen方式加載,主要負責跟modem硬體通信,轉換來自libril.so的請求為AT指令,同時監聽Modem的回報資訊給libril.so

Android——RIL 機制源碼分析

Android的電話系統主要分為三個部分,java層的各種電話相關應用,java層的Phone Service,主要為上層提供API,同時與native進行通信,可以看做為電話系統的用戶端,native層的電話服務程序RILD,負責為上層提供各種電話功能服務,直接與modem進行互動:

Android——RIL 機制源碼分析

Android電話系統設計架構圖:

Android——RIL 機制源碼分析

由于Android 開發者使用的Modem 是不一樣的,各種指令格式,初始化序列都可能不一樣,是以為了消除這些差别,Android 設計者将ril 做了一個抽象,使用一個虛拟電話的概念,不同modem相關的AT指令或者通信協定編譯成相應的動态連結庫.so檔案,Rild 是具體的AT 指令合成者和應答解析者。

Android電話系統代碼結構圖:

Android——RIL 機制源碼分析

RILD架構設計

在android的電話系統中,在native層實作了電話服務的服務端,由RILD服務與modem的互動,在java層實作電話的用戶端,本文主要介紹電話系統的服務端RILD程序,以下是RILD的設計架構圖:

Android——RIL 機制源碼分析

RILD源碼分析

接下來通過源碼對RILD的整個架構進行詳細介紹。

在kernel啟動完成後,将啟動第一個應用程序Init程序,在android之Init程序啟動過程源碼分析一文中對init程序的啟動流程進行了詳細的介紹。init程序在啟動過程中将讀取init.rc檔案來啟動一些重量級的native服務,rild程序就是通過配置在init.rc中來啟動的。

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 sdcard_rw log
           

RILD程序入口函數分析

接下來給出的是RILD程序啟動的時序圖:

Android——RIL 機制源碼分析

hardware\ril\rild\rild.c

int main(int argc, char **argv)
{
    const char * rilLibPath = NULL;
    char **rilArgv;
    void *dlHandle;
    const RIL_RadioFunctions *(*rilInit)(const struct RIL_Env *, int, char **);
    const RIL_RadioFunctions *funcs;
    char libPath[PROPERTY_VALUE_MAX];
    unsigned char hasLibArgs = 0;
    int i;
  umask(S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH);
  //rild啟動無參數
    for (i = 1; i < argc ;) {
        if (0 == strcmp(argv[i], "-l") && (argc - i > 1)) {
            rilLibPath = argv[i + 1];
            i += 2;
        } else if (0 == strcmp(argv[i], "--")) {
            i++;
            hasLibArgs = 1;
            break;
        } else {
            usage(argv[0]);
        }
    }
  if (rilLibPath == NULL) {
      //通過Android屬性系統讀取屬性"rild.libpath"的值,即lib庫的存放路徑
        if ( 0 == property_get(LIB_PATH_PROPERTY, libPath, NULL)) {
            goto done;
        } else {
            rilLibPath = libPath;
        }
  }
##################################################################################
                            判斷是否為模拟器
##################################################################################
#if 1
    {
        static char*  arg_overrides[3];
        static char   arg_device[32];
        int           done = 0;
#define  REFERENCE_RIL_PATH  "/system/lib/libreference-ril.so"
        /* first, read /proc/cmdline into memory */
        char          buffer[1024], *p, *q;
        int           len;
        int           fd = open("/proc/cmdline",O_RDONLY);
        if (fd < 0) {
            LOGD("could not open /proc/cmdline:%s", strerror(errno));
            goto OpenLib;
        }
        //讀取/proc/cmdline檔案中的内容
        do {
            len = read(fd,buffer,sizeof(buffer)); }
        while (len == -1 && errno == EINTR);
        if (len < 0) {
            LOGD("could not read /proc/cmdline:%s", strerror(errno));
            close(fd);
            goto OpenLib;
        }
        close(fd);
        //判斷是否為模拟器,對于真機,此處條件為false
        if (strstr(buffer, "android.qemud=") != NULL)
        {
            int  tries = 5;
#define  QEMUD_SOCKET_NAME    "qemud"
            while (1) {
                int  fd;
                sleep(1);
                fd = socket_local_client(QEMUD_SOCKET_NAME,
                            ANDROID_SOCKET_NAMESPACE_RESERVED,
                            SOCK_STREAM );
                if (fd >= 0) {
                    close(fd);
                    snprintf( arg_device, sizeof(arg_device), "%s/%s",
                                ANDROID_SOCKET_DIR, QEMUD_SOCKET_NAME );
                    arg_overrides[1] = "-s";
                    arg_overrides[2] = arg_device;
                    done = 1;
                    break;
                }
                LOGD("could not connect to %s socket: %s",QEMUD_SOCKET_NAME, strerror(errno));
                if (--tries == 0)
                    break;
            }
            if (!done) {
                LOGE("could not connect to %s socket (giving up): %s",
                    QEMUD_SOCKET_NAME, strerror(errno));
                while(1)
                    sleep(0x00ffffff);
            }
        }

        /* otherwise, try to see if we passed a device name from the kernel */
        if (!done) do { //true
#define  KERNEL_OPTION  "android.ril="
#define  DEV_PREFIX     "/dev/"
            //判斷/proc/cmdline中的内容是否包含"android.ril="
            p = strstr( buffer, KERNEL_OPTION );
            if (p == NULL)
                break;
            p += sizeof(KERNEL_OPTION)-1;
            q  = strpbrk( p, " \t\n\r" );
            if (q != NULL)
                *q = 0;
            snprintf( arg_device, sizeof(arg_device), DEV_PREFIX "%s", p );
            arg_device[sizeof(arg_device)-1] = 0;
            arg_overrides[1] = "-d";
            arg_overrides[2] = arg_device;
            done = 1;
        } while (0);
        
        if (done) { //false
            argv = arg_overrides;
            argc = 3;
            i    = 1;
            hasLibArgs = 1;
            rilLibPath = REFERENCE_RIL_PATH;
            LOGD("overriding with %s %s", arg_overrides[1], arg_overrides[2]);
        }
    }
OpenLib:
#endif
##################################################################################
                            動态庫裝載
##################################################################################

  switchUser();//設定Rild程序的組使用者為radio
  //加載廠商自定義的庫
    ①dlHandle = dlopen(rilLibPath, RTLD_NOW);
    if (dlHandle == NULL) {
        fprintf(stderr, "dlopen failed: %s\n", dlerror());
        exit(-1);
  }
  //<span style="font-family:'Courier New';color:#080000;line-height: 1.5; white-space: pre-wrap;">建立用戶端事件監聽線程</span>
  ②RIL_startEventLoop();
  //<span style="font-family:'Courier New';color:#080000;line-height: 1.5; white-space: pre-wrap;">通過dlsym定位到RIL_Init函數的位址,并且強制轉換為RIL_RadioFunctions的函數指針</span>
    ③rilInit = (const RIL_RadioFunctions *(*)(const struct RIL_Env *, int, char **))dlsym(dlHandle, "RIL_Init");
    if (rilInit == NULL) {
        fprintf(stderr, "RIL_Init not defined or exported in %s\n", rilLibPath);
        exit(-1);
    }
    if (hasLibArgs) { //false
        rilArgv = argv + i - 1;
        argc = argc -i + 1;
    } else {
        static char * newArgv[MAX_LIB_ARGS];
        static char args[PROPERTY_VALUE_MAX];
        rilArgv = newArgv;
        property_get(LIB_ARGS_PROPERTY, args, "");//通過屬性系統讀取"rild.libargs"屬性值
        argc = make_argv(args, rilArgv);
    }
    // Make sure there's a reasonable argv[0]
  rilArgv[0] = argv[0];
  //調用RIL_Init函數來初始化rild,傳入參數s_rilEnv,傳回RIL_RadioFunctions位址
  ④funcs = rilInit(&s_rilEnv, argc, rilArgv);
  //<span style="font-family:'Courier New';color:#080000;line-height: 1.5; white-space: pre-wrap;">注冊用戶端事件處理接口</span><span style="font-family:'Courier New';color:#080000;line-height: 1.5; white-space: pre-wrap;">RIL_RadioFunctions</span><span style="font-family:'Courier New';color:#080000;line-height: 1.5; white-space: pre-wrap;">,并建立socket監聽事件</span>
    ⑤RIL_register(funcs);
done:
    while(1) {
        // sleep(UINT32_MAX) seems to return immediately on bionic
        sleep(0x00ffffff);
    }
}
           

在main函數中主要完成以下工作:

1.解析指令行參數,通過判斷是否為模拟器采取不同的方式來讀取libreference-ril.so庫的存放路徑;

2.使用dlopen手動裝載libreference-ril.so庫;

3.啟動事件循環處理;

4.從libreference-ril.so庫中取得RIL_Init函數位址,并使用該函數将libril.so庫中的RIL_Env接口注冊到libreference-ril.so庫,同時将libreference-ril.so庫中的RIL_RadioFunctions接口注冊到到libril.so庫中,建立起libril.so庫與libreference-ril.so庫通信橋梁;

啟動事件循環處理eventLoop工作線程

建立多路I/O驅動機制的消息隊列,用來接收上層發出的指令以及往Modem發送AT指令的工作,時整個RIL系統的核心部分。建立一個事件分發線程s_tid_dispatch,線程執行體為eventLoop。

hardware\ril\libril\Ril.cpp

extern "C" void RIL_startEventLoop(void) {
    int ret;
    pthread_attr_t attr;
    /* spin up eventLoop thread and wait for it to get started */
    s_started = 0;
    pthread_mutex_lock(&s_startupMutex);
    pthread_attr_init (&attr);
  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
  //建立一個工作線程eventLoop
  ret = pthread_create(&s_tid_dispatch, &attr, eventLoop, NULL);
  //確定函數傳回前eventLoop線程啟動運作
    while (s_started == 0) {
        pthread_cond_wait(&s_startupCond, &s_startupMutex);
    }
    pthread_mutex_unlock(&s_startupMutex);
    if (ret < 0) {
        LOGE("Failed to create dispatch thread errno:%d", errno);
        return;
    }
}
           

eventLoop執行時序圖:

Android——RIL 機制源碼分析
static void * eventLoop(void *param) {
    int ret;
    int filedes[2];
    ril_event_init(); //初始化請求隊列
    pthread_mutex_lock(&s_startupMutex);
    s_started = 1; //eventLoop線程運作标志位
    pthread_cond_broadcast(&s_startupCond);
  pthread_mutex_unlock(&s_startupMutex);
  //建立匿名管道
    ret = pipe(filedes);
    if (ret < 0) {
        LOGE("Error in pipe() errno:%d", errno);
        return NULL;
  }
  //s_fdWakeupRead為管道讀端
  s_fdWakeupRead = filedes[0];
  //s_fdWakeupWrite為管道寫端
  s_fdWakeupWrite = filedes[1];
  //設定管道讀端為O_NONBLOCK非阻塞
  fcntl(s_fdWakeupRead, F_SETFL, O_NONBLOCK);
  //初始化s_wakeupfd_event結構體的内容,句柄為s_fdWakeupRead,回調函數為   processWakeupCallback
    ril_event_set (&s_wakeupfd_event, s_fdWakeupRead, true,processWakeupCallback, NULL);
    ①rilEventAddWakeup (&s_wakeupfd_event);
    // Only returns on error
    ②ril_event_loop();
    LOGE ("error in event_loop_base errno:%d", errno);
    return NULL;
}
           

在rild中定義了event的概念,Rild支援兩種類型的事件:

1. 定時事件:根據事件的執行時間來啟動執行,通過ril_timer_add添加到time_list隊列中

2. Wakeup事件:這些事件的句柄fd将加入的select IO多路複用的句柄池readFDs中,當對應的fd可讀時将觸發這些事件。對于處于listen端的socket,fd可讀表示有個用戶端連接配接,此時需要調用accept接受連接配接。

事件定義如下:

struct ril_event {
    struct ril_event *next;
    struct ril_event *prev;
    int fd;  //檔案句柄
    int index; //該事件在監控表中的索引 
    bool persist; //如果是保持的,則不從watch_list 中删除
    struct timeval timeout; //任務執行時間
    ril_event_cb func; //回調事件處理函數
    void *param; //回調時參數
};
           

在Rild程序中的幾個重要事件有

static struct ril_event s_commands_event;
ril_event_set (&s_commands_event, s_fdCommand, 1,processCommandsCallback, p_rs)

static struct ril_event s_wakeupfd_event;
ril_event_set (&s_wakeupfd_event, s_fdWakeupRead, true,processWakeupCallback, NULL)

static struct ril_event s_listen_event;
ril_event_set (&s_listen_event, s_fdListen, false,listenCallback, NULL)

static struct ril_event s_wake_timeout_event;
ril_timer_add(&(p_info->event), &myRelativeTime);

static struct ril_event s_debug_event;
ril_event_set (&s_debug_event, s_fdDebug, true,debugCallback, NULL)
           

在RILD中定義了三個事件隊列,用于處理不同的事件:

/事件監控隊列

static struct ril_event * watch_table[MAX_FD_EVENTS];

//定時事件隊列

static struct ril_event timer_list;

//處理事件隊列

static struct ril_event pending_list; //待處理事件隊列,事件已經觸發,需要所回調處理的事件

Android——RIL 機制源碼分析

添加事件

1.添加 Wakeup 事件  

static void rilEventAddWakeup(struct ril_event *ev) {
    ril_event_add(ev); //向監控表watch_table添加一個s_wakeupfd_event事件
    triggerEvLoop(); //向管道s_fdWakeupWrite中寫入之來觸發事件循環
}
           
void ril_event_add(struct ril_event * ev)
{
    dlog("~~~~ +ril_event_add ~~~~");
    MUTEX_ACQUIRE();
    for (int i = 0; i < MAX_FD_EVENTS; i++) { //周遊監控表watch_table
        if (watch_table[i] == NULL) { //從監控表中查找空閑的索引,然後把該任務加入到監控表中
            watch_table[i] = ev; //向監控表中添加事件
            ev->index = i; //事件的索引設定為在監控表中的索引
            dlog("~~~~ added at %d ~~~~", i);
            dump_event(ev);
            FD_SET(ev->fd, &readFds); //将添加的事件對應的句柄添加到句柄池readFds中
            if (ev->fd >= nfds) nfds = ev->fd+1; //修改句柄最大值
            dlog("~~~~ nfds = %d ~~~~", nfds);
            break;
        }
    }
    MUTEX_RELEASE();
    dlog("~~~~ -ril_event_add ~~~~");
}
           

2.添加定時事件

void ril_timer_add(struct ril_event * ev, struct timeval * tv)
{
    dlog("~~~~ +ril_timer_add ~~~~");
    MUTEX_ACQUIRE();
    struct ril_event * list;
    if (tv != NULL) {
        list = timer_list.next;
        ev->fd = -1; // make sure fd is invalid
        struct timeval now;
        getNow(&now);
        timeradd(&now, tv, &ev->timeout);
        // keep list sorted
        while (timercmp(&list->timeout, &ev->timeout, < ) && (list != &timer_list)) {
            list = list->next;
        }
        // list now points to the first event older than ev
        addToList(ev, list);
    }
    MUTEX_RELEASE();
    dlog("~~~~ -ril_timer_add ~~~~");
}
           

觸發事件

static void triggerEvLoop() {
    int ret;
  if (!pthread_equal(pthread_self(), s_tid_dispatch)) { //如果目前線程ID不等于事件分發線程eventLoop的線程ID
      do {
            ret = write (s_fdWakeupWrite, " ", 1); //向管道寫端寫入值1來觸發eventLoop事件循環
         } while (ret < 0 && errno == EINTR);
    }
}
           

處理事件

void ril_event_loop()
{
    int n;
    fd_set rfds;
    struct timeval tv;
    struct timeval * ptv;
    for (;;) {
        memcpy(&rfds, &readFds, sizeof(fd_set));
        if (-1 == calcNextTimeout(&tv)) {
            dlog("~~~~ no timers; blocking indefinitely ~~~~");
            ptv = NULL;
        } else {
            dlog("~~~~ blocking for %ds + %dus ~~~~", (int)tv.tv_sec, (int)tv.tv_usec);
            ptv = &tv;
        }
        //使用select 函數等待在FDS 上,隻要FDS 中記錄的裝置有資料到來,select 就會設定相應的标志位并傳回。readFDS 記錄了所有的事件相關裝置句柄。readFDS 中句柄是在在AddEvent 加入的。
        printReadies(&rfds);
        n = select(nfds, &rfds, NULL, NULL, ptv); 
        printReadies(&rfds);
        dlog("~~~~ %d events fired ~~~~", n);
        if (n < 0) {
            if (errno == EINTR) continue;
            LOGE("ril_event: select error (%d)", errno);
            return;
        }
        processTimeouts(); //從timer_list中查詢執行時間已到的事件,并添加到pending_list中
        processReadReadies(&rfds, n); //從watch_table中查詢資料可讀的事件,并添加到pending_list中去處理,如果該事件不是持久事件,則同時從watch_table中删除
        //周遊pending_list,調用事件處理回調函數處理所有事件
        firePending();
    }
}
           

在eventLoop工作線程中,循環處理到來的事件及定時結束事件,整個處理流程如下圖所示:

Android——RIL 機制源碼分析

首先通過Linux中的select多路I/O複用對句柄池中的所有句柄進行監控,當有事件到來時select傳回,否則阻塞。當select傳回時,表示有事件的到來,通過調用processTimeouts函數來處理逾時事件,處理方式是周遊time_list連結清單以查詢逾時事件,并将逾時事件移入到pending_list連結清單中,接着調用processReadReadies函數來處理觸發的事件,處理方式為周遊watch_table清單以查詢觸發的事件,并将觸發的事件移入到pending_list連結清單中,如果該事件不是持久事件,還需要從watch_table清單中移除,當查詢完兩種待處理的事件并放入到pending_list連結清單中後,調用firePending函數對待處理的事件進行集中處理,處理方式為周遊連結清單,調用每一個事件的回調函數。

 1.逾時事件查詢
static void processTimeouts()
{
    dlog("~~~~ +processTimeouts ~~~~");
    MUTEX_ACQUIRE();
    struct timeval now;
    struct ril_event * tev = timer_list.next;
    struct ril_event * next;
    getNow(&now); //擷取目前時間
  dlog("~~~~ Looking for timers <= %ds + %dus ~~~~", (int)now.tv_sec, (int)now.tv_usec);
  //如果目前時間大于事件的逾時時間,則将該事件從timer_list中移除,添加到pending_list
    while ((tev != &timer_list) && (timercmp(&now, &tev->timeout, >))) {
        dlog("~~~~ firing timer ~~~~");
        next = tev->next;
        removeFromList(tev); //從timer_list中移除事件
        addToList(tev, &pending_list); //将事件添加到pending_list
        tev = next;
    }
    MUTEX_RELEASE();
    dlog("~~~~ -processTimeouts ~~~~");
}
           
2.可讀事件查詢
static void processReadReadies(fd_set * rfds, int n)
{
    dlog("~~~~ +processReadReadies (%d) ~~~~", n);
  MUTEX_ACQUIRE(); 
  //周遊watch_table數組,根據select傳回的句柄n查找對應的事件
    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); //将該事件添加到pending_list中
            if (rev->persist == false) { //如果該事件不是持久事件還要從watch_table中移除
                removeWatch(rev, i);
            }
            n--;
        }
    }
    MUTEX_RELEASE();
    dlog("~~~~ -processReadReadies (%d) ~~~~", n);
}
           
3.事件處理
static void firePending()
{
    dlog("~~~~ +firePending ~~~~");
    struct ril_event * ev = pending_list.next;
    while (ev != &pending_list) { //周遊pending_list連結清單,處理連結清單中的所有事件
        struct ril_event * next = ev->next;
        removeFromList(ev); //将處理完的事件從pending_list中移除
        ev->func(ev->fd, 0, ev->param); //調用事件處理的回調函數
        ev = next;
    }
    dlog("~~~~ -firePending ~~~~");
}
           

RIL_Env定義

hardware\ril\include\telephony\ril.h

struct RIL_Env {
    //動态庫完成請求後通知處理結果的接口
  void (*OnRequestComplete)(RIL_Token t, RIL_Errno e,void *response, size_t responselen);
    //動态庫unSolicited Response通知接口
  void (*OnUnsolicitedResponse)(int unsolResponse, const void *data,size_t datalen);
    //向Rild送出一個逾時任務的接口
    void (*RequestTimedCallback) (RIL_TimedCallback callback,void *param, const struct timeval *relativeTime);
};
           

hardware\ril\rild\rild.c

s_rilEnv變量定義:

static struct RIL_Env s_rilEnv = {
    RIL_onRequestComplete,
    RIL_onUnsolicitedResponse,
    RIL_requestTimedCallback
};
           

在hardware\ril\libril\ril.cpp中實作了RIL_Env的各個接口函數

1.RIL_onRequestComplete

extern "C" void RIL_onRequestComplete(RIL_Token t, RIL_Errno e, void *response, size_t responselen) {
    RequestInfo *pRI;
    int ret;
    size_t errorOffset;
    pRI = (RequestInfo *)t;
    if (!checkAndDequeueRequestInfo(pRI)) {
        LOGE ("RIL_onRequestComplete: invalid RIL_Token");
        return;
    }
    if (pRI->local > 0) {
        // Locally issued command...void only!
        // response does not go back up the command socket
        LOGD("C[locl]< %s", requestToString(pRI->pCI->requestNumber));
        goto done;
    }
    appendPrintBuf("[%04d]< %s",pRI->token, requestToString(pRI->pCI->requestNumber));
    if (pRI->cancelled == 0) {
        Parcel p;
        p.writeInt32 (RESPONSE_SOLICITED);
        p.writeInt32 (pRI->token);
        errorOffset = p.dataPosition();
        p.writeInt32 (e);
        if (response != NULL) {
            // there is a response payload, no matter success or not.
            ret = pRI->pCI->responseFunction(p, response, responselen);
            /* if an error occurred, rewind and mark it */
            if (ret != 0) {
                p.setDataPosition(errorOffset);
                p.writeInt32 (ret);
            }
        }
        if (e != RIL_E_SUCCESS) {
            appendPrintBuf("%s fails by %s", printBuf, failCauseToString(e));
        }
        if (s_fdCommand < 0) {
            LOGD ("RIL onRequestComplete: Command channel closed");
        }
        sendResponse(p);
    }
done:
    free(pRI);
}
           

通過調用responseXXX将底層響應傳給客戶程序

2.RIL_onUnsolicitedResponse

extern "C" void RIL_onUnsolicitedResponse(int unsolResponse, void *data,
                                size_t datalen)
{
    int unsolResponseIndex;
    int ret;
    int64_t timeReceived = 0;
    bool shouldScheduleTimeout = false;
    if (s_registerCalled == 0) {
        // Ignore RIL_onUnsolicitedResponse before RIL_register
        LOGW("RIL_onUnsolicitedResponse called before RIL_register");
        return;
    }
    unsolResponseIndex = unsolResponse - RIL_UNSOL_RESPONSE_BASE;
    if ((unsolResponseIndex < 0)
        || (unsolResponseIndex >= (int32_t)NUM_ELEMS(s_unsolResponses))) {
        LOGE("unsupported unsolicited response code %d", unsolResponse);
        return;
    }
    // Grab a wake lock if needed for this reponse,
    // as we exit we'll either release it immediately
    // or set a timer to release it later.
    switch (s_unsolResponses[unsolResponseIndex].wakeType) {
        case WAKE_PARTIAL:
            grabPartialWakeLock();
            shouldScheduleTimeout = true;
        break;
        case DONT_WAKE:
        default:
            // No wake lock is grabed so don't set timeout
            shouldScheduleTimeout = false;
            break;
    }
    // Mark the time this was received, doing this
    // after grabing the wakelock incase getting
    // the elapsedRealTime might cause us to goto
    // sleep.
    if (unsolResponse == RIL_UNSOL_NITZ_TIME_RECEIVED) {
        timeReceived = elapsedRealtime();
    }
    appendPrintBuf("[UNSL]< %s", requestToString(unsolResponse));
    Parcel p;
    p.writeInt32 (RESPONSE_UNSOLICITED);
    p.writeInt32 (unsolResponse);
    ret = s_unsolResponses[unsolResponseIndex].responseFunction(p, data, datalen);
    if (ret != 0) {
        // Problem with the response. Don't continue;
        goto error_exit;
    }
    // some things get more payload
    switch(unsolResponse) {
        case RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED:
            p.writeInt32(s_callbacks.onStateRequest());
            appendPrintBuf("%s {%s}", printBuf,
                radioStateToString(s_callbacks.onStateRequest()));
        break;
        case RIL_UNSOL_NITZ_TIME_RECEIVED:
            // Store the time that this was received so the
            // handler of this message can account for
            // the time it takes to arrive and process. In
            // particular the system has been known to sleep
            // before this message can be processed.
            p.writeInt64(timeReceived);
        break;
    }
    ret = sendResponse(p);
    if (ret != 0 && unsolResponse == RIL_UNSOL_NITZ_TIME_RECEIVED) {
        // Unfortunately, NITZ time is not poll/update like everything
        // else in the system. So, if the upstream client isn't connected,
        // keep a copy of the last NITZ response (with receive time noted
        // above) around so we can deliver it when it is connected
        if (s_lastNITZTimeData != NULL) {
            free (s_lastNITZTimeData);
            s_lastNITZTimeData = NULL;
        }
        s_lastNITZTimeData = malloc(p.dataSize());
        s_lastNITZTimeDataSize = p.dataSize();
        memcpy(s_lastNITZTimeData, p.data(), p.dataSize());
    }
    // For now, we automatically go back to sleep after TIMEVAL_WAKE_TIMEOUT
    // FIXME The java code should handshake here to release wake lock
    if (shouldScheduleTimeout) {
        // Cancel the previous request
        if (s_last_wake_timeout_info != NULL) {
            s_last_wake_timeout_info->userParam = (void *)1;
        }
        s_last_wake_timeout_info= internalRequestTimedCallback(wakeTimeoutCallback, NULL,
                                            &TIMEVAL_WAKE_TIMEOUT);
    }
    return;
error_exit:
    if (shouldScheduleTimeout) {
        releaseWakeLock();
    }
}
           

這個函數處理modem從網絡端接收到的各種事件,如網絡信号變化,撥入的電話,收到短信等。然後傳給客戶程序。

3.RIL_requestTimedCallback

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 *) malloc (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_set(&(p_info->event), -1, false, userTimerCallback, p_info);
    ril_timer_add(&(p_info->event), &myRelativeTime);
    triggerEvLoop();
    return p_info;
}
           

RIL_RadioFunctions定義

用戶端向Rild發送請求的接口,由各手機廠商實作。

hardware\ril\include\telephony\Ril.h

typedef struct {
    int version; //Rild版本
    RIL_RequestFunc onRequest; //AP請求接口
    RIL_RadioStateRequest onStateRequest;//BP狀态查詢
    RIL_Supports supports;
    RIL_Cancel onCancel;
    RIL_GetVersion getVersion;//動态庫版本
} RIL_RadioFunctions;
           
static const RIL_RadioFunctions s_callbacks = {
    RIL_VERSION,
    onRequest,
    currentState,
    onSupports,
    onCancel,
    getVersion
};
           

在hardware\ril\reference-ril\reference-ril.c中實作了RIL_RadioFunctions的各個接口函數

1.onRequest

static void onRequest (int request, void *data, size_t datalen, RIL_Token t)
{
    ATResponse *p_response;
    int err;
    LOGD("onRequest: %s", requestToString(request));
    /* Ignore all requests except RIL_REQUEST_GET_SIM_STATUS
     * when RADIO_STATE_UNAVAILABLE.
     */
    if (sState == RADIO_STATE_UNAVAILABLE
        && request != RIL_REQUEST_GET_SIM_STATUS
    ) {
        RIL_onRequestComplete(t, RIL_E_RADIO_NOT_AVAILABLE, NULL, 0);
        return;
    }
    /* Ignore all non-power requests when RADIO_STATE_OFF
     * (except RIL_REQUEST_GET_SIM_STATUS)
     */
    if (sState == RADIO_STATE_OFF&& !(request == RIL_REQUEST_RADIO_POWER
            || request == RIL_REQUEST_GET_SIM_STATUS)
    ) {
        RIL_onRequestComplete(t, RIL_E_RADIO_NOT_AVAILABLE, NULL, 0);
        return;
    }
    switch (request) {
        case RIL_REQUEST_GET_SIM_STATUS: {
            RIL_CardStatus *p_card_status;
            char *p_buffer;
            int buffer_size;
            int result = getCardStatus(&p_card_status);
            if (result == RIL_E_SUCCESS) {
                p_buffer = (char *)p_card_status;
                buffer_size = sizeof(*p_card_status);
            } else {
                p_buffer = NULL;
                buffer_size = 0;
            }
            RIL_onRequestComplete(t, result, p_buffer, buffer_size);
            freeCardStatus(p_card_status);
            break;
        }
        case RIL_REQUEST_GET_CURRENT_CALLS:
            requestGetCurrentCalls(data, datalen, t);
            break;
        case RIL_REQUEST_DIAL:
            requestDial(data, datalen, t);
            break;
        case RIL_REQUEST_HANGUP:
            requestHangup(data, datalen, t);
            break;
        case RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND:
            // 3GPP 22.030 6.5.5
            // "Releases all held calls or sets User Determined User Busy
            //  (UDUB) for a waiting call."
            at_send_command("AT+CHLD=0", NULL);
            /* success or failure is ignored by the upper layer here.
               it will call GET_CURRENT_CALLS and determine success that way */
            RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
            break;
        case RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND:
            // 3GPP 22.030 6.5.5
            // "Releases all active calls (if any exist) and accepts
            //  the other (held or waiting) call."
            at_send_command("AT+CHLD=1", NULL);
            /* success or failure is ignored by the upper layer here.
               it will call GET_CURRENT_CALLS and determine success that way */
            RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
            break;
        case RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE:
            // 3GPP 22.030 6.5.5
            // "Places all active calls (if any exist) on hold and accepts
            //  the other (held or waiting) call."
            at_send_command("AT+CHLD=2", NULL);

#ifdef WORKAROUND_ERRONEOUS_ANSWER
            s_expectAnswer = 1;
#endif /* WORKAROUND_ERRONEOUS_ANSWER */
            /* success or failure is ignored by the upper layer here.
               it will call GET_CURRENT_CALLS and determine success that way */
            RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
            break;
        case RIL_REQUEST_ANSWER:
            at_send_command("ATA", NULL);
#ifdef WORKAROUND_ERRONEOUS_ANSWER
            s_expectAnswer = 1;
#endif /* WORKAROUND_ERRONEOUS_ANSWER */
            /* success or failure is ignored by the upper layer here.
               it will call GET_CURRENT_CALLS and determine success that way */
            RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
            break;
        case RIL_REQUEST_CONFERENCE:
            // 3GPP 22.030 6.5.5
            // "Adds a held call to the conversation"
            at_send_command("AT+CHLD=3", NULL);
            /* success or failure is ignored by the upper layer here.
               it will call GET_CURRENT_CALLS and determine success that way */
            RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
            break;
        case RIL_REQUEST_UDUB:
            /* user determined user busy */
            /* sometimes used: ATH */
            at_send_command("ATH", NULL);
            /* success or failure is ignored by the upper layer here.
               it will call GET_CURRENT_CALLS and determine success that way */
            RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
            break;
        case RIL_REQUEST_SEPARATE_CONNECTION:
            {
                char  cmd[12];
                int   party = ((int*)data)[0];
                // Make sure that party is in a valid range.
                // (Note: The Telephony middle layer imposes a range of 1 to 7.
                // It's sufficient for us to just make sure it's single digit.)
                if (party > 0 && party < 10) {
                    sprintf(cmd, "AT+CHLD=2%d", party);
                    at_send_command(cmd, NULL);
                    RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
                } else {
                    RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
                }
            }
            break;
        case RIL_REQUEST_SIGNAL_STRENGTH:
            requestSignalStrength(data, datalen, t);
            break;
        case RIL_REQUEST_REGISTRATION_STATE:
        case RIL_REQUEST_GPRS_REGISTRATION_STATE:
            requestRegistrationState(request, data, datalen, t);
            break;
        case RIL_REQUEST_OPERATOR:
            requestOperator(data, datalen, t);
            break;
        case RIL_REQUEST_RADIO_POWER:
            requestRadioPower(data, datalen, t);
            break;
        case RIL_REQUEST_DTMF: {
            char c = ((char *)data)[0];
            char *cmd;
            asprintf(&cmd, "AT+VTS=%c", (int)c);
            at_send_command(cmd, NULL);
            free(cmd);
            RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
            break;
        }
        case RIL_REQUEST_SEND_SMS:
            requestSendSMS(data, datalen, t);
            break;
        case RIL_REQUEST_SETUP_DATA_CALL:
            requestSetupDataCall(data, datalen, t);
            break;
        case RIL_REQUEST_SMS_ACKNOWLEDGE:
            requestSMSAcknowledge(data, datalen, t);
            break;
        case RIL_REQUEST_GET_IMSI:
            p_response = NULL;
            err = at_send_command_numeric("AT+CIMI", &p_response);
            if (err < 0 || p_response->success == 0) {
                RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
            } else {
                RIL_onRequestComplete(t, RIL_E_SUCCESS,
                    p_response->p_intermediates->line, sizeof(char *));
            }
            at_response_free(p_response);
            break;
        case RIL_REQUEST_GET_IMEI:
            p_response = NULL;
            err = at_send_command_numeric("AT+CGSN", &p_response);

            if (err < 0 || p_response->success == 0) {
                RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
            } else {
                RIL_onRequestComplete(t, RIL_E_SUCCESS,
                    p_response->p_intermediates->line, sizeof(char *));
            }
            at_response_free(p_response);
            break;
        case RIL_REQUEST_SIM_IO:
            requestSIM_IO(data,datalen,t);
            break;
        case RIL_REQUEST_SEND_USSD:
            requestSendUSSD(data, datalen, t);
            break;
        case RIL_REQUEST_CANCEL_USSD:
            p_response = NULL;
            err = at_send_command_numeric("AT+CUSD=2", &p_response);
            if (err < 0 || p_response->success == 0) {
                RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
            } else {
                RIL_onRequestComplete(t, RIL_E_SUCCESS,
                    p_response->p_intermediates->line, sizeof(char *));
            }
            at_response_free(p_response);
            break;
        case RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC:
            at_send_command("AT+COPS=0", NULL);
            break;
        case RIL_REQUEST_DATA_CALL_LIST:
            requestDataCallList(data, datalen, t);
            break;
        case RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE:
            requestQueryNetworkSelectionMode(data, datalen, t);
            break;
        case RIL_REQUEST_OEM_HOOK_RAW:
            // echo back data
            RIL_onRequestComplete(t, RIL_E_SUCCESS, data, datalen);
            break;
        case RIL_REQUEST_OEM_HOOK_STRINGS: {
            int i;
            const char ** cur;
            LOGD("got OEM_HOOK_STRINGS: 0x%8p %lu", data, (long)datalen);
            for (i = (datalen / sizeof (char *)), cur = (const char **)data ;
                    i > 0 ; cur++, i --) {
                LOGD("> '%s'", *cur);
            }
            // echo back strings
            RIL_onRequestComplete(t, RIL_E_SUCCESS, data, datalen);
            break;
        }
        case RIL_REQUEST_WRITE_SMS_TO_SIM:
            requestWriteSmsToSim(data, datalen, t);
            break;
        case RIL_REQUEST_DELETE_SMS_ON_SIM: {
            char * cmd;
            p_response = NULL;
            asprintf(&cmd, "AT+CMGD=%d", ((int *)data)[0]);
            err = at_send_command(cmd, &p_response);
            free(cmd);
            if (err < 0 || p_response->success == 0) {
                RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
            } else {
                RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
            }
            at_response_free(p_response);
            break;
        }
        case RIL_REQUEST_ENTER_SIM_PIN:
        case RIL_REQUEST_ENTER_SIM_PUK:
        case RIL_REQUEST_ENTER_SIM_PIN2:
        case RIL_REQUEST_ENTER_SIM_PUK2:
        case RIL_REQUEST_CHANGE_SIM_PIN:
        case RIL_REQUEST_CHANGE_SIM_PIN2:
            requestEnterSimPin(data, datalen, t);
            break;
        case RIL_REQUEST_GSM_SMS_BROADCAST_ACTIVATION:
            requestSmsBroadcastActivation(0,data, datalen, t);
            break;
        case RIL_REQUEST_GSM_SET_BROADCAST_SMS_CONFIG:
             LOGD("onRequest RIL_REQUEST_GSM_SET_BROADCAST_SMS_CONFIG");
            requestSetSmsBroadcastConfig(0,data, datalen, t);
            break;
        case RIL_REQUEST_GSM_GET_BROADCAST_SMS_CONFIG:
            requestGetSmsBroadcastConfig(0,data, datalen, t);
            break;
        default:
            RIL_onRequestComplete(t, RIL_E_REQUEST_NOT_SUPPORTED, NULL, 0);
            break;
    }
}
           

對每一個RIL_REQUEST_XXX請求轉化成相應的ATcommand,發送給modem,然後睡眠等待,當收到ATcommand的最終響應後,線程被喚醒,将響應傳給用戶端程序。

2.currentState

static RIL_RadioState currentState()
{
    return sState;
}
           

3.onSupports

static int onSupports (int requestCode)
{
    //@@@ todo
    return 1;
}
           

4.onCancel

static void onCancel (RIL_Token t)
{
    //@@@todo
}
           

5.getVersion

static const char * getVersion(void)
{
    return "android reference-ril 1.0";
}
           

注冊RIL_Env接口

Android——RIL 機制源碼分析

由于各手機廠商的AT指令差異,是以與modem互動層需要各手機廠商實作,以動态庫的形式提供。作為介于modem與上層的中間層,即要與底層互動也要與上層通信,是以就需要定義一個接口來銜接RILD與動态庫,RIL_Env和RIL_RadioFunctions接口就是libril.so與librefrence.so通信的橋梁。是Rild架構中用于隔離通用代碼和廠商代碼的接口,RIL_Env由通用代碼實作,而RIL_RadioFunctions則是由廠商代碼實作。

RIL_Init的主要任務:

1. 向librefrence.so注冊libril.so提供的接口RIL_Env;

2. 建立一個mainLoop工作線程,用于初始化AT子產品,并監控AT子產品的狀态,一旦AT被關閉,則重新打開并初始化AT;

3. 當AT被打開後,mainLoop工作線程将向Rild送出一個定時事件,并觸發eventLoop來完成對modem的初始化;

4. 建立一個readLoop工作線程,用于從AT序列槽中讀取資料;

5.傳回librefrence.so提供的接口RIL_RadioFunctions;

hardware\ril\reference-ril\reference-ril.c

const RIL_RadioFunctions *RIL_Init(const struct RIL_Env *env, int argc, char **argv)
{
    int ret;
    int fd = -1;
    int opt;
    pthread_attr_t attr;
  s_rilenv = env; //将ril.cpp中定義的RIL_Env注冊到reference-ril.c中的s_rilenv
    while ( -1 != (opt = getopt(argc, argv, "p:d:s:"))) {
        switch (opt) {
            case 'p':
                s_port = atoi(optarg);
                if (s_port == 0) {
                    usage(argv[0]);
                    return NULL;
                }
                LOGI("Opening loopback port %d\n", s_port);
            break;
            case 'd':
                s_device_path = optarg;
                LOGI("Opening tty device %s\n", s_device_path);
            break;
            case 's':
                s_device_path   = optarg;
                s_device_socket = 1;
                LOGI("Opening socket %s\n", s_device_path);
            break;
            default:
                usage(argv[0]);
                return NULL;
        }
    }
    if (s_port < 0 && s_device_path == NULL) {
        usage(argv[0]);
        return NULL;
    }
    pthread_attr_init (&attr);
  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
  //建立一個mainLoop線程
  ret = pthread_create(&s_tid_mainloop, &attr, mainLoop, NULL);
  //将reference-ril.c中定義的RIL_RadioFunctions傳回并注冊到ril.cpp中的s_callbacks
    return &s_callbacks;
}
           

mainLoop工作線程是用來初始化并監控AT子產品的,一旦AT子產品被關閉,就自動打開。

static void * mainLoop(void *param)
{
    int fd;
    int ret;
  AT_DUMP("== ", "entering mainLoop()", -1 );
  //為AT子產品設定回調函數
    at_set_on_reader_closed(onATReaderClosed);
    at_set_on_timeout(onATTimeout);
    for (;;) {
        fd = -1;
        while  (fd < 0) { //獲得序列槽AT子產品的裝置檔案描述符
            if (s_port > 0) {
                fd = socket_loopback_client(s_port, SOCK_STREAM);
            } else if (s_device_socket) {
                if (!strcmp(s_device_path, "/dev/socket/qemud")) {
                    /* Qemu-specific control socket */
                    fd = socket_local_client( "qemud",
                 ANDROID_SOCKET_NAMESPACE_RESERVED,SOCK_STREAM );
                    if (fd >= 0 ) {
                        char  answer[2];
                        if ( write(fd, "gsm", 3) != 3 ||read(fd, answer, 2) != 2 ||
                             memcmp(answer, "OK", 2) != 0)
                        {
                            close(fd);
                            fd = -1;
                        }
                   }
                }
                else
                    fd = socket_local_client( s_device_path,    ANDROID_SOCKET_NAMESPACE_FILESYSTEM,SOCK_STREAM );
            } else if (s_device_path != NULL) {
                fd = open (s_device_path, O_RDWR);
                if ( fd >= 0 && !memcmp( s_device_path, "/dev/ttyS", 9 ) ) {
                    /* disable echo on serial ports */
                    struct termios  ios;
                    tcgetattr( fd, &ios );
                    ios.c_lflag = 0;  /* disable ECHO, ICANON, etc... */
                    tcsetattr( fd, TCSANOW, &ios );
                }
            }
            if (fd < 0) {
                perror ("opening AT interface. retrying...");
                sleep(10);
            }
        }
        s_closed = 0;
        //打開AT子產品,建立AT讀取線程s_tid_reader,fd為modem裝置檔案句柄
        ret = at_open(fd, onUnsolicited);
        if (ret < 0) {
            LOGE ("AT error %d on at_open\n", ret);
            return 0;
        }
        //向Rild送出逾時任務
        RIL_requestTimedCallback(initializeCallback, NULL, &TIMEVAL_0);
        sleep(1);
        //如果AT子產品被關閉,則waitForClose傳回,重新打開AT,如果AT已打開,則阻塞
        waitForClose();
        LOGI("Re-opening after close");
    }
}
           

1.打開AT子產品

通過at_open打開檔案描述符為fd的AT序列槽裝置,并注冊回調函數ATUnsolHandler

int at_open(int fd, ATUnsolHandler h)
{
    int ret;
    pthread_t tid;
    pthread_attr_t attr;
    s_fd = fd;
    s_unsolHandler = h;
    s_readerClosed = 0;
    s_responsePrefix = NULL;
    s_smsPDU = NULL;
    sp_response = NULL;
    /* Android power control ioctl */
#ifdef HAVE_ANDROID_OS
#ifdef OMAP_CSMI_POWER_CONTROL
    ret = ioctl(fd, OMAP_CSMI_TTY_ENABLE_ACK);
    if(ret == 0) {
        int ack_count;
		int read_count;
        int old_flags;
        char sync_buf[256];
        old_flags = fcntl(fd, F_GETFL, 0);
        fcntl(fd, F_SETFL, old_flags | O_NONBLOCK);
        do {
            ioctl(fd, OMAP_CSMI_TTY_READ_UNACKED, &ack_count);
			read_count = 0;
            do {
                ret = read(fd, sync_buf, sizeof(sync_buf));
				if(ret > 0)
					read_count += ret;
            } while(ret > 0 || (ret < 0 && errno == EINTR));
            ioctl(fd, OMAP_CSMI_TTY_ACK, &ack_count);
         } while(ack_count > 0 || read_count > 0);
        fcntl(fd, F_SETFL, old_flags);
        s_readCount = 0;
        s_ackPowerIoctl = 1;
    }
    else
        s_ackPowerIoctl = 0;
#else // OMAP_CSMI_POWER_CONTROL
        s_ackPowerIoctl = 0;
#endif // OMAP_CSMI_POWER_CONTROL
#endif /*HAVE_ANDROID_OS*/
    pthread_attr_init (&attr);
  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
  //建立readerLoop工作線程,該線程用于從序列槽讀取資料
    ret = pthread_create(&s_tid_reader, &attr, readerLoop, &attr);
    if (ret < 0) {
        perror ("pthread_create");
        return -1;
    }
    return 0;
}
           

2.添加定時事件RIL_requestTimedCallback

RIL_requestTimedCallback(initializeCallback, NULL, &TIMEVAL_0);

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

向定時事件隊列中添加一個定時事件,該事件的處理函數為initializeCallback,用于發送一些AT指令來初始化BP的modem。

3.readLoop工作線程

Read loop 解析從Modem 發過來的回應。如果遇到URC 則通過handleUnsolicited 上報的RIL_JAVA。如果是指令的應答,則通過handleFinalResponse 通知send_at_command 有應答結果。

Android——RIL 機制源碼分析

注冊RIL_RadioFunctions接口

hardware\ril\libril\ril.cpp

extern "C" void RIL_register (const RIL_RadioFunctions *callbacks) {
    int ret;
  int flags;
  //版本驗證
    if (callbacks == NULL || ((callbacks->version != RIL_VERSION)&& (callbacks->version < 2))) { 
        return;
    }
    if (callbacks->version < RIL_VERSION) {
        LOGE ("RIL_register: upgrade RIL to version %d current version=%d",
              RIL_VERSION, callbacks->version);
    }
    if (s_registerCalled > 0) {
        LOGE("RIL_register has been called more than once. "Subsequent call ignored");
        return;
  }
    //将reference-ril.c中定義的RIL_RadioFunctions注冊到ril.cpp中
    memcpy(&s_callbacks, callbacks, sizeof (RIL_RadioFunctions));
    s_registerCalled = 1;
    for (int i = 0; i < (int)NUM_ELEMS(s_commands); i++) {
        assert(i == s_commands[i].requestNumber); //序号驗證
    }
    for (int i = 0; i < (int)NUM_ELEMS(s_unsolResponses); i++) {
        assert(i + RIL_UNSOL_RESPONSE_BASE== s_unsolResponses[i].requestNumber);
    }
    // old standalone impl wants it here.
    if (s_started == 0) {
        RIL_startEventLoop();
    }
  // 得到名為rild的socket句柄
  s_fdListen = android_get_control_socket(SOCKET_NAME_RIL);
    if (s_fdListen < 0) {
        LOGE("Failed to get socket '" SOCKET_NAME_RIL "'");
        exit(-1);
  }
  // 監聽該socket
    ret = listen(s_fdListen, 4);
    if (ret < 0) {
        LOGE("Failed to listen on control socket '%d': %s",s_fdListen, strerror(errno));
        exit(-1);
    }
    /* 設定s_listen_event事件,一旦有用戶端連接配接,即s_fdListen可讀就會導緻eventLoop工作線程中的select傳回,因為該事件不是持久的,是以調用為listenCallback處理完後,将從watch_table移除該事件,是以Rild隻支援一個用戶端連接配接*/
  ril_event_set (&s_listen_event, s_fdListen, false,listenCallback, NULL);
    /* 添加s_listen_event事件,并觸發eventLoop工作線程 */
    rilEventAddWakeup (&s_listen_event);
#if 1
    // 得到調試socket的句柄rild-debug
    s_fdDebug = android_get_control_socket(SOCKET_NAME_RIL_DEBUG);
    if (s_fdDebug < 0) {
        LOGE("Failed to get socket '" SOCKET_NAME_RIL_DEBUG "' errno:%d", errno);
        exit(-1);
  }
  //監聽該socket
    ret = listen(s_fdDebug, 4);
    if (ret < 0) {
        LOGE("Failed to listen on ril debug socket '%d': %s",s_fdDebug, strerror(errno));
        exit(-1);
  }
    /* 設定s_debug_event事件 */
    ril_event_set (&s_debug_event, s_fdDebug, true,debugCallback, NULL);
    /* 添加s_debug_event事件,并觸發eventLoop工作線程  */
    rilEventAddWakeup (&s_debug_event);
#endif
}
           

打開監聽端口,接收來自用戶端程序的指令請求,當與客戶程序連接配接建立時調用listenCallback函數,建立單獨線程監視并處理所有事件源。

1.用戶端連接配接處理

s_listen_event事件用于處理上層用戶端的socket連接配接,當得到socket連接配接請求時,eventLoop工作線程裡的select傳回并自動調用listenCallback回調函數進行處理:

tatic void listenCallback (int fd, short flags, void *param) {
    int ret;
    int err;
    int is_phone_socket;
    RecordStream *p_rs;
    commthread_data_t *user_data = NULL;
    user_data =(commthread_data_t *)malloc(sizeof(commthread_data_t));
    struct sockaddr_un peeraddr;
    socklen_t socklen = sizeof (peeraddr);
    struct ucred creds;
    socklen_t szCreds = sizeof(creds);
    struct passwd *pwd = NULL;
    assert (s_fdCommand < 0);
  assert (fd == s_fdListen);
  //接收一個用戶端的連接配接,并将該socket連接配接儲存在變量s_fdCommand中
    s_fdCommand = accept(s_fdListen, (sockaddr *) &peeraddr, &socklen);
    if (s_fdCommand < 0 ) {
        LOGE("Error on accept() errno:%d", errno);
        /* start listening for new connections again */
        rilEventAddWakeup(&s_listen_event);
	      return;
    }
    /* 對用戶端權限判斷,判斷是否是程序組ID為radio的程序發起的連接配接*/
    errno = 0;
    is_phone_socket = 0;
    err = getsockopt(s_fdCommand, SOL_SOCKET, SO_PEERCRED, &creds, &szCreds);
    if (err == 0 && szCreds > 0) {
        errno = 0;
        pwd = getpwuid(creds.uid);
        if (pwd != NULL) {
            if (strcmp(pwd->pw_name, PHONE_PROCESS) == 0) {
                is_phone_socket = 1;
            } else {
                LOGE("RILD can't accept socket from process %s", pwd->pw_name);
            }
        } else {
            LOGE("Error on getpwuid() errno: %d", errno);
        }
    } else {
        LOGD("Error on getsockopt() errno: %d", errno);
    }
    
    if ( !is_phone_socket ) {
      LOGE("RILD must accept socket from %s", PHONE_PROCESS);
      close(s_fdCommand);
      s_fdCommand = -1;
      onCommandsSocketClosed();
      /* start listening for new connections again */
      rilEventAddWakeup(&s_listen_event);
      return;
    }
#if 0
    if(s_dualSimMode) {
        if(s_sim_num == 0) {
            property_get(SIM_POWER_PROPERTY, prop, "0");
            if(!strcmp(prop, "0")) {
                property_set(SIM_POWER_PROPERTY, "1");
                s_callbacks.powerSIM(NULL);
            }
        } else if(s_sim_num == 1) {
            property_get(SIM_POWER_PROPERTY1, prop, "0");
            if(!strcmp(prop, "0")) {
                property_set(SIM_POWER_PROPERTY1, "1");
                s_callbacks.powerSIM(NULL);
            }
        }
    } else {
        property_get(SIM_POWER_PROPERTY, prop, "0");
        if(!strcmp(prop, "0")) {
            property_set(SIM_POWER_PROPERTY, "1");
            s_callbacks.powerSIM(NULL);
        }
    }
#endif
    //p_rs為RecordStream類型,它内部會配置設定一個緩沖區來存儲用戶端發送過來的資料
  p_rs = record_stream_new(s_fdCommand, MAX_COMMAND_BYTES);
  //添加一個針對接收到的用戶端連接配接的處理事件,進而在eventLoop工作線程中處理該用戶端的各種請求
    ril_event_set (&s_commands_event, s_fdCommand, 1,processCommandsCallback, p_rs);
    rilEventAddWakeup (&s_commands_event);
    onNewCommandConnect();
}
           

2.用戶端通信處理

在listenCallback中首先接收用戶端的連接配接請求,并驗證用戶端的權限,同時将該用戶端以事件的形式添加到eventLoop工作線程中進行監控,當該用戶端有資料請求時,eventLoop工作線程從select中傳回,并自動調用processCommandsCallback回調函數:

static void processCommandsCallback(int fd, short flags, void *param) {
    RecordStream *p_rs;
    void *p_record;
    size_t recordlen;
    int ret;
    assert(fd == s_fdCommand);
    p_rs = (RecordStream *)param;
  for (;;) { //循環處理用戶端發送過來的AT指令
      //讀取一條AT指令
        ret = record_stream_get_next(p_rs, &p_record, &recordlen);
        if (ret == 0 && p_record == NULL) {
            break;
        } else if (ret < 0) {
            break;
        } else if (ret == 0) { /* && p_record != NULL */
            //處理用戶端發送過來的AT指令
            processCommandBuffer(p_record, recordlen);
        }
    }
    if (ret == 0 || !(errno == EAGAIN || errno == EINTR)) {
        if (ret != 0) {
            LOGE("error on reading command socket errno:%d\n", errno);
        } else {
            LOGW("EOS.  Closing command socket.");
        }
        close(s_fdCommand);
        s_fdCommand = -1;
        ril_event_del(&s_commands_event);
        record_stream_free(p_rs);
        rilEventAddWakeup(&s_listen_event);
        onCommandsSocketClosed();
    }
}
           

通過processCommandBuffer函數來處理每一條AT指令:

static int processCommandBuffer(void *buffer, size_t buflen) {
    Parcel p;
    status_t status;
    int32_t request;
    int32_t token;
    RequestInfo *pRI;
    int ret;
    p.setData((uint8_t *) buffer, buflen);
    // status checked at end
    status = p.readInt32(&request);
    status = p.readInt32 (&token);
    if (status != NO_ERROR) {
        LOGE("invalid request block");
        return 0;
    }
    if (request < 1 || request >= (int32_t)NUM_ELEMS(s_commands)) {
        LOGE("unsupported request code %d token %d", request, token);
        return 0;
    }
    pRI = (RequestInfo *)calloc(1, sizeof(RequestInfo));
    pRI->token = token; //AT指令标号
    pRI->pCI = &(s_commands[request]); //根據request找到s_commands指令數組中的指定AT指令
    ret = pthread_mutex_lock(&s_pendingRequestsMutex);
    assert (ret == 0);
    pRI->p_next = s_pendingRequests;
    s_pendingRequests = pRI;
    ret = pthread_mutex_unlock(&s_pendingRequestsMutex);
  assert (ret == 0);
  //調用指定AT指令的dispatch函數,根據接收來自客戶程序的指令和參數,調用onRequest進行處理。
    pRI->pCI->dispatchFunction(p, pRI);
    return 0;
}
           

打電話的AT指令:{RIL_REQUEST_DIAL, dispatchDial, responseVoid},

發短信的AT指令:{RIL_REQUEST_SEND_SMS, dispatchStrings, responseSMS},

3.電話撥打流程

Android——RIL 機制源碼分析
static void dispatchDial (Parcel &p, RequestInfo *pRI) {
  RIL_Dial dial; //RIL_Dial存儲了打電話的所有資訊
    RIL_UUS_Info uusInfo; 
    int32_t sizeOfDial;
    int32_t t;
    .................. //初始化dial變量  
  s_callbacks.onRequest(pRI->pCI->requestNumber, &dial, sizeOfDial, pRI);
  .................
    return;
}
           

s_callbacks.onRequest其實就是調用RIL_RadioFunctions中的onRequest函數,該函數在前面已介紹過了。

static void onRequest (int request, void *data, size_t datalen, RIL_Token t)
{
    switch (request) {
        case RIL_REQUEST_DIAL:
            requestDial(data, datalen, t);
            break;
    }
}
           
static void requestDial(void *data, size_t datalen, RIL_Token t)
{
    RIL_Dial *p_dial;
    char *cmd;
    const char *clir;
    int ret;
    p_dial = (RIL_Dial *)data;
    switch (p_dial->clir) {
        case 1: clir = "I"; break;  /*invocation*/
        case 2: clir = "i"; break;  /*suppression*/
        default:
        case 0: clir = ""; break;   /*subscription default*/
  }
  //向序列槽發送AT指令
    ret = at_send_command(cmd, NULL);
  free(cmd);
  //通知請求結果
    RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
}
           

向AT發送完撥号指令後,通過RIL_onRequestComplete傳回處理結果,RIL_onRequestComplete實際上是RIL_Env中的OnRequestComplete函數,在前面我們也介紹過了

extern "C" void RIL_onRequestComplete(RIL_Token t, RIL_Errno e, void *response, size_t responselen) {
    RequestInfo *pRI;
    int ret;
    size_t errorOffset;
  pRI = (RequestInfo *)t;
  //該請求已經處理,需要從請求隊列中移除該請求
    if (!checkAndDequeueRequestInfo(pRI)) {
        LOGE ("RIL_onRequestComplete: invalid RIL_Token");
        return;
    }
    if (pRI->local > 0) {
        ...........
        sendResponse(p);
    }
done:
    free(pRI);
}
           
static int sendResponse (Parcel &p) {
    return sendResponseRaw(p.data(), p.dataSize()); //将結果發送給JAVA RIL用戶端
}
           
static int sendResponseRaw (const void *data, size_t dataSize) {
    int fd = s_fdCommand;
    int ret;
    uint32_t header;
    if (s_fdCommand < 0) {
        return -1;
    }
    if (dataSize > MAX_COMMAND_BYTES) {
        return -1;
    }
    pthread_mutex_lock(&s_writeMutex);
    header = htonl(dataSize);
    ret = blockingWrite(fd, (void *)&header, sizeof(header));
    if (ret < 0) {
        pthread_mutex_unlock(&s_writeMutex);
        return ret;
    }
    ret = blockingWrite(fd, data, dataSize);
    if (ret < 0) {
        pthread_mutex_unlock(&s_writeMutex);
        return ret;
    }
    pthread_mutex_unlock(&s_writeMutex);
    return 0;
}
           

撥打電話的時序圖如下:

Android——RIL 機制源碼分析

Rild通過onRequest向動态庫送出一個請求,然後傳回,動态庫處理完請求後,處理結果通過回調接口通知用戶端

繼續閱讀