Android Ril库总结

          

1总体框架

    Rild是Init进程启动的一个本地服务,这个本地服务并没有使用Binder之类的通讯手段,而是采用了socket通讯这种方式。

Andoid将RIL层分为两个代码空间:RILD管理框架(rild、libril.so),AT相关的xxxril.so动态链接库(libreference-ril.so)。rild把libril.so和libreference-ril.so联系起来,libril.so对上是java的socket通信,对下是把java层的命名分发到libreference-ril.so,而libreference-ril.so则把命名转换层AT的命令,通过串口发送给Modem。将RIL独立成一个动态链接库的好处就是Android系统适应不同的Modem,不同的Mode可以有一个独立的Ril与之对应。

     而ril是具体的AT指令合成者和应答解析者。从最基本的功能来讲,ril建立了一个侦听Socket,等待客户端的连接,然后从该连接上读取RIL-Java成传递来的命令并转化成AT指令发送到Modem。并等待Modem的回应,然后将结果通过套接口传回到Ril-Java层。下图是Ril-D的基本框架:


下面的数据流传递描述图表描述了RIL-JAVA层发出一个电话指令的5 步曲:


① JAVA层通过socket发送命令到RILD

② RILD在EventLoop线程监听到socket消息,读取后封装成AT指令,通过串口发送给

     Modem。并等待Modem的回应命令

③ ReaderLoop线程不断读取串口端口数据,得到回应。回应信息分为两种,一种是对

于第二步AT指令的回应,另一种是主动上报信息,即URC消息,例如短信送达的

消息。

④ 判断是回应AT命令的回应信息,将消息传送到ril再次处理

⑤ 通过socket,将AT回应消息发送到JAVA

⑥ URC消息通过socket,通知到JAVA。

Ril-d的整体数据流及其控制流示意图:

2初始化流程分析

2.1 初始化流程图

   

2.2 代码分析

Ø        init.XXX.rc                    

Ø        hardware/ril/rild/rild.c            

Ø        hardware/ril/libril/ril.cpp          

Ø        hardware/ril/libril/ril_event.cpp

Ø        hardware/ril/reference-ril/reference-ril.c

Ø        hardware/ril/reference-ril/atchannel.c   

2.2.1 入口 

---1. init.XXX.rc  ---

service ril-daemon /system/bin/rild -l libreference-ril.so -- -d /dev/ttyUSB2
    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

在Android配置脚本init.XXX.rc里面,如上所示,定义用于启动RILD的服务ril-daemon,我们可以看到, /system/bin/rild就是该服务的执行文件,即hardware/ril/rild/rild.c文件编译出来的可执行文件。参数如下:

Ø        -l:指定AT相关的动态链接库为libreference-ril.so

Ø        -d:指定modem的端口为/dev/tty/USB2

在ril-daemon服务中,还创建了两个socket端口,分别是rild、rild-debug,我们可以在

小机/dev/socket/目录下看到其节点。rild端口用于Ril库与java代码的socket通信,而rild-debug用于radiooptions(hardware/ril/rild/radiooptions.c)模拟java发送socket命令,起到调试的作用。


2. hardware/ril/rild/rild.c---main

int main(int argc, char **argv)
{
    const char * rilLibPath = NULL;
    void *dlHandle;
    const RIL_RadioFunctions *(*rilInit)(const struct RIL_Env *, int, char **);
    const RIL_RadioFunctions *funcs;
    unsigned char hasLibArgs = 0;
    dlHandle = dlopen(rilLibPath, RTLD_NOW);//获取动态链接库libreferencer-ril.so
    //入口地址
    ......
    RIL_startEventLoop();//初始化EventLoop
    .......
    rilInit = (const RIL_RadioFunctions *(*)(const struct RIL_Env *, int, char **))dlsym(dlHandle, "RIL_Init");//获取libreferencer-ril.so中RIL_Init方法的指针
    .......
    funcs = rilInit(&s_rilEnv, argc, rilArgv);//向libreference-ril.so注册
    RIL_register(funcs);//向libril注册libreference的回调函数,并监听socket端口
    ......
}

