Android Ril库总结

RIL 模块解析
本文深入剖析了Android系统的RIL模块,包括其总体框架、初始化流程、request和response流程等核心内容。揭示了RIL如何通过socket通信连接Java层与底层硬件,以及AT命令的发送与回应机制。

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

[plain]  view plain copy
  1. service ril-daemon /system/bin/rild -l libreference-ril.so -- -d /dev/ttyUSB2  
  2.     class main  
  3.     socket rild stream 660 root radio  
  4.     socket rild-debug stream 660 radio system  
  5.     user root  
  6.     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

[cpp]  view plain copy
  1. int main(int argc, char **argv)  
  2. {  
  3.     const char * rilLibPath = NULL;  
  4.     void *dlHandle;  
  5.     const RIL_RadioFunctions *(*rilInit)(const struct RIL_Env *, intchar **);  
  6.     const RIL_RadioFunctions *funcs;  
  7.     unsigned char hasLibArgs = 0;  
  8.     dlHandle = dlopen(rilLibPath, RTLD_NOW);//获取动态链接库libreferencer-ril.so  
  9.     //入口地址  
  10.     ......  
  11.     RIL_startEventLoop();//初始化EventLoop  
  12.     .......  
  13.     rilInit = (const RIL_RadioFunctions *(*)(const struct RIL_Env *, intchar **))dlsym(dlHandle, "RIL_Init");//获取libreferencer-ril.so中RIL_Init方法的指针  
  14.     .......  
  15.     funcs = rilInit(&s_rilEnv, argc, rilArgv);//向libreference-ril.so注册  
  16.     RIL_register(funcs);//向libril注册libreference的回调函数,并监听socket端口  
  17.     ......  
  18. }  

通过解析参数获取动态链接库的名字保存在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

[cpp]  view plain copy
  1. struct ril_event {  
  2.     struct ril_event *next;  
  3.     struct ril_event *prev;  
  4. int fd; //事件相关设备句柄,例如socket端口、管道  
  5. int index;  
  6.     bool persist;//如果是保持的,则不从watch_list中删除。  
  7.     struct timeval timeout;  
  8.     ril_event_cb func; //回调事件处理函数  
  9.     void *param; //回调时参数  
  10. };  

为了统一管理事件,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

[cpp]  view plain copy
  1. RIL_startEventLoop(void) {  
  2.     ......  
  3.     pthread_mutex_lock(&s_startupMutex);  
  4.     pthread_attr_init (&attr);  
  5.     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);  
  6.     ret = pthread_create(&s_tid_dispatch, &attr, eventLoop, NULL);  
  7.     ......  
  8. }  

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

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

