基于Android6.0的RIL底层模块分析
来源:互联网 发布:日程提醒软件 编辑:程序博客网 时间:2024/05/17 11:07
看代码的时候不要看到细节里面,先构建模块的运行框架,后续有需要再深入细节。必要的时候需要拿一个本子将主要流程画出来或者写出来。
我们先看看,从系统刚开机是如何启动RIL功能的。首先先查看一下init.rc(这个文件包含一些初始化的服务或者功能,在开机阶段占有很重要的地位)。
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 qcom_diag
可以看到启动了三个socket,其中sap_uim_socket1和rild-debug这两个我们不需要关注,sap是跟蓝牙接听有关的,大家如果有兴趣可以另外在了解。debug从命名上看应该是跟调试功能有关的,我们现在也不关注,所以我们只看:
socket rild stream 660 root radio
Android系统解析这句的时候,会创建一个名为rild的socket,这个socket就是上层跟底层进行通信的socket了。可以看看RIL.java里面的RILReceiver和RILSender类,里面都是针对这个socket进行的读写操作,可见该socket确实就是上下层进行通信的途径了。
而这个service的对应的可执行文件地址为:
/system/bin/rild 对应的逻辑代码为:hardware\ril\rild\Rild.c
去掉不相干的代码,只研究最核心的代码如下:
int main(int argc, char **argv) { const RIL_RadioFunctions *(*rilInit)(const struct RIL_Env *, int, char **); const RIL_RadioFunctions *funcs; dlHandle = dlopen(rilLibPath, RTLD_NOW); RIL_startEventLoop();// 1 rilInit = (const RIL_RadioFunctions *(*)(const struct RIL_Env *, int, char **))dlsym(dlHandle, "RIL_Init");funcs = rilInit(&s_rilEnv, argc, rilArgv);// 2 RIL_register(funcs);// 3}
这个main函数里面主要就是我上面标明的1/2/3三个函数调用,而这三个函数调用就建立了rild进程的整个事件循环机制,与底层modem通信机制,与上层java层的framework里面的RIL.java的通信机制。其实上层的拨打电话,发短信什么的都是通过rild来实现。rild类似一个中间层,将java层的请求(打电话,发短信等)转发到modem端,将底层modem的来电或者来短信通知到上层(就是程序中经常看到的Unsolicited,意为不请自来。也就是来电,来短信之类的事件)。
在程序里面先是载入了libreference-ril.so库文件,这个库的实现是在“hardware\ril\reference-ril\Reference-ril.c”里面,所以第二步里面执行的rilInit函数可以在这个源码文件里面去找。后续再详细分析这个初始化函数。
- 列表内容
RIL_startEventLoop-启动一个线程,并将事件监听机制建立并启动
我们先看看RIL_startEventLoop的源码:
//Ril.cppextern "C" void RIL_startEventLoop(void) { int result = pthread_create(&s_tid_dispatch, &attr, eventLoop, NULL);}static void *eventLoop(void *param) { int filedes[2]; ril_event_init(); ret = pipe(filedes); s_fdWakeupRead = filedes[0]; s_fdWakeupWrite = filedes[1]; ril_event_set (&s_wakeupfd_event, s_fdWakeupRead, true, processWakeupCallback, NULL); rilEventAddWakeup (&s_wakeupfd_event); // Only returns on error ril_event_loop(); kill(0, SIGKILL); return NULL;}
总结一下,实际上另外启动了一个线程s_tid_dispatch(从名字dispatch上看,应该是一个分发事件的线程),这个线程的函数主体是eventLoop。在eventLoop里面首先调用的ril_event_init函数,这个函数里面对fd_set readFds,timer_list,pending_list,watch_table进行了初始化(或者说清零)。从这个重要的初始化函数,大家应该可以大概猜到,这个线程主要是操作这几个数据结构的,至于后续如何操作可以再深入了解,但是第一遍我们只需要知道大概流程。里面用到了fd_set,大家应该去搜索一下Linux的select机制和FD_SET这两个关键字。这里简单描述一下,Linux会对fd_set集合里面的一组socket或者文件句柄进行监听,如果有部分socket有数据变化,那么就会返回这部分socket的数组。这样就可以不用一直轮询所有的socket或者文件句柄,节省了大量的cpu资源。
除此之外还建立了一个管道,并且将这个管道的读端绑定到了s_wakedupfd_event上面。注意看这个event设置二人组,在程序里面很多地方都出现过:
ril_event_set (&s_wakeupfd_event, s_fdWakeupRead, true, processWakeupCallback, NULL);rilEventAddWakeup (&s_wakeupfd_event);
先用一些参数设置一个event,然后将这个event添加到watch_table和readFds里面,我们上面在ril_event_init函数里面也提到过过这两个变量。其实翻译成人类理解的语言就是,如果在s_fdWakeupRead这个fd上有数据变化,那么就发送给processWakeupCallback进行处理。将这两个值包装成一个ril_event,并且另起一个线程建立起eventloop循环来处理ril_event事件。
最后调用ril_event_loop,核心代码如下:
//Ril_event.cppvoid ril_event_loop(){ for (;;) { // make local copy of read fd_set memcpy(&rfds, &readFds, sizeof(fd_set)); n = select(nfds, &rfds, NULL, NULL, ptv); // Check for timeouts processTimeouts(); // Check for read-ready processReadReadies(&rfds, n); // Fire away firePending(); }}
基本上启动了一个无限循环,不停的查看readFds集合里面是否有一些文件句柄fd有数据变化,并将这些有变化的句柄保存到nfds里面。processTimeouts进行超时处理,processReadReadies将nfds里面的ril_event事件取出,进行一定判断,并将其放到pending_list里面(注意这个列表我们在ril_event_init里面也提到过)。firePending,看名字就知道是处理Pending列表了,它的代码里面确实对ril_event事件进行了处理。
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; }}
基本上是一个遍历链表,并且处理每个节点的过程。注意,ril_event结构里面本身就包含了一个func函数指针,这个函数的作用就是在这个时候处理自己的。所以其实这个事件本身就包含了如何处理自身数据的功能,这样也算是一种程度上的解耦吧。类似一种自治的结构,不需要外部模块提供处理函数,同时提高了封装性。
再从前面的代码:
ril_event_set (&s_wakeupfd_event, s_fdWakeupRead, true, processWakeupCallback, NULL);
通体再读一遍,会发现其实这个事件的处理函数processWakeupCallback,其实只是起到清空wakeup socket的作用。
阶段总结
RIL_startEventLoop会另起一个线程,监听readFds这个集合里面的socket(或者文件或者管道)句柄,并且对监听到的变化进行处理。而readFds里面的内容是通过二人组进行添加的。你看这样分析之后就很简单了吧。
- rilInit(&s_rilEnv, argc, rilArgv)
这个函数实际调用的是libreference-ril.so库文件里面的RIL_Init函数:
//Reference-ril.cconst RIL_RadioFunctions *RIL_Init(const struct RIL_Env *env, int argc, char **argv){ ret = pthread_create(&s_tid_mainloop, &attr, mainLoop, NULL);}static void *mainLoop(void *param __unused){ for (;;) { fd = -1; while (fd < 0) { if (s_port > 0) { fd = socket_loopback_client(s_port, SOCK_STREAM); } else if (s_device_path != NULL) { fd = open (s_device_path, O_RDWR); if ( fd >= 0 && !memcmp( s_device_path, "/dev/ttyS", 9 ) ) { } } } ret = at_open(fd, onUnsolicited); RIL_requestTimedCallback(initializeCallback, NULL, &TIMEVAL_0); }}
其实就是启动一个s_tid_mainloop线程,主题函数为mainLoop。在这个函数里面会用at_open打开文件/dev/ttyS。其实这个就是modem在linux系统里面抽象出来的文件。而在at_open里面会另外再起一个线程:
pthread_create(&s_tid_reader, &attr, readerLoop, &attr);
这个readerLoop函数需要自己去看了,我只简单说一下(因为其实我也没有深入去看啊,不过只是了解流程的话并无必要,除非碰到实际问题需要解决)。这个readerLoop从AT channel读取内容,并且解析该内容,并且根据内容调用不同的函数进行处理。看看下面的处理函数:
static void processLine(const char *line){ pthread_mutex_lock(&s_commandmutex); if (sp_response == NULL) { /* no command pending */ handleUnsolicited(line); } else if (isFinalResponseSuccess(line)) { sp_response->success = 1; handleFinalResponse(line); } else if (isFinalResponseError(line)) { sp_response->success = 0; handleFinalResponse(line); } pthread_mutex_unlock(&s_commandmutex);}
Unsolicited就是不请自来的来电,来短信等事件了。response就是上层要求的事件比如打电话/发短信等。这些处理函数再深入进去看真的是五花八门,各显神通了。有直接赋值的handle函数,有使用Linux下面的条件互斥,通知另外一个线程进行处理的机制等。
阶段总结
rilInit会启动一个线程mainLoop(其实后面还启动了一个readerLoop),专门用AT命令从modem读取数据,并且进行处理。且最后会返回一个RIL_RadioFunctions到rild.c里面的main函数里面。
static const RIL_RadioFunctions s_callbacks = { RIL_VERSION, onRequest, currentState, onSupports, onCancel, getVersion};
- RIL_register(funcs);
这是main里面的最后一个函数了。
extern "C" void RIL_register (const RIL_RadioFunctions *callbacks) { memcpy(&s_callbacks, callbacks, sizeof (RIL_RadioFunctions)); /* Initialize socket1 parameters */ s_ril_param_socket = { RIL_SOCKET_1, /* socket_id */ -1, /* fdListen */ -1, /* fdCommand */ PHONE_PROCESS, /* processName */ &s_commands_event, /* commands_event */ &s_listen_event, /* listen_event */ processCommandsCallback, /* processCommandsCallback */NULL /* p_rs */}; // start listen socket1 startListen(RIL_SOCKET_1, &s_ril_param_socket);}
干了两件事,一个是初始化了一个s_ril_param_socket,并且将其作为参数传递到了startListen函数。先看看这个结构体SocketListenParam s_ril_param_socket。
typedef struct SocketListenParam { RIL_SOCKET_ID socket_id; int fdListen; int fdCommand; char* processName; struct ril_event* commands_event; struct ril_event* listen_event; void (*processCommandsCallback)(int fd, short flags, void *param); RecordStream *p_rs; RIL_SOCKET_TYPE type;} SocketListenParam;
用通俗语言解释就是,在某一张卡(针对的是多卡的手机)上,从listen和command的句柄上获取listen到的事件和上层发送过来的command事件。并且还包含有处理完命令之后的回调函数。
下面分析一下startListen函数:
static void startListen(RIL_SOCKET_ID socket_id, SocketListenParam* socket_listen_p) { fdListen = android_get_control_socket(socket_name); ret = listen(fdListen, 4); socket_listen_p->fdListen = fdListen; /* note: non-persistent so we can accept only one connection at a time */ ril_event_set (socket_listen_p->listen_event, fdListen, false,listenCallback, socket_listen_p); rilEventAddWakeup (socket_listen_p->listen_event);}
这个android_get_control_socket通过socket_name(一般是”rild”)获取到socket句柄。用这个句柄名称在init.rc中查找,并且返回指定句柄。见文章开头提到的那个句柄。后续会在这个句柄上监听,该句柄支持最多缓存四个事件,超过的就会被丢弃。并且会把这个句柄用来赋值给socket_listen_p这个变量里面的fdListen值。后面又看到二人组了,ril_event_set和rilEventAddWakeup。将socket_listen_p->listen_event这个ril_event设置一下(包括监听哪个fdListen,使用哪个函数处理自己listenCallback),然后把这个ril_event事件添加到watch_table里面,并且把这个fdListen句柄添加到readFds里面。前面已经讲过,这个readFds上面有个线程在不停监听,并对有数据的fd进行处理。二人组函数ril_event_set和rilEventAddWakeup的组合,其实就是一个机制:将socket添加到多路IO复用机制中,一旦有上层请求,那么就能触发相应的处理函数。
上面就是整个native层的rild模块的主要工作内容了,相信大家已经比较了解了。他对应的Java层的RIL也会对套接字“rild”进行操作,这样就达到了互相通信的目的了。大家可以看看RIL.java这个函数。Java层的RIL分析后续有时间再奉上。如本文有错漏之处,请各位朋友指正。
- 基于Android6.0的RIL底层模块分析
- 基于Android6.0的RIL框架层模块分析
- Android6.0的phone应用源码分析(5)——RIL层分析
- Android6.0的phone应用源码分析(6)——RIL层框架分析
- Android6.0的phone应用源码分析(7)——RIL层框架分析2
- Android6.0的phone应用源码分析(5)——RIL层分析
- Android6.0的phone应用源码分析(7)——RIL层框架分析2
- Android RIl模块的结构分析
- 基于Android6.0的Activity加载View源码分析
- [Android6.0] Reference-RIL 运行框架
- 基于android6.0 mediaplayer框架分析
- Android 的RIL驱动模块
- Android之通信RIL模块分析
- Android平台开发-3G RIL模块分析
- Android平台开发-3G RIL模块分析
- 基于迅为4412精英版连接SIM7100C模块Android下调试——RIL库分析(一)
- 基于迅为4412精英版连接SIM7100C模块Android下调试——RIL库分析(二)
- 基于迅为4412精英版连接SIM7100C模块Android下调试——RIL库分析(二)
- 修改视图
- linux常见命令
- 强悍的 Ubuntu —— 粘贴板
- C++面试题:三种数值交换方法
- mkfifo函数
- 基于Android6.0的RIL底层模块分析
- C语言函数传递中形参的变化不会改变实参的值
- Android Studio 快捷键整理分享
- 拿石子问题
- 动态内存分配
- ImageLoad中的配置属性--Configuration所有配置简介
- HTML基础
- Ruby语言学习系列--基本的ruby语法
- 第七届蓝桥杯省赛A组题解(1~5)