通过解析参数获取动态链接库的名字保存在rilLibPath中,调用dlopen()获取动态链接库的入口地址,然后再通过调用dlsym()获取libreference-ril中的RIL_Init()方法的指针。

调用RIL_startEventLoop()初始化EventLoop(),创建线程监听端口(管道、socket)事件。

调用libreference-ril的RIL_Init()方法,所带的参数是s_rilEnv结构体指针及-d指定的Modem端口/dev/ttyUSB2,并返回回调函数给Rild。

Ril_register()将回调函数注册到libril中。

Main()函数解析参数后,依次去做一些初始化工作,详细解析如下分析。

2.2.2  eventLoop

    Event Loop的基本工作就是等待在事件端口(管道,Socket),一旦有数据到达就根据登记的Event回调函数进行处理。

1.ril_event对象 hardware/ril/libril/ril_event.h

struct ril_event {
    struct ril_event *next;
    struct ril_event *prev;
int fd; //事件相关设备句柄,例如socket端口、管道
int index;
    bool persist;//如果是保持的,则不从watch_list中删除。
    struct timeval timeout;
    ril_event_cb func; //回调事件处理函数
    void *param; //回调时参数
};

为了统一管理事件,Android使用了三个队列:watch_table,timer_list,pending_list,并使用了一个设备句柄池readFDS,是Linux的fd_set,readFDS保存了Rild中所有的设备文件句柄,以便利用select函数统一的完成事件的侦听。

Ø        watch_table:监测时间队列。需要检测的事件都放入到该队列中。

Ø        timer_list:timer队列

Ø        pending_list:待处理事件队列,事件已经触发,需要所回调处理的事件。

    事件队列队列的操作:ril_event_add,ril_event_del, ril_timer_add

2. hardware/ril/libril/ril.cpp---RIL_startEventLoop

RIL_startEventLoop(void) {
    ......
    pthread_mutex_lock(&s_startupMutex);
    pthread_attr_init (&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    ret = pthread_create(&s_tid_dispatch, &attr, eventLoop, NULL);
    ......
}

这个方法很简单,创建新线程,执行eventLoop()方法。

3. hardware/ril/libril/ril.cpp---eventLoop

static void *
eventLoop(void *param) {
    int ret;
    int filedes[2];
    ril_event_init(); //初始化三个队列
    pthread_mutex_lock(&s_startupMutex);
    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);//设置非阻塞模式
    ril_event_set (&s_wakeupfd_event, s_fdWakeupRead, true,
                processWakeupCallback, NULL);//设置ril_event
    rilEventAddWakeup (&s_wakeupfd_event);//添加到watch_table,并主动唤醒队列
    ril_event_loop();//开始循环扫面readFDS端口
}

   (1)ril_event_init

      初始化三个队列timer_list、pending_list、watch_table及readFDS

   (2)pipe

      创建无名管道,用于队列主动唤醒,一个为读端另一个是写端。

   (3)O_NONBLOCK

      设置了读端s_fdWakeupRead非阻塞模式,即如果没有数据的时候,立即返回。

   (4)ril_event_set

      将s_fdWakeupRead作为句柄,初始化s_wakeupfd_event结构体,即ril_event。

   (5)rilEventAddWakeup   

static void rilEventAddWakeup(struct ril_event *ev) {
    ril_event_add(ev);
    triggerEvLoop();//主动唤醒队列
}

    ril_event_add()将s_wakeupfd_event添加到watch_table队列中,并把s_fdWakeupRead

       句柄添加到readFDS句柄池中,然后理解调用triggerEvLoop(),往s_fdWakeupWrite

       写一个空格字符,主动唤醒队列。

   (6)ril_event_loop

       开始扫面线程池readFDS。

   到此,我们监听的端口只有s_fdWakeupRead。

4.hardware/ril/libril/ril_event.cpp---ril_event_loop()

void ril_event_loop(){
    int n;
    fd_set rfds;
    struct timeval tv;
    struct timeval * ptv;
    for (;;) {
        // make local copy of read fd_set
        memcpy(&rfds, &readFds, sizeof(fd_set));
        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;
        }
        printReadies(&rfds);
        n = select(nfds, &rfds, NULL, NULL, ptv);
        printReadies(&rfds);
        ......
        processTimeouts();
        processReadReadies(&rfds, n);
        firePending();
    }
}
    我们知道对于Linux设备来讲,我们可以使用select函数等待在FDS上,只要FDS中记录的设备有数据到来,select就会设置相应的标志位并返回。readFDS记录了所有的事件相关设备句柄。readFDS中句柄是在在AddEvent加入的。所有的事件侦听都是建立在linux的select readFDS基础上。

ril_event_loop 利用select等待在readFDS(fd_set)上,当select设备有数据时,ril_event_loop会从select返回,processTimeouts()检查timer_list是否有Timeout事件(request回调),有则添加到pengding_list列表中。processReadReadies()检查watch_list中是否有Event可读,有则相应的Event放置到pend_list,如果Event是持久性的则不从watch_list中删除。然后firePending遍历pengding_list处理Event事件,发起事件回调函数。

2.2.3  readerLoop

RIL_Init开始,开启对modem端口硬件的事件监听,在创建线程中readerLoop循环读取modem端口(/dev/ttyUSBX)AT应答执行。

1.hardware/ril/reference-ril/reference-ril.c---RIL_Init

const RIL_RadioFunctions *RIL_Init(const struct RIL_Env *env, int argc, char **argv)
{
......
s_rilenv = env;
    while ( -1 != (opt = getopt(argc, argv, "p:d:s:"))) {
        switch (opt) {
            case 'd':
                s_device_path = optarg; //解析参数,保存modem端口
                ALOGI("Opening tty device %s\n", s_device_path);
            break;
        }
    }
    pthread_attr_init (&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    ret = pthread_create(&s_tid_mainloop, &attr, mainLoop, NULL);//创建线程
    return &s_callbacks;
}

  (1) 将env赋值给 s_rilenv,env是在rild.c定义的,通过RIL_Init传进来的,定义如下:

static struct RIL_Env s_rilEnv = {
    RIL_onRequestComplete,   //request完成的时候调用
    RIL_onUnsolicitedResponse, //URC消息回调
    RIL_requestTimedCallback  //request超时回调,后面会添加timer_list事件
};

 这三个方法,是在ril.cpp中定义的。

(2)-d参数指定了modem端口,用变量s_device_path保存,然后调用pthread_create创建

      新线程,在新线程中调用mainLoop()方法。

(3)直接将s_callbacks返回给Rild。

   hardware/ril/reference-ril/reference-ril.c

static const RIL_RadioFunctions s_callbacks = {
    RIL_VERSION,
    onRequest, //java层发送AT命令时回调,处理requst请求
    currentState,
    onSupports,
    onCancel,
    getVersion
};

2.hardware/ril/reference-ril/reference-ril.c---mainLoop

static void *
mainLoop(void *param)
{
    int fd;
    int ret;
    AT_DUMP("== ", "entering mainLoop()", -1 );
    at_set_on_reader_closed(onATReaderClosed);
    at_set_on_timeout(onATTimeout);
    for (;;) {
        fd = -1;
        while  (fd < 0) {
            if (s_port > 0) {
                fd = socket_loopback_client(s_port, SOCK_STREAM);
            } else if (s_device_socket) {
             ......  
            } else if (s_device_path != NULL) {
                fd = open (s_device_path, O_RDWR);//打开modem端口
                if ( fd >= 0 && !memcmp( s_device_path, "/dev/ttyS", 9 ) ) {
             ......
            }
        }
        s_closed = 0;
        ret = at_open(fd, onUnsolicited);//建立起modem端口上的reader等待循环
        RIL_requestTimedCallback(initializeCallback, NULL, &TIMEVAL_0);
    }
}

  (1) 打开modem端口

  (2)通过at_open建立起modem设备文件上的reader等待循环

  (3)Ril的相应机制,回调initializeCallback方法,执行了一些模块的初始化命令。

3.hardware/ril/reference-ril/atchannel.c---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);
    ......
    return 0;
}

