76

Android7.0 Rild工作流程 - Android移动开发技术文章_手机开发 -

 5 years ago
source link: http://www.10tiao.com/html/257/201806/2247485423/1.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

一、基于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搭建的通信架构后,分析底层拨号的流程还是比较简单的。

转载声明:本文系转载,原文地址:https://www.2cto.com/kf/201609/549285.html


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK