RIL 机制---消息从RIL到reference-ril
5,RIL—>reference-ril
首先ril从socket中读取RILJ发送的数据,然后在ril进行处理封装,最后发送到reference-ril进行处理。
5.1 读取数据
读取数据流程图如下,
eventLoop方法如下,
static void *eventLoop(void *param) {int ret;int filedes[2];ril_event_init();//Event的初始化pthread_mutex_lock(&s_startupMutex);s_started = 1;pthread_cond_broadcast(&s_startupCond);pthread_mutex_unlock(&s_startupMutex);ret = pipe(filedes);//创建管道if (ret < 0) {RLOGE("Error in pipe() errno:%d", errno);return NULL;}s_fdWakeupRead = filedes[0];s_fdWakeupWrite = filedes[1];
fcntl(s_fdWakeupRead, F_SETFL, O_NONBLOCK); //设置管道读端属性
//利用pipe创建一个Eventril_event_set (&s_wakeupfd_event, s_fdWakeupRead, true, processWakeupCallback, NULL);rilEventAddWakeup (&s_wakeupfd_event);// Only returns on errorril_event_loop();//进入loop循环RLOGE ("error in event_loop_base errno:%d", errno);// kill self to restart on errorkill(0, SIGKILL);return NULL;
}
主要分为2个步骤:
1,调用ril_event_init方法进行Event的初始化。
2,调用ril_event_loop方法进入循环。
5.1.1 Event初始化
ril_event_init方法如下,
void ril_event_init()
{MUTEX_INIT();FD_ZERO(&readFds);init_list(&timer_list);init_list(&pending_list);memset(watch_table, 0, sizeof(watch_table));
}
对三个链表进行初始化。
5.1.2 Event循环
ril_event_loop方法如下,
void ril_event_loop()
{int n;fd_set rfds;struct timeval tv;struct timeval * ptv;for (;;) { //死循环检测Event消息// make local copy of read fd_setmemcpy(&rfds, &readFds, sizeof(fd_set));if (-1 == calcNextTimeout(&tv)) { //计算下一次超时时间// no pending timers; block indefinitelydlog("~~~~ no timers; blocking indefinitely ~~~~");ptv = NULL; /NULL说明select是阻塞模式} else {dlog("~~~~ blocking for %ds + %dus ~~~~", (int)tv.tv_sec, (int)tv.tv_usec);ptv = &tv; //非空说明在超时时间内是阻塞模式}printReadies(&rfds);
//用select去扫描readFds中的所有管道集合,检测RILJ是否有新数据出现。n = select(nfds, &rfds, NULL, NULL, ptv);printReadies(&rfds);dlog("~~~~ %d events fired ~~~~", n);if (n < 0) {if (errno == EINTR) continue;RLOGE("ril_event: select error (%d)", errno);// bail?return;}// Check for timeoutsprocessTimeouts();//检查超时事件,如果超时,将Event放入pending_list中 // Check for read-readyprocessReadReadies(&rfds, n); //检查watch_table,将Event加入pending_list中// Fire awayfirePending();//执行pending_list中的Event}
}
当RILJ发送数据后,在EventLoop中会依次调用processTimeouts、processReadReadies、firePending方法分别处理三个链表。
5.1.2.1 processTimeouts
processTimeouts方法如下,
static void processTimeouts()
{dlog("~~~~ +processTimeouts ~~~~");MUTEX_ACQUIRE();struct timeval now;struct ril_event * tev = timer_list.next;struct ril_event * next;getNow(&now);// walk list, see if now >= ev->timeout for any events//如果timer_list中某个事件已经超时dlog("~~~~ Looking for timers <= %ds + %dus ~~~~", (int)now.tv_sec, (int)now.tv_usec);while ((tev != &timer_list) && (timercmp(&now, &tev->timeout, >))) {// Timer expireddlog("~~~~ firing timer ~~~~");next = tev->next;removeFromList(tev); //从timer_list中删除addToList(tev, &pending_list); //添加到pending_list中tev = next;}MUTEX_RELEASE();dlog("~~~~ -processTimeouts ~~~~");
}
5.1.2.2 processReadReadies
processReadReadies方法如下,
static void processReadReadies(fd_set * rfds, int n)
{dlog("~~~~ +processReadReadies (%d) ~~~~", 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); //添加到pending_listif (rev->persist == false) {removeWatch(rev, i); //如果persist为false才去删除当前Event}n--;}}MUTEX_RELEASE();dlog("~~~~ -processReadReadies (%d) ~~~~", n);
}
5.1.2.3 firePending
firePending方法如下,
static void firePending()
{dlog("~~~~ +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); //执行其func并把参数传递进去ev = next;}dlog("~~~~ -firePending ~~~~");
}
上面的循环过程说明,eventLoop的是通过在内部循环中用Linux中的select方法检测
readFds中所有的文件句柄(或者说管道),如果发现有新的数据进来,就去遍历watch_table和timer_list表,
把需要处理的eventLoop加入到pending_list中,然后进入pending_list中去执行每个Event的func。
5.1.3 Event机制
Event要做的就是循环检测EventLoop中添加的句柄池,如果在当前的句柄池中有任意一个
句柄所代表的通道中有新的数据进来,就去处理当前的新数据。而在句柄池中最重要的句柄就是RILJ与RILC之间的Socket通道。
Event的实现主要在ril_event.cpp文件中, Event定义在ril_event.c文件中,
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;
};
从上面的结构体可以看出,Event的管理是通过链表实现的,一些重要的成员变量的意义如下:
fd:事件相关设备句柄。最重要的就是RILJ与RILC之间的Socket文件句柄
persist:说明当前的Event需要保持,不能从watch_table中删除
func:当前Event的处理函数
param:调用当前Event处理函数时的参数
处理Event的方法如下,
//Event的初始化,其实就是对3个主要链表的初始化
static void init_list(struct ril_event * list)
//添加Event到链表
static void addToList(struct ril_event * ev, struct ril_event * list)
//从链表删除Event
static void removeFromList(struct ril_event * ev)
//从watch连表中删除某个Event
static void removeWatch(struct ril_event * ev, int index)
//处理超时的Event
static void processTimeouts()
//初始化Event链表
void ril_event_init()
//初始化一个Event
void ril_event_set(struct ril_event * ev, int fd, bool persist, ril_event_cb func, void * param)
//把一个Event添加到watch_table中
void ril_event_add(struct ril_event * ev)
//把一个Event添加到timer_list中
void ril_timer_add(struct ril_event * ev, struct timeval * tv)
//把一个Event从watch_table中删除
void ril_event_del(struct ril_event * ev)
//主循环
void ril_event_loop()
管理Event的过程应该是生成相应的Event节点,然后将Event添加到链表,
处理Event之后就需要把当前的Event从链表中删除。而且我们看出,
Event管理中应该存在多个链表,那么究竟有哪些链表在运行呢?
RIL的Event管理体系中存在3个链表结构:watch_table,timer_list,pending_list,并使用了一个
设备句柄池readFDS,把所有的Socket管道的文件句柄保存起来。而管理的过程可以归纳为以下6点:
1、可以将一个Event添加到watch_table或者timer_list中;
2、如果Event是添加到watch_table中,需要把当前Event的fd(事件设备句柄)添加到readFDS中;
3、如果Event是添加到timer_list中,不需要把当前Event的fd(事件设备句柄)添加到readFDS中,而且当前Event的fd值是无效的;
4、在循环检测过程中,如果发现watch_table中有Event就会把当前Event添加到pending_list中,
如果当前Event的persist属性为false,说明不需要保留当前节点,就把当前的Event从watch_table中删除;
如果persist为true,说明需要保留,就不需要从watch_table中删除当前节点。
5、在循环检测过程中,如果发现timer_list中的Event超时时,把当前Event移动到pending_list中,同时删除timer_list中的节点。
6、在循环检测的过程中,等watch_table和timer_list处理完毕后,就去pending_list中执行里面的Event所指向的func。
5.2 ril中数据处理
在5.1.2.3小节中, firePending方法从list中逐个读出消息,然后回调func方法,其实就是listenCallback方法。
消息处理流程图如下,
5.2.1 处理
listenCallback主要方法如下,
fdCommand = accept(fd, (sockaddr *) &peeraddr, &socklen);// 得到流套接字
//获取当前命令的选项值
err = getsockopt(fdCommand, SOL_SOCKET, SO_PEERCRED, &creds, &szCreds);
p_info->processCommandsCallback, p_info);//对消息进行处理
onNewCommandConnect(p_info->socket_id);//向RILJ发送上报消息
onNewCommandConnect方法如下,
// RIL连接成功
RIL_UNSOL_RESPONSE(RIL_UNSOL_RIL_CONNECTED, &rilVer, sizeof(rilVer), socket_id);
//Radio状态改变
RIL_UNSOL_RESPONSE(RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED, NULL, 0, socket_id);
因为RILJ向RILC发送消息是异步处理的,所以RILC回2次,第一次返回收到了消息,第二次返回处理结果。RIL_UNSOL_RESPONSE定义如下,
#define RIL_UNSOL_RESPONSE(a, b, c, d) RIL_onUnsolicitedResponse((a), (b), (c), (d))
所以,实际上调用的还是RIL_onUnsolicitedResponse方法。
processCommandsCallback方法如下,
for (;;) {/* loop until EAGAIN/EINTR, end of stream, or other error */ret = record_stream_get_next(p_rs, &p_record, &recordlen);if (ret == 0 && p_record == NULL) {/* end-of-stream */break;} else if (ret < 0) {break;} else if (ret == 0) { /* && p_record != NULL *///把RILJ层数据通过AT发送到ModemprocessCommandBuffer(p_record, recordlen, p_info->socket_id);}}if (ret == 0 || !(errno == EAGAIN || errno == EINTR)) {/* fatal error or end-of-stream */if (ret != 0) {RLOGE("error on reading command socket errno:%d\n", errno);} else {RLOGW("EOS. Closing command socket.");}close(fd); //命令已经发送完成,关闭当前命令的流套接字p_info->fdCommand = -1;ril_event_del(p_info->commands_event); //删掉当前Eventrecord_stream_free(p_rs);/* start listening for new connections again */rilEventAddWakeup(&s_listen_event); //重新添加RILJ与RILC之间的Socket EventonCommandsSocketClosed(p_info->socket_id);}
1,调用processCommandBuffer将数据发送到Modem
2,对Event的后续处理,删除当前已经处理的Event。
processCommandBuffer方法如下,
pRI = (RequestInfo *)calloc(1, sizeof(RequestInfo));status = p.readInt32(&request);//获取消息码
status = p.readInt32 (&token);//获取系列号,也成为令牌pRI->token = token;pRI->pCI = &(s_commands[request]); //s_commands中针对不同的命令对应不同的处理函数pRI->socket_id = socket_id;ret = pthread_mutex_lock(pendingRequestsMutexHook);assert (ret == 0);pRI->p_next = *pendingRequestsHook;*pendingRequestsHook = pRI;ret = pthread_mutex_unlock(pendingRequestsMutexHook);assert (ret == 0);/* sLastDispatchedToken = token; */pRI->pCI->dispatchFunction(p, pRI);// 调用reference 中对应方法
重要的是消息的封装调用机制,下个小结论述。
5.2.2 消息封装机制
s_commands变量如下,对应的是一个文件,
static CommandInfo s_commands[] = {
#include "ril_commands.h"
};
CommandInfo结构体定义如下,
typedef struct {int requestNumber;void (*dispatchFunction) (Parcel &p, struct RequestInfo *pRI);int(*responseFunction) (Parcel &p, void *response, size_t responselen);
} CommandInfo;
该结构体分为3部分,
1, requestNumber表示ril从socket中读取消息的消息码。
2, dispatchFunction发送方法,当前命令可以通过这个接口把数据发送到reference-ril库;
3,responseFunction回调方法,当Modem返回数据后reference-ril侧可以用这个函数把数据进行打包,然后传递给ril侧。
processCommandBuffer中就是通过当前的消息码找到对应的发送方法(dispatchFunction)
和回调方法(responseFunction)。然后把这三个数据连同当前消息的系列号(当前消息的ID)
构建一个在ril和reference-ril通用的数据类型(RequestInfo)并把它发送给reference-ril侧。
include"ril_commands.h 文件部分内容如下,
{RIL_REQUEST_ENTER_NETWORK_DEPERSONALIZATION, dispatchStrings, responseInts},
{RIL_REQUEST_GET_CURRENT_CALLS, dispatchVoid, responseCallList},{RIL_REQUEST_DIAL, dispatchDial, responseVoid},
通俗的将,例如,读取消息的消息码为RIL_REQUEST_DIAL,对应的发送方法dispatchDial主要代码如下,
CALL_ONREQUEST(pRI->pCI->requestNumber, &dial, sizeOfDial, pRI, pRI->socket_id);
CALL_ONREQUEST定义如下,
#define CALL_ONREQUEST(a, b, c, d, e) s_callbacks.onRequest((a), (b), (c), (d), (e))
include"ril_commands.h 文件中的发送方法最后都会调用s_callbacks结构中的onRequest方法(详情请见3.3小节),
实质是调用reference-ril.c中的onRequest方法。这样消息就来到了reference-ril中。
最后需要论述的是,通过ril到reference-ril最后发送给Modem的消息肯定会有对应的回应消息,
并且是一一对应的,因此发送方法和回调方法也是一一对应的。
小结:
1,论述了Event以及链表的初始化等操作。
2, 开启EventLoop循环,读取RILJ发送过来的数据。
3,通过回调,完成了ril到reference-ril的数据发送。
RIL 机制---消息从RIL到reference-ril相关推荐
- RIL 机制---消息从RIL到RILJ
8 RIL -->RILJ 8.1 上报消息处理 ril.cpp的RIL_onUnsolicitedResponse方法主要代码如下, //得到当前命令的请求码 unsolResponseInd ...
- RIL 机制---rild守护进程
3 守护进程 ril与上层的RILJ沟通方式是通过Socket传输数据与命令,而reference-ril与底层Modem的信号传输是通过串口用AT命令来实现. RILC是一个守护进程,由init进程 ...
- RIL 机制---开篇
1, 概念 本文基于android 6.0.RIL(Radio Interface Layer,无线通信接口层)主要相关的结构如下, 为了便于论述,将RIL分为三个部分, 1, Framework层中 ...
- (1)RIL简析(高通)——RIL如何启动及初始化
Android设置了RIL层,是上层framework与Modem沟通的桥梁.高通使用qcril作为其vendor-RIL,与modem之间使用QMI机制通讯. 分3篇分析下面的问题: RIL如何启动 ...
- 我的架构梦:(九十九)消息中间件之RocketMQ的高可用机制——消息消费高可用
欢迎大家关注我的公众号[老周聊架构],Java后端主流技术栈的原理.源码分析.架构以及各种互联网高并发.高性能.高可用的解决方案. 一.前言 在前两篇我们介绍了 我的架构梦:(九十七)消息中间件之Ro ...
- Android——RIL 机制源码分析
Android 电话系统框架介绍 在android系统中rild运行在AP上,AP上的应用通过rild发送AT指令给BP,BP接收到信息后又通过rild传送给AP.AP与BP之间有两种通信方式: 1. ...
- 我的架构梦:(九十八)消息中间件之RocketMQ的高可用机制——消息发送高可用
欢迎大家关注我的公众号[老周聊架构],Java后端主流技术栈的原理.源码分析.架构以及各种互联网高并发.高性能.高可用的解决方案. 一.前言 在上一篇我们介绍了 我的架构梦:(九十七)消息中间件之Ro ...
- rabbitmq消息队列 ack机制(消息确认机制)和消息补偿机制
参考:https://blog.csdn.net/pan_junbiao/article/details/112956537 ack 机制就是消息在 生产者在发布消息以后,消息存在内存中,如果消息被确 ...
- linux架构接口层教程,在LINUX平台上进行成功实现RIL层功能和框架层应用
1.课题研究的背景和意义 1.1 Android RIL简介 Android RIL(Radio Interface Layer)提供了无线基带Modem与电话应用之间的抽象层.在Android RI ...
最新文章
- 设置WebStrom切换最近打开过的项目快捷键Alt+E
- 上海市金山区财政局容灾项目竞争性谈判600万元
- Systick 延时函数详解
- 正则表达式(基础、常用)----JavaScript
- 第一阶段unity基础
- linux下vi的一些简单的操作
- php遍历memcache,php遍历memcache所有键值
- acdream 1042: Classification of the species 抽象无根树并查集
- 搞明白“清算-结算”的二级制
- C程序设计试题汇编(第三版)谭浩强主编 第二章 选择题解析+总结
- echarts的全国和全球经纬度数据
- 禁止Unity3D中的物体碰撞后旋转
- Arithmetic Progressions
- 转炉炼钢计算机仿真实验报告,转炉侧吹熔炼水模型计算机仿真实验-计算机仿真论文-计算机论文(8页)-原创力文档...
- 9660图像 缺少iso_刻录映像时出现的问题
- VMware中Ubuntu20.04网卡丢失
- NB无信号以及无法连接网络问题分析及解决
- DRM之Microsoft PlayReady学习入门(一)
- 进阶爬虫:今日头条街拍美图
- DVR 和 NVR 的区别