该方法也通过新建一个线程完成,即pthread_create创建,线程的入口是readerLoop方法。实现如下。

4. hardware/ril/reference-ril/atchannel.c---readerLoop

static void *readerLoop(void *arg)
{
    for (;;) {
        const char * line;
        line = readline(); //读取modem端口数据
        if(isSMSUnsolicited(line)) {//判断相应类型
            char *line1;
            const char *line2;
            line1 = strdup(line);
            line2 = readline();
            if (s_unsolHandler != NULL) {
                s_unsolHandler (line1, line2);//URC消息处理
            }
            free(line1);
        } else {
            processLine(line);//回应java层的AT请求
        }
    onReaderClosed();
    return NULL;
}

      在AT通讯的过程中有两类响应:一种是请求后给出应答,一种是通知类,即为不请自来的,例如短信通知达到,我们称该类通知为URC。在Rild中URC和一般的Response是分开处理的,概念上URC由handleUnsolicited处理,而Response由handleFinalResponse来处理。

 (1) readline不断读取串口数据

(2)isSMSUnsolicited判断相应类型

(3)U_unsolHandler处理URC消息

(4)processLine处理java层的的response信息

 对于readerLoop,到这里就告一段落,后续我们会分析request和response的。

2.2.4  监听socket端口

      有上面介绍中,RIL_Init函数获取一组函数指针RIL_RadioFunctions,通过RIL_register将回调函数注册到libril.so中,并打开接受来自java层命令的socket端口。

extern "C" void
RIL_register (const RIL_RadioFunctions *callbacks) {
    int ret;
    int flags;
......
memcpy(&s_callbacks, callbacks, sizeof (RIL_RadioFunctions));
//打开与java层通信的socket端口/dev/socket/rild
    s_fdListen = android_get_control_socket(SOCKET_NAME_RIL);
    if (s_fdListen < 0) {
        ALOGE("Failed to get socket '" SOCKET_NAME_RIL "'");
        exit(-1);
    }
    ret = listen(s_fdListen, 4);
    /* note: non-persistent so we can accept only one connection at a time */
    ril_event_set (&s_listen_event, s_fdListen, false,
                listenCallback, NULL);
    rilEventAddWakeup (&s_listen_event);
.......
//注册debug端口
 ril_event_set (&s_debug_event, s_fdDebug, true,
                debugCallback, NULL);
    rilEventAddWakeup (&s_debug_event);
}

函数指针callbacks中的函数,是在reference-ril.c中定义的,这样,socket端口数AT请求的时候,就可以将at命令传送到reference-ril.c中处理了。将callbacks赋值给s_callbacks。

调用接口android_get_control_socket()获取/dev/socket/rild的文件描述符,并设置为s_listen_event事件,再调用rilEventAddWakeup添加到watch_table列表中,并主动唤醒队列。

到这里就完成了RILD守护进程的初始化操作,最后main函数主线程就将进入死循环中。回顾下初始化过程中,挂载到watch_table队列中的event事件有如下3个:

Ø        s_listen_event ,名为rild的socket,它主要是request和response的通道

Ø        s_debug_event,名为rild-debug的socket,主要作用是调试用的request与response通道。与s_listen_event类似。

Ø        s_wakeupfd_evnet,无名管道,用于队列的主动唤醒。

3 request流程分析

    Request即请求流程,java层通过socket将命令发送到RIL层的RILD守护进程,这时RILD负责监听的ril_event_loop消息循环的select发现了java层的socket请求连接信号,s_listen_event就会被挂到pending_list队列中,并执行了event->func回调函数,即listenCallback。下面开始分析。

3.1 request调用关系图


3.2 代码分析

1. hardware/ril/libril/ril.c---listenCallback

static void listenCallback (int fd, short flags, void *param) {
......
//接受连接请求
    s_fdCommand = accept(s_fdListen, (sockaddr *) &peeraddr, &socklen);
    ......
    ret = fcntl(s_fdCommand, F_SETFL, O_NONBLOCK);
    ......
    p_rs = record_stream_new(s_fdCommand, MAX_COMMAND_BYTES);
    ril_event_set (&s_commands_event, s_fdCommand, 1,
        processCommandsCallback, p_rs);
    rilEventAddWakeup (&s_commands_event);
    onNewCommandConnect();
}

  (1) java层要与RILD通信,首先调用socket.connect()发起连接请求。而这里,通过accept   

    函数接受连接请求,返回传入的socket描述符给s_fdCommand

 (2) 通过record_stream_new建立起一个record_stream,连接于上层的数据通道,并开始接

     受数据请求,将record_stream与s_fdCommand绑定。

 (3)将s_fdCommand设置到s_commands_event事件中,并添加到watch_table队列中。

 这样,一旦s_fdCommand有数据,那么就回调processCommandsCallback方法。

2.hardware/ril/libril/ril.c---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 (;;) {
        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 */
            processCommandBuffer(p_record, recordlen);
        }
    }
    //省略.....
    }
}

