一、基于Rild的通信架构
一般智能手机的硬件架构都是两个处理器:
一个处理器用来运行操作系统,上面运行应用程序,这个处理器称作Application Processor,简称AP;另一个处理负责和射频无线通信相关的工作,叫Baseband Processor,简称BP。
在Android系统中,Rild运行在AP上,它是AP和BP在软件层上通信的中枢。
目前通过Rild,AP和BP的通信方式可以分为两种:
第一种是AP发送请求给BP,BP响应并回复AP。此时,BP通过Rild回复的请求被称为solicited Response。
第二种是BP主动发送信息给AP。在这种情况下,BP通过Rild发送的请求被称为unsolicited Response。
基于Rild进程的整个通信架构,基本上如上图所示。
从图中我们可以看出:
1、Android框架部分,是通过Phone进程与Rild进程通信的。它们之间的通信方式采用的是socket。
在前面介绍PhoneApp启动时,我们知道Phone进程中有两个Phone对象。每个Phone对象持有一个socket,与对应的Rild进程通信。因此,我们知道手机中实际上启动了两个Rild进程(双卡手机)。
shell:/ $ ps | grep rild
radio 572 1 113732 14792 hrtimer_na 0000000000 S /system/bin/rild
radio 869 1 109604 13944 hrtimer_na 0000000000 S /system/bin/rild
shell:/ $ ps | grep phone
radio 2621 605 2019984 74424 SyS_epoll_ 0000000000 S com.android.phone
shell:/ $ ps | grep init
root 1 0 9648 1712 SyS_epoll_ 0000000000 S /init
shell:/ $ ps | grep zygote
root 605 1 2195280 70956 poll_sched 0000000000 S zygote64
root 606 1 1610708 59144 poll_sched 0000000000 S zygote
我们通过usb连接手机后,通过adb shell进入终端,通过ps和grep命令,可以得到上述结果。
明显可以看到一个Phone进程对应者两个Rild进程;同时Rild进程由init进程加载,Phone进程由zygote进程加载。
2、Rild与BP之间并没有直接通信,而是引入了厂商的动态库。
这种设计应该是为了保证灵活性吧。
用面向对象的思想来看,我们可以认为Rild是一个接口,定义了AP、BP双向通信时需要使用的最基本的函数。不同的厂商都需要满足这个接口,以提供手机最基本的通信功能。
至于具体如何实现,是完全独立和自由的。
二、Rild的启动
在hardware/ril/rild/rild.rc中定义了Rild启动时对应的选项:
service ril-daemon /system/bin/rild
class main
socket rild stream 660 root radio
socket sap_uim_socket1 stream 660 bluetooth bluetooth
socket rild-debug stream 660 radio system
user root
group radio cache inet misc audio log readproc wakelock
在Android 7.0之前的版本中,该文件的内容是被定义在init.rc中的。
到了Android7.0 之后,init.rc文件中的许多内容均被移出,添加到各个进程中。如前面分析Vold进程时,对应的启动文件定义于vold.rc中。
个人猜测这些文件应该会在编译时,重新集成起来,毕竟在在rild对应的Android.mk中增加了下述字段:
.......
LOCAL_MODULE:= rild
LOCAL_MODULE_TAGS := optional
//新增字段
LOCAL_INIT_RC := rild.rc
.......
目前手边没有Android7.0的机器,还不好验证,以后有机会再做尝试。
init进程根据rild.rc文件启动一个Rild进程,还需要根据厂商定义的rc文件启动另一个Rild进程。
厂商定义的rc文件中,与Rild进程相关的主要内容与rild.rc相似,就是socket名称不同。对于第二个Rild进程,其socket名应该为rild2。
现在我们看看Rild进程的main函数,定义于rild.c中:
int main(int argc, char **argv) {
//rilLibPath用于指定动态库的位置
const char * rilLibPath = NULL;
........
//Rild规定动态库必须实现一个叫做Ril_init的函数,这个函数的第一个参数指向结构体RIL_Env
//而它的返回值指向结构体RIL_RadioFunctions
const RIL_RadioFunctions *(*rilInit)(const struct RIL_Env *, int, char **);
........
const RIL_RadioFunctions *funcs;
char libPath[PROPERTY_VALUE_MAX];
//解析参数
........
if (strncmp(clientId, "0", MAX_CLIENT_ID_LENGTH)) {
strlcat(rild, clientId, MAX_SOCKET_NAME_LENGTH);
//注意此处调用了ril.cpp中的函数,保存了Rild进程对应socket的名字,后文还会提到
RIL_setRilSocketName(rild);
}
if (rilLibPath == NULL) {
//读取系统属性,LIB_PATH_PROPERTY的值为rild.libpath
//原生的属性值定义于build/target/board/generic/system.prop文件中
//实际的手机中将会使用厂商指定的system.prop文件
if ( 0 == property_get(LIB_PATH_PROPERTY, libPath, NULL)) {
// No lib sepcified on the command line, and nothing set in props.
// Assume "no-ril" case.
goto done;
} else {
rilLibPath = libPath;
}
}
..........
//根据动态库位置,利用dlopen打开动态库
dlHandle = dlopen(rilLibPath, RTLD_NOW);
..........
//1、启动EventLoop,事件处理
RIL_startEventLoop()
//从动态库中的到RIL_Init函数的地址
rilInit =
(const RIL_RadioFunctions *(*)(const struct RIL_Env *, int, char **))
dlsym(dlHandle, "RIL_Init");
......
//2、调用RIL_init函数
funcs = rilInit(&s_rilEnv, argc, rilArgv);
RLOGD("RIL_Init rilInit completed");
//3、注册funcs到Rild中
RIL_register(funcs);
........
done:
RLOGD("RIL_Init starting sleep loop");
while (true) {
sleep(UINT32_MAX);
}
}
根据Rild的main函数,我们可以看出主要就进行了三件事:启动Event Loop、调用RIL_Init函数和注册库函数。
接下来我们分别分析一下主要事件对应的流程。
1、 RIL_startEventLoop
RIL_startEventLoop定义于hardware/ril/libril/ril.cpp中:
extern "C" void
RIL_startEventLoop(void) {
/* spin up eventLoop thread and wait for it to get started */
s_started = 0;
.........
//创建工作线程,线程ID存入s_tid_dispatch,对应执行函数为eventLoop
int result = pthread_create(&s_tid_dispatch, &attr, eventLoop, NULL);
.........
//工作线程eventLoop运行后,会设置s_started为1,并触发s_startupCond
//这里的等待的目的是保证RIL_startEventLoop返回前,工作线程创建并运行成功
while (s_started == 0) {
pthread_cond_wait(&s_startupCond, &s_startupMutex);
}
.........
}
我们需要跟进eventLoop函数:
static void *
eventLoop(void *param) {
int ret;
int filedes[2];
//1、初始化内部数据结构
ril_event_init();
pthread_mutex_lock(&s_startupMutex);
//通知RIL_startEventLoop本线程已经创建并成功运行了
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);
//2、创建一个ril_event
ril_event_set (&s_wakeupfd_event, s_fdWakeupRead, true,
processWakeupCallback, NULL);
//3、将创建出的ril_event加入到event队列中
rilEventAddWakeup (&s_wakeupfd_event);
//4、进入事件等待循环中
ril_event_loop();
.........
}
1.1 初始化内部数据结构
我们先看看ril_event_init函数:
void ril_event_init()
{
MUTEX_INIT();
FD_ZERO(&readFds);
//初始化timer_list,任务插入时按时间排序
init_list(&timer_list);
//初始化pending_list,保存每次需要执行的任务
init_list(&pending_list);
//初始化监控表
memset(watch_table, 0, sizeof(watch_table));
}
static void init_list(struct ril_event * list)
{
memset(list, 0, sizeof(struct ril_event));
list->next = list;
list->prev = list;
list->fd = -1;
}
//MAX_FD_EVENTS为8
//watchtable将用于保存FD加入到readFDs中的ril_event
static struct ril_event * watch_table[MAX_FD_EVENTS];
可以看出ril_event_init就是初始化readFds、timer_list、pending_list和watch_table,其中后三种数据结构均是用来存放ril_event的。
根据前文的代码,我们知道Rild的main函数中,通过调用RIL_startEventLoop单独启动了一个线程运行eventLoop,这是一个工作线程。
这个工作线程就是靠ril_event结构体来描述自己需要执行的任务,并且它将多个任务按时间顺序组织起来,保存在任务队列中。
ril_event的数据结构如下:
struct ril_event {
struct ril_event *next;
struct ril_event *prev;
int fd;
int index;
bool persist;
struct timeval timeout;
ril_event_cb func;
void *param;
};
如果从设计模式的角度来理解Rild的工作线程,易于看出,这其实是比较典型的命令模式。
就如同之前博客分析vold进程一样,CommandListener收到数据后,调用对应Command的runCommand方法进行处理。
此处,工作线程收到ril_event后,加入队列中,当需要处理时,调用ril_event对应的处理函数func。
1.2 创建wakeupfd ril_event
工作线程完成数据结构的初始化后,创建了第一个ril_event:
........
ril_event_set (&s_wakeupfd_event, s_fdWakeupRead, true,
processWakeupCallback, NULL);
........
// Initialize an event
void ril_event_set(struct ril_event * ev, int fd, bool persist, ril_event_cb func, void * param)
{
dlog("~~~~ ril_event_set %x ~~~~", (unsigned int)ev);
memset(ev, 0, sizeof(struct ril_event));
ev->fd = fd;
ev->index = -1;
ev->persist = persist;
ev->func = func;
ev->param = param;
fcntl(fd, F_SETFL, O_NONBLOCK);
}
从上面的代码可以看出,创建的第一个ril_event的fd为管道的读端、回调函数为processWakeupCallback,同时persist属性为true。
1.3 将创建出的ril_event加入到event队列中
static void rilEventAddWakeup(struct ril_event *ev) {
ril_event_add(ev);
triggerEvLoop();
}
// Add event to watch list
void ril_event_add(struct ril_event * ev)
{
dlog("~~~~ +ril_event_add ~~~~");
MUTEX_ACQUIRE();
for (int i