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层,前面介绍过,这里就不重复了。