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相关推荐

  1. RIL 机制---消息从RIL到RILJ

    8 RIL -->RILJ 8.1 上报消息处理 ril.cpp的RIL_onUnsolicitedResponse方法主要代码如下, //得到当前命令的请求码 unsolResponseInd ...

  2. RIL 机制---rild守护进程

    3 守护进程 ril与上层的RILJ沟通方式是通过Socket传输数据与命令,而reference-ril与底层Modem的信号传输是通过串口用AT命令来实现. RILC是一个守护进程,由init进程 ...

  3. RIL 机制---开篇

    1, 概念 本文基于android 6.0.RIL(Radio Interface Layer,无线通信接口层)主要相关的结构如下, 为了便于论述,将RIL分为三个部分, 1, Framework层中 ...

  4. (1)RIL简析(高通)——RIL如何启动及初始化

    Android设置了RIL层,是上层framework与Modem沟通的桥梁.高通使用qcril作为其vendor-RIL,与modem之间使用QMI机制通讯. 分3篇分析下面的问题: RIL如何启动 ...

  5. 我的架构梦:(九十九)消息中间件之RocketMQ的高可用机制——消息消费高可用

    欢迎大家关注我的公众号[老周聊架构],Java后端主流技术栈的原理.源码分析.架构以及各种互联网高并发.高性能.高可用的解决方案. 一.前言 在前两篇我们介绍了 我的架构梦:(九十七)消息中间件之Ro ...

  6. Android——RIL 机制源码分析

    Android 电话系统框架介绍 在android系统中rild运行在AP上,AP上的应用通过rild发送AT指令给BP,BP接收到信息后又通过rild传送给AP.AP与BP之间有两种通信方式: 1. ...

  7. 我的架构梦:(九十八)消息中间件之RocketMQ的高可用机制——消息发送高可用

    欢迎大家关注我的公众号[老周聊架构],Java后端主流技术栈的原理.源码分析.架构以及各种互联网高并发.高性能.高可用的解决方案. 一.前言 在上一篇我们介绍了 我的架构梦:(九十七)消息中间件之Ro ...

  8. rabbitmq消息队列 ack机制(消息确认机制)和消息补偿机制

    参考:https://blog.csdn.net/pan_junbiao/article/details/112956537 ack 机制就是消息在 生产者在发布消息以后,消息存在内存中,如果消息被确 ...

  9. linux架构接口层教程,在LINUX平台上进行成功实现RIL层功能和框架层应用

    1.课题研究的背景和意义 1.1 Android RIL简介 Android RIL(Radio Interface Layer)提供了无线基带Modem与电话应用之间的抽象层.在Android RI ...

最新文章

  1. 设置WebStrom切换最近打开过的项目快捷键Alt+E
  2. 上海市金山区财政局容灾项目竞争性谈判600万元
  3. Systick 延时函数详解
  4. 正则表达式(基础、常用)----JavaScript
  5. 第一阶段unity基础
  6. linux下vi的一些简单的操作
  7. php遍历memcache,php遍历memcache所有键值
  8. acdream 1042: Classification of the species 抽象无根树并查集
  9. 搞明白“清算-结算”的二级制
  10. C程序设计试题汇编(第三版)谭浩强主编 第二章 选择题解析+总结
  11. echarts的全国和全球经纬度数据
  12. 禁止Unity3D中的物体碰撞后旋转
  13. Arithmetic Progressions
  14. 转炉炼钢计算机仿真实验报告,转炉侧吹熔炼水模型计算机仿真实验-计算机仿真论文-计算机论文(8页)-原创力文档...
  15. 9660图像 缺少iso_刻录映像时出现的问题
  16. VMware中Ubuntu20.04网卡丢失
  17. NB无信号以及无法连接网络问题分析及解决
  18. DRM之Microsoft PlayReady学习入门(一)
  19. 进阶爬虫:今日头条街拍美图
  20. DVR 和 NVR 的区别

热门文章

  1. Linux关机后Docker无法启动
  2. Redis实现feed流
  3. 终于回应!林志玲春晚水上芭蕾表演替身曝光是怎么回事?
  4. webView.clearHistory()清空历史记录
  5. 利用perceptual_loss感知损失获得更好的图片重建效果
  6. powerdesigne
  7. 没了乔布斯,苹果还甜吗?
  8. 在linux下看中大网络电视
  9. 如何定位前后端Bug?
  10. Aircrack-ng: (2) WEP WPA/WPA2 破解