在Android中,电话系统由三个部分组成,Phone Framework, RIL-Daemon, Vendor-specified RIL。RIL-Daemon的作用是将Android Phone Framework和Vendor-RIL结合起来。这样做的好处是提供了一个松散耦合结构,不同厂家都可以根据自己的实际需求设计Vendor-RIL实现功能. Android Phone Framework 和 Vendor-RIL之间是通过RIL-Daemon进行交互的。Phone Framework和RIL-Daemon之间通过socket进行进程间通讯,RIL-Daemon解析从socket传来的RIL Request,然后调用Vendor-RIL中相对应的函数进行实际的操作
RIL-Daemon 分析
分析从main函数进行,main函数主要工作流程包含一下几点:
从上面这个流程来看,RIL-Daemon将用于通知Framework的回调函数注册到了Vendor RIL中,将Vendor RIL中的命令处理函数注册到了RIL-Daemon中。这样以来,Vendor-RIL和Framework就可以通过RILD进行交互了。回调函数接口定义以及意义可以参考.
Event相关分析
在RIL-Daemon的初始化中看来,Deamon启动了eventloop,从命名可以断定RIL-Daemon是事件驱动的。Eventloop有着密切关系的是如下三个结构:
timer_list |
记录了和延时相关的event,event按照延时时间,从小到大排序 |
watch_table | 记录了当前RIL-Daemon中所关注的event |
pending_list | 录了所有触发了的event,包括timer event和event |
在Eventloop中的event可以分为三种:
1、Time-Event: 超时事件
2、Persist FD-Event:永久FD事件,一直监视FD上的变化
3、Non-persist FD-Eent:非永久FD时间,仅监视一次FD变化
注:FD-Event有最大数量限制!由MAX_FD_EVENTS宏决定,定义在ril_event.h中。
Eventloop分析:
RIL-Daemon在main()中会通过RIL_startEventLoop()启动Eventloop.启动的过程主要包含如下几方面。
现在具体来看ril_event_loop的工作方式,ril_event_loop是继续select多路复用模型的。也就是说当select函数返回时,要么是对应fd上发生了变化,要么就是超时。在ril_event_loop中,每一次都会通过调用calcNextTimeout()来计算select的等待超时。如果timer_list上没有等待的event,则select采取阻塞等待。否则,select的超时时限有timer_event决定。在select返回后,processTimeout()和processReadReadies()两个函数会将满足触发条件的event添加到penging_list中,然后通过调用firePending在执行相对应的处理回调函数。
1 static void processTimeouts() 2 { 3 MUTEX_ACQUIRE(); 4 /*...*/ 5 while ((tev != &timer_list) && (timercmp(&now, &tev->timeout, >))) { 6 next = tev->next; 7 removeFromList(tev); 8 addToList(tev, &pending_list); 9 tev = next; 10 } 11 MUTEX_RELEASE(); 12 }
上面的代码可以看出,所有timeout的event都从timer_list上移除并加到了pending_list上。
static void processReadReadies(fd_set * rfds, int n) { MUTEX_ACQUIRE(); for (int i = 0; (i < MAX_FD_EVENTS) && (n > 0); i++) { struct ril_event * rev = watch_table[i]; if (rev != NULL && FD_ISSET(rev->fd, rfds)) { addToList(rev, &pending_list); if (rev->persist == false) removeWatch(rev, i); n--; } } MUTEX_RELEASE(); }
从上面的代码可以看出,所有fd有变化的event也都被加到了pending_list上。但与 processTimeouts不同是,并不是所有满足触发条件的FD-Event都会从watchtable移除,只有哪些非persist的会被移除。
static void firePending() { struct ril_event * ev = pending_list.next; while (ev != &pending_list) { struct ril_event * next = ev->next; removeFromList(ev); ev->func(ev->fd, 0, ev->param); ev = next; } }
一个特殊的event——s_wakeupfd_event:
在eventloop初始化的时候会创建一个pipe,并在一个endpoint上设置一个persist的wakeupfd_event。这个event的作用只有一个:通知eventloop有新事件到达。具体的来说,针对Timer-Event,event会迫使当前select返回并在下一次循环更新select的超时条件,否则在select阻塞的情况下timer-event不能及时响应。对于FD-Event则是将需要检测的event加入到watch_table,迫使select返回并在下一个循环中更新select的fdset。
RIL_Event结构分析:
typedef void (*ril_event_cb)(int fd, short events, void *userdata); 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; };
综合上面的分析来看这个结构体,可以得到如下的结论:
1、event是一个链表的结构
2、event和一个fd相关连或者timer关联
3、针对和fd相关的event具有persist属性
4、event都有一个处理回调函数
5、每一个回调函数都有一个param参数
6、index用于记录event在watchtable中的位置,方便删除的操作。
RIL_Init:
主要的工作是将通知Phone-Framework的回调函数注册到vendor-ril当中,这样当vendor-ril就可以将来自phone-framwrok的request处理结果,以及unsolicited response的结果返回给framework。此外,还完成了负责和AT模块交流的硬件(串口,socket,etc.)的初始化工作,并建立readerloop,用于监听AT模块返回的结果。vendor-ril可以参考reference-ril。
RIL_register分析:
这里的作用是将vendor-ril中的request处理函数注册到rild中,并建立rild socket的监听事件。这个sockt在init阶段就已经创建好了。在代码中可以看到这一个片段:
ril_event_set (&s_listen_event, s_fdListen, false, listenCallback, NULL); rilEventAddWakeup (&s_listen_event);
根据之前得分析,我们可以知道,通过这两句话,我们在watch_table中添加了一个fd-event,在这个fd有变化的时候调用listencallback进行处理。由于这个event不是persist的,因此rild每次只能接受一个socket连接。因为phone和rild之间采用的是socket通讯,那listencallback中肯定会包含accept。
ListenCallback分析:
正如之前分析的一样,Listencallback中确实存在accept。在这个callback中要对socket的合法性进行检查,然后,在accpet的fd上建立s_commands_event。
ril_event_set (&s_commands_event, s_fdCommand, 1, processCommandsCallback, p_rs); rilEventAddWakeup (&s_commands_event);
这样以来,只要phone端通过socket发送来数据,对应的 processCommandsCallback进行处理。
ProcessCommandsCallback分析:
这个函数会读取一条完整的command,然后调用processCommandBuffer将来自phone端的request进行分发。然后处理完完整的command之后,rild会端来和phone之间的socket链接,并重新监听端口。如下:
close(s_fdCommand);
rilEventAddWakeup(&s_listen_event);
响应command request全过程: