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