通过record_stream_get_next()阻塞读取s_fdCommand上发来的数据,直到一个完整的request请求,在通过processCommandBuffer处理。

3. hardware/ril/libril/ril.c---processCommandBuffer

static int
processCommandBuffer(void *buffer, size_t buflen) {
//省略......
p.setData((uint8_t *) buffer, buflen);
status = p.readInt32(&request);
status = p.readInt32 (&token);
    pRI = (RequestInfo *)calloc(1, sizeof(RequestInfo));
    pRI->token = token;
    pRI->pCI = &(s_commands[request]);
    ret = pthread_mutex_lock(&s_pendingRequestsMutex);
    pRI->p_next = s_pendingRequests;
    s_pendingRequests = pRI;
    ret = pthread_mutex_unlock(&s_pendingRequestsMutex);
    pRI->pCI->dispatchFunction(p, pRI);
    return 0;
}

这个方法很关键,它完成了消息的派发。Java层传递的命令格式为:Parcel+Request号+令牌+内容组成,通过socket传送到RIL层,由于parcel是一套简单是序列化协议,因此需要通过反序列化的方法将其提取出来,以RequestInfo结构体形式存在,定义如下:

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

在这里,我们看到了request号,它是在java命令中指定的,由它我们就可以找到ril_commands.h中定义的方法了。RIL层命令的分发也是基于request号的。我们看看静态变量s_commands的定义:

static CommandInfo s_commands[] = {
#include "ril_commands.h"
};

 CommandInfo 表示一个结构体,关联了request号和实际的请求函数,以及响应函数之间的关系,该结构体的定义如下:

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

    我们再看看ril_commands.h文件的定义,其对数组变量s_commands进行了初始化:

   {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},
//省略......

    第一个参数,就是request号,第二个参数是对应的dispatchXXX,第三个参数,对应的是该请求的回应函数responseXXX,dispatch方法和response方法都是在ril.cpp中定义的。

回到processCommandBuffer中,根据request号,指定s_commands数组中的CommandInfo实例,并赋值给pRI->pCI。接着就调用pRI->pCI->dispatchFunction(),调用到了具体的dispatchXXX方法。

在所有的dispatchXXX方法中,最终都会调用到s_callbacks.onRequest()。s_callbacks应该不会陌生吧,是获取reference-ril.c中的RIL_RadioFunctions结构体指针。

到这里,消息就传到了reference-ril.c中的onRequest中了。

4.hardware/ril/reference-ril/reference-ril.c----onRequest

static void
onRequest (int request, void *data, size_t datalen, RIL_Token t)
{
   //省略......
    switch (request) {
         //省略......
        case RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND:
//阻塞,等待超时或接收到Modem的相应
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;
        //省略......
        default:
            RIL_onRequestComplete(t, RIL_E_REQUEST_NOT_SUPPORTED, NULL, 0);
            break;
    }
}

     onRequest也很简单,会根据不同的request号做不同的处理,并命令和参数转换成对应的AT命令。然后调用at_send_command()方法进行传送,最终会调用到at_send_command_full_nolock,并最终的写操作是通过writeline()方法中完成的,实际上是通过linux系统调用write接口向modem设备节点文件描述符完成写操作。