[cpp]  view plain copy
  1. static void *  
  2. eventLoop(void *param) {  
  3.     int ret;  
  4.     int filedes[2];  
  5.     ril_event_init(); //初始化三个队列  
  6.     pthread_mutex_lock(&s_startupMutex);  
  7.     s_started = 1;  
  8.     pthread_cond_broadcast(&s_startupCond);  
  9.     pthread_mutex_unlock(&s_startupMutex);  
  10.     ret = pipe(filedes); //创建管道通信  
  11.     ......  
  12.     s_fdWakeupRead = filedes[0];  
  13.     s_fdWakeupWrite = filedes[1];  
  14.     fcntl(s_fdWakeupRead, F_SETFL, O_NONBLOCK);//设置非阻塞模式  
  15.     ril_event_set (&s_wakeupfd_event, s_fdWakeupRead, true,  
  16.                 processWakeupCallback, NULL);//设置ril_event  
  17.     rilEventAddWakeup (&s_wakeupfd_event);//添加到watch_table,并主动唤醒队列  
  18.     ril_event_loop();//开始循环扫面readFDS端口  
  19. }  

   (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   

[cpp]  view plain copy
  1. static void rilEventAddWakeup(struct ril_event *ev) {  
  2.     ril_event_add(ev);  
  3.     triggerEvLoop();//主动唤醒队列  
  4. }  

    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()

[cpp]  view plain copy
  1. void ril_event_loop(){  
  2.     int n;  
  3.     fd_set rfds;  
  4.     struct timeval tv;  
  5.     struct timeval * ptv;  
  6.     for (;;) {  
  7.         // make local copy of read fd_set  
  8.         memcpy(&rfds, &readFds, sizeof(fd_set));  
  9.         if (-1 == calcNextTimeout(&tv)) {  
  10.             // no pending timers; block indefinitely  
  11.             dlog("~~~~ no timers; blocking indefinitely ~~~~");  
  12.             ptv = NULL;  
  13.         } else {  
  14.             dlog("~~~~ blocking for %ds + %dus ~~~~", (int)tv.tv_sec, (int)tv.tv_usec);  
  15.             ptv = &tv;  
  16.         }  
  17.         printReadies(&rfds);  
  18.         n = select(nfds, &rfds, NULL, NULL, ptv);  
  19.         printReadies(&rfds);  
  20.         ......  
  21.         processTimeouts();  
  22.         processReadReadies(&rfds, n);  
  23.         firePending();  
  24.     }  
  25. }  
    我们知道对于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

[cpp]  view plain copy
  1. const RIL_RadioFunctions *RIL_Init(const struct RIL_Env *env, int argc, char **argv)  
  2. {  
  3. ......  
  4. s_rilenv = env;  
  5.     while ( -1 != (opt = getopt(argc, argv, "p:d:s:"))) {  
  6.         switch (opt) {  
  7.             case 'd':  
  8.                 s_device_path = optarg; //解析参数,保存modem端口  
  9.                 ALOGI("Opening tty device %s\n", s_device_path);  
  10.             break;  
  11.         }  
  12.     }  
  13.     pthread_attr_init (&attr);  
  14.     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);  
  15.     ret = pthread_create(&s_tid_mainloop, &attr, mainLoop, NULL);//创建线程  
  16.     return &s_callbacks;  
  17. }  

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

[cpp]  view plain copy
  1. static struct RIL_Env s_rilEnv = {  
  2.     RIL_onRequestComplete,   //request完成的时候调用  
  3.     RIL_onUnsolicitedResponse, //URC消息回调  
  4.     RIL_requestTimedCallback  //request超时回调,后面会添加timer_list事件  
  5. };  

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

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

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

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

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

[cpp]  view plain copy
  1. static const RIL_RadioFunctions s_callbacks = {  
  2.     RIL_VERSION,  
  3.     onRequest, //java层发送AT命令时回调,处理requst请求  
  4.     currentState,  
  5.     onSupports,  
  6.     onCancel,  
  7.     getVersion  
  8. };  

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

[cpp]  view plain copy
  1. static void *  
  2. mainLoop(void *param)  
  3. {  
  4.     int fd;  
  5.     int ret;  
  6.     AT_DUMP("== ""entering mainLoop()", -1 );  
  7.     at_set_on_reader_closed(onATReaderClosed);  
  8.     at_set_on_timeout(onATTimeout);  
  9.     for (;;) {  
  10.         fd = -1;  
  11.         while  (fd < 0) {  
  12.             if (s_port > 0) {  
  13.                 fd = socket_loopback_client(s_port, SOCK_STREAM);  
  14.             } else if (s_device_socket) {  
  15.              ......    
  16.             } else if (s_device_path != NULL) {  
  17.                 fd = open (s_device_path, O_RDWR);//打开modem端口  
  18.                 if ( fd >= 0 && !memcmp( s_device_path, "/dev/ttyS", 9 ) ) {  
  19.              ......  
  20.             }  
  21.         }  
  22.         s_closed = 0;  
  23.         ret = at_open(fd, onUnsolicited);//建立起modem端口上的reader等待循环  
  24.         RIL_requestTimedCallback(initializeCallback, NULL, &TIMEVAL_0);  
  25.     }  
  26. }  

  (1) 打开modem端口

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

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

3.hardware/ril/reference-ril/atchannel.c---at_open

[cpp]  view plain copy
  1. int at_open(int fd, ATUnsolHandler h)  
  2. {  
  3.     .......  
  4.     pthread_attr_init (&attr);  
  5.     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);  
  6.     ret = pthread_create(&s_tid_reader, &attr, readerLoop, &attr);  
  7.     ......  
  8.     return 0;  
  9. }  

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

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

[cpp]  view plain copy
  1. static void *readerLoop(void *arg)  
  2. {  
  3.     for (;;) {  
  4.         const char * line;  
  5.         line = readline(); //读取modem端口数据  
  6.         if(isSMSUnsolicited(line)) {//判断相应类型  
  7.             char *line1;  
  8.             const char *line2;  
  9.             line1 = strdup(line);  
  10.             line2 = readline();  
  11.             if (s_unsolHandler != NULL) {  
  12.                 s_unsolHandler (line1, line2);//URC消息处理  
  13.             }  
  14.             free(line1);  
  15.         } else {  
  16.             processLine(line);//回应java层的AT请求  
  17.         }  
  18.     onReaderClosed();  
  19.     return NULL;  
  20. }  

      在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端口。

[cpp]  view plain copy
  1. extern "C" void  
  2. RIL_register (const RIL_RadioFunctions *callbacks) {  
  3.     int ret;  
  4.     int flags;  
  5. ......  
  6. memcpy(&s_callbacks, callbacks, sizeof (RIL_RadioFunctions));  
  7. //打开与java层通信的socket端口/dev/socket/rild  
  8.     s_fdListen = android_get_control_socket(SOCKET_NAME_RIL);  
  9.     if (s_fdListen < 0) {  
  10.         ALOGE("Failed to get socket '" SOCKET_NAME_RIL "'");  
  11.         exit(-1);  
  12.     }  
  13.     ret = listen(s_fdListen, 4);  
  14.     /* note: non-persistent so we can accept only one connection at a time */  
  15.     ril_event_set (&s_listen_event, s_fdListen, false,  
  16.                 listenCallback, NULL);  
  17.     rilEventAddWakeup (&s_listen_event);  
  18. .......  
  19. //注册debug端口  
  20.  ril_event_set (&s_debug_event, s_fdDebug, true,  
  21.                 debugCallback, NULL);  
  22.     rilEventAddWakeup (&s_debug_event);  
  23. }  

函数指针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

[cpp]  view plain copy
  1. static void listenCallback (int fd, short flags, void *param) {  
  2. ......  
  3. //接受连接请求  
  4.     s_fdCommand = accept(s_fdListen, (sockaddr *) &peeraddr, &socklen);  
  5.     ......  
  6.     ret = fcntl(s_fdCommand, F_SETFL, O_NONBLOCK);  
  7.     ......  
  8.     p_rs = record_stream_new(s_fdCommand, MAX_COMMAND_BYTES);  
  9.     ril_event_set (&s_commands_event, s_fdCommand, 1,  
  10.         processCommandsCallback, p_rs);  
  11.     rilEventAddWakeup (&s_commands_event);  
  12.     onNewCommandConnect();  
  13. }  

  (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

[cpp]  view plain copy
  1. static void processCommandsCallback(int fd, short flags, void *param) {  
  2.     RecordStream *p_rs;  
  3.     void *p_record;  
  4.     size_t recordlen;  
  5.     int ret;  
  6.     assert(fd == s_fdCommand);  
  7.     p_rs = (RecordStream *)param;  
  8.     for (;;) {  
  9.         ret = record_stream_get_next(p_rs, &p_record, &recordlen);  
  10.         if (ret == 0 && p_record == NULL) {  
  11.             break;  
  12.         } else if (ret < 0) {  
  13.             break;  
  14.         } else if (ret == 0) { /* && p_record != NULL */  
  15.             processCommandBuffer(p_record, recordlen);  
  16.         }  
  17.     }  
  18.     //省略.....  
  19.     }  
  20. }  

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

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

[cpp]  view plain copy
  1. static int  
  2. processCommandBuffer(void *buffer, size_t buflen) {  
  3. //省略......  
  4. p.setData((uint8_t *) buffer, buflen);  
  5. status = p.readInt32(&request);  
  6. status = p.readInt32 (&token);  
  7.     pRI = (RequestInfo *)calloc(1, sizeof(RequestInfo));  
  8.     pRI->token = token;  
  9.     pRI->pCI = &(s_commands[request]);  
  10.     ret = pthread_mutex_lock(&s_pendingRequestsMutex);  
  11.     pRI->p_next = s_pendingRequests;  
  12.     s_pendingRequests = pRI;  
  13.     ret = pthread_mutex_unlock(&s_pendingRequestsMutex);  
  14.     pRI->pCI->dispatchFunction(p, pRI);  
  15.     return 0;  
  16. }  

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

[cpp]  view plain copy
  1. typedef struct RequestInfo {  
  2.     int32_t token;      //this is not RIL_Token  
  3.     CommandInfo *pCI;  
  4.     struct RequestInfo *p_next;  
  5.     char cancelled;  
  6.     char local;         // responses to local commands do not go back to command process  
  7. } RequestInfo;  

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

[cpp]  view plain copy
  1. static CommandInfo s_commands[] = {  
  2. #include "ril_commands.h"  
  3. };  

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

[cpp]  view plain copy
  1. typedef struct {  
  2.     int requestNumber;  
  3.     void (*dispatchFunction) (Parcel &p, struct RequestInfo *pRI);  
  4.     int(*responseFunction) (Parcel &p, void *response, size_t responselen);  
  5. } CommandInfo;  

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

[cpp]  view plain copy
  1.    {0, NULL, NULL},                   //none  
  2.     {RIL_REQUEST_GET_SIM_STATUS, dispatchVoid, responseSimStatus},  
  3.     {RIL_REQUEST_ENTER_SIM_PIN, dispatchStrings, responseInts},  
  4. {RIL_REQUEST_ENTER_SIM_PUK, dispatchStrings, responseInts},  
  5. //省略......  

    第一个参数,就是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

[cpp]  view plain copy
  1. static void  
  2. onRequest (int request, void *data, size_t datalen, RIL_Token t)  
  3. {  
  4.    //省略......  
  5.     switch (request) {  
  6.          //省略......  
  7.         case RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND:  
  8. //阻塞,等待超时或接收到Modem的相应  
  9. at_send_command("AT+CHLD=0", NULL);  
  10.             /* success or failure is ignored by the upper layer here. 
  11.                it will call GET_CURRENT_CALLS and determine success that way */  
  12.             RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);  
  13.             break;  
  14.         //省略......  
  15.         default:  
  16.             RIL_onRequestComplete(t, RIL_E_REQUEST_NOT_SUPPORTED, NULL, 0);  
  17.             break;  
  18.     }  
  19. }  

     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

