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.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,无名管道,用于队列的主动唤醒。
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请求了。
Response即modem通过串口回应信息到java层,在AT通讯的过程中有两类Response:一种是请求后给出应答,另一种是通知类,即为不请自来的,例如短信通知达到,我们称该类通知为URC。在Rild中URC和一般的Response是分开处理的,概念上URC由handleUnsolicited处理,而Response由handleFinalResponse来处理。
在前面,我们讲解初始化流程的时候,有介绍到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层,前面介绍过,这里就不重复了。