在at_send_command_full_nolock中,在writeline之后,发送命令的线程进入响应等待的状态,直到超时或者接收到响应才返回。返回之后,则调用RIL_onRequestComplete,即respose请求,这里就线不讲了,后续在respone流程中再分析。

5.hardware/ril/reference-ril/atchannel.c---at_send_command_full_nolock

static int at_send_command_full_nolock (const char *command, ATCommandType type,
                    const char *responsePrefix, const char *smspdu,
                    long long timeoutMsec, ATResponse **pp_outResponse)
{
//省略…..
//向设备发送命令
    err = writeline (command);
    s_type = type;
    s_responsePrefix = responsePrefix;
    s_smsPDU = smspdu;
    sp_response = at_response_new();
#ifndef USE_NP
    if (timeoutMsec != 0) {
        setTimespecRelative(&ts, timeoutMsec);
    }
#endif /*USE_NP*/
    //处理等待情况
    while (sp_response->finalResponse == NULL && s_readerClosed == 0) {
        if (timeoutMsec != 0) {
#ifdef USE_NP
            err = pthread_cond_timeout_np(&s_commandcond, &s_commandmutex, timeoutMsec);
#else
            err = pthread_cond_timedwait(&s_commandcond, &s_commandmutex, &ts);
#endif /*USE_NP*/
        } else {
            err = pthread_cond_wait(&s_commandcond, &s_commandmutex);
        }
  //省略…….
}
在writeline之后则进入线程等待处理中,关键在于信号量s_commandcond,当readerLoop读取到回应信息的时候,会做一个判断该信息的类型,如果有request在等待回应,再调用pthread_cond_signal(&s_commandcond)发送信号给request等待线程,即pthread_cond_wait返回,结束等待。

Ok,到这里,request流程就分析结束了,下面就开始分析response请求了。

4response流程分析

    Response即modem通过串口回应信息到java层,在AT通讯的过程中有两类Response:一种是请求后给出应答,另一种是通知类,即为不请自来的,例如短信通知达到,我们称该类通知为URC。在Rild中URC和一般的Response是分开处理的,概念上URC由handleUnsolicited处理,而Response由handleFinalResponse来处理。

4.1response调用关系图

4.2代码分析

在前面,我们讲解初始化流程的时候,有介绍到readerLoop,循环读取modem端口的数据,readerLopp的处理流程如下:

1.hardware/ril/reference-ril/atchannel.c---readerLoop

static void *readerLoop(void *arg)
{
    for (;;) {
        const char * line;
        line = readline(); //读取modem端口数据
        if(isSMSUnsolicited(line)) {//判断相应类型
            char *line1;
            const char *line2;
            line1 = strdup(line);
            line2 = readline();
            if (s_unsolHandler != NULL) {
                s_unsolHandler (line1, line2);//URC消息处理
            }
            free(line1);
        } else {
            processLine(line);//回应java层的AT请求
        }
    onReaderClosed();
    return NULL;
}
首先通过readline读取端口数据接收响应,然后isSMSUnsolicited()判断响应信息的类型,如果是URC信息,那么调用s_unsolHandler处理,如果是响应java层的request请求,那么调用processLine处理。这里我们先分析URC消息,processLine会稍后分析。

4.2.1 URC消息

s_unsolHandler是一个函数指针,是在初始化的时候,reference-ril.c通过调用at_open传给atchannel.c的,其实现的代码如下:

1. hardware/ril/reference-ril/ reference-ril.c---onUnsolicited

static void onUnsolicited (const char *s, const char *sms_pdu)
{
    char *line = NULL;
    int err;
      //省略….
    if (strStartsWith(s, "%CTZV:")) {
        //省略….
    } else if (strStartsWith(s,"+CREG:")|| strStartsWith(s,"+CGREG:"){
      //省略….
    } else if (strStartsWith(s,"+CREG:") || strStartsWith(s,"+CGREG:")) {
        RIL_onUnsolicitedResponse (
            RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED,
            NULL, 0);
#ifdef WORKAROUND_FAKE_CGEV
        RIL_requestTimedCallback (onDataCallListChanged, NULL, NULL);
#endif /* WORKAROUND_FAKE_CGEV */
}
 //省略….
}
跟request类似,response也有一个response号,onUnsolicited做一个简单的处理,通常通过misc.c的strStartsWith方法,只解析头部,然后根据类型的不一样得到不一样的response号,进行下一步处理,都调用RIL_onUnsolicitedResponse进行处理,其第一个参数就是response号。

2. hardware/ril/libril/ril.c ---RIL_onUnsolicitedResponse

extern "C"
void RIL_onUnsolicitedResponse(int unsolResponse, void *data,
                                size_t datalen)
{
    //省略......
    unsolResponseIndex = unsolResponse - RIL_UNSOL_RESPONSE_BASE;
    //省略......
    Parcel p;
    p.writeInt32 (RESPONSE_UNSOLICITED);
    p.writeInt32 (unsolResponse);
    //省略......
    ret = s_unsolResponses[unsolResponseIndex]
                .responseFunction(p, data, datalen);
    ret = sendResponse(p);
    //省略......
}
 
   s_unsolResponses变量是结构体UnsolResponseInfo的数组,根据传入的response号,减去基数RIL_UNSOL_RESPONSE_BASE,得到s_unsolResponses数组的索引,然后调用其responseFunction方法。这个和request的派发类似,这里就不详细的介绍了。
typedef struct {
    int requestNumber;
    int (*responseFunction) (Parcel &p, void *response, size_t responselen);
    WakeType wakeType;
} UnsolResponseInfo;
    经过responseFunction的处理,将response信息封装到parcel中,然后调用sendResponse()继续回应,然后直接调用sendResponseRaw(),代码如下。

3 hardware/ril/libril/ril.c ---sendResponseRaw

static int sendResponseRaw (const void *data, size_t dataSize) {
    int fd = s_fdCommand;
    //省略...
    ret = blockingWrite(fd, data, dataSize);
    //省略...
}
     对于s_fdCommand应该是不陌生了,在request流程分析中的listenCallback中讲到的,是RIL与JAVA层Socket的数据通道对应的设备节点。在blockingWrite方法中,通过write系统调用,将Parcel数据、大小作为参数,往s_fdCommand写数据,完成向JAVA层回应消息。

    到此,对于URC消息的分析就到此结束。

4.2.2 AT 回应消息

回到readerLoop()方法中,分析回应AT命令的消息处理processLine,在processLine会做一些判断处理,但最终都调用到handleFinalResponse中,这是关键。

1. hardware/ril/libril/ril.c---handleFinalResponse

static void handleFinalResponse(const char *line)
{
    sp_response->finalResponse = strdup(line);
    pthread_cond_signal(&s_commandcond);//发送结束等待信号量
}
  前面在request的时候讲解到,把AT命令写到Modem后,写线程进入了阻塞的等待状态,即等待Modem的回应信息,而这里就发送了s_commandcond的结束等待的信号量。则at_send_command_full_nolock方法立即结束等待返回到onRequest中,然后调用RIL_onRequestComplete方法。

2. hardware/ril/libril/ril.c  ---RIL_onRequestComplete

extern "C" void
RIL_onRequestComplete(RIL_Token t, RIL_Errno e, void *response, size_t responselen) {
    //省略....
    pRI = (RequestInfo *)t;
    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);
        }
        sendResponse(p);
    }
}
 
   对于第一个参数t,其实是在request的时候创建的RequestInfo结构体,有其对应的responseFunction接口,调用后将回应信息封装在Parcel中,后续调用sendResponse将数据发送到JAVA层,前面介绍过,这里就不重复了。

 

你可能感兴趣的:(android)