[cpp]  view plain copy
  1. static int at_send_command_full_nolock (const char *command, ATCommandType type,  
  2.                     const char *responsePrefix, const char *smspdu,  
  3.                     long long timeoutMsec, ATResponse **pp_outResponse)  
  4. {  
  5. //省略…..  
  6. //向设备发送命令  
  7.     err = writeline (command);  
  8.     s_type = type;  
  9.     s_responsePrefix = responsePrefix;  
  10.     s_smsPDU = smspdu;  
  11.     sp_response = at_response_new();  
  12. #ifndef USE_NP  
  13.     if (timeoutMsec != 0) {  
  14.         setTimespecRelative(&ts, timeoutMsec);  
  15.     }  
  16. #endif /*USE_NP*/  
  17.     //处理等待情况  
  18.     while (sp_response->finalResponse == NULL && s_readerClosed == 0) {  
  19.         if (timeoutMsec != 0) {  
  20. #ifdef USE_NP  
  21.             err = pthread_cond_timeout_np(&s_commandcond, &s_commandmutex, timeoutMsec);  
  22. #else  
  23.             err = pthread_cond_timedwait(&s_commandcond, &s_commandmutex, &ts);  
  24. #endif /*USE_NP*/  
  25.         } else {  
  26.             err = pthread_cond_wait(&s_commandcond, &s_commandmutex);  
  27.         }  
  28.   //省略…….  
  29. }  
在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

[cpp]  view plain copy
  1. static void *readerLoop(void *arg)  
  2. {  
  3.     for (;;) {  
  4.         const char * line;  
  5.         line = readline(); //读取modem端口数据  
  6.         if(isSMSUnsolicited(line)) {//判断相应类型  
  7.             char *line1;  
  8.             const char *line2;  
  9.             line1 = strdup(line);  
  10.             line2 = readline();  
  11.             if (s_unsolHandler != NULL) {  
  12.                 s_unsolHandler (line1, line2);//URC消息处理  
  13.             }  
  14.             free(line1);  
  15.         } else {  
  16.             processLine(line);//回应java层的AT请求  
  17.         }  
  18.     onReaderClosed();  
  19.     return NULL;  
  20. }  
首先通过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

[cpp]  view plain copy
  1. static void onUnsolicited (const char *s, const char *sms_pdu)  
  2. {  
  3.     char *line = NULL;  
  4.     int err;  
  5.       //省略….  
  6.     if (strStartsWith(s, "%CTZV:")) {  
  7.         //省略….  
  8.     } else if (strStartsWith(s,"+CREG:")|| strStartsWith(s,"+CGREG:"){  
  9.       //省略….  
  10.     } else if (strStartsWith(s,"+CREG:") || strStartsWith(s,"+CGREG:")) {  
  11.         RIL_onUnsolicitedResponse (  
  12.             RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED,  
  13.             NULL, 0);  
  14. #ifdef WORKAROUND_FAKE_CGEV  
  15.         RIL_requestTimedCallback (onDataCallListChanged, NULL, NULL);  
  16. #endif /* WORKAROUND_FAKE_CGEV */  
  17. }  
  18.  //省略….  
  19. }  
跟request类似,response也有一个response号,onUnsolicited做一个简单的处理,通常通过misc.c的strStartsWith方法,只解析头部,然后根据类型的不一样得到不一样的response号,进行下一步处理,都调用RIL_onUnsolicitedResponse进行处理,其第一个参数就是response号。

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

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

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

[cpp]  view plain copy
  1. static int sendResponseRaw (const void *data, size_t dataSize) {  
  2.     int fd = s_fdCommand;  
  3.     //省略...  
  4.     ret = blockingWrite(fd, data, dataSize);  
  5.     //省略...  
  6. }  
     对于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

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

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

[cpp]  view plain copy
  1. extern "C" void  
  2. RIL_onRequestComplete(RIL_Token t, RIL_Errno e, void *response, size_t responselen) {  
  3.     //省略....  
  4.     pRI = (RequestInfo *)t;  
  5.     if (pRI->cancelled == 0) {  
  6.         Parcel p;  
  7.         p.writeInt32 (RESPONSE_SOLICITED);  
  8.         p.writeInt32 (pRI->token);  
  9.         errorOffset = p.dataPosition();  
  10.            //省略....  
  11.         p.writeInt32 (e);  
  12.           //省略....  
  13.         if (response != NULL) {  
  14.             // there is a response payload, no matter success or not.  
  15.             ret = pRI->pCI->responseFunction(p, response, responselen);  
  16.         }  
  17.         sendResponse(p);  
  18.     }  
  19. }  
  20.    
   对于第一个参数t,其实是在request的时候创建的RequestInfo结构体,有其对应的responseFunction接口,调用后将回应信息封装在Parcel中,后续调用sendResponse将数据发送到JAVA层,前面介绍过,这里就不重复了。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值