• RIL如何启动及初始化?
  • RILJ的消息如何传递和被处理?

    • event table的定义
    • requested event的处理函数
    • ril_event的管理
    • RIL_REQEUST执行结果的返回
    • RIL_UNSOL类型消息的上报
  • 一个消息处理的具体例子——SETUP_DATA_CALL

  • 如何添加一个RIL消息和对应的处理函数?

event table的定义

RIL所有的EVENT都定义在Ril.h中,有以RIL_REQUEST和RIL_UNSOL_RESPONSE开头的消息,前者代表主动请求,后者一般是上报的消息,例如下面这2个:

#define RIL_REQUEST_SETUP_DATA_CALL 27
#define RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED 1000

当然,无论是REQUEST还是UNSOL_RESPONSE都会带参数,因此还需定义消息对应的结构体,例如RADIO_STATE的结构体:

typedef enum {RADIO_STATE_OFF = 0,                   /* Radio explictly powered off (eg CFUN=0) */RADIO_STATE_UNAVAILABLE = 1,           /* Radio unavailable (eg, resetting or not booted) *//* States 2-9 below are deprecated. Just leaving them here for backward compatibility. */RADIO_STATE_SIM_NOT_READY = 2,         /* Radio is on, but the SIM interface is not ready */RADIO_STATE_SIM_LOCKED_OR_ABSENT = 3,  /* SIM PIN locked, PUK required, networkpersonalization locked, or SIM absent */RADIO_STATE_SIM_READY = 4,             /* Radio is on and SIM interface is available */RADIO_STATE_RUIM_NOT_READY = 5,        /* Radio is on, but the RUIM interface is not ready */RADIO_STATE_RUIM_READY = 6,            /* Radio is on and the RUIM interface is available */RADIO_STATE_RUIM_LOCKED_OR_ABSENT = 7, /* RUIM PIN locked, PUK required, networkpersonalization locked, or RUIM absent */RADIO_STATE_NV_NOT_READY = 8,          /* Radio is on, but the NV interface is not available */RADIO_STATE_NV_READY = 9,              /* Radio is on and the NV interface is available */RADIO_STATE_ON = 10                    /* Radio is on */
} RIL_RadioState;

通过搜索,发现ril_commands.h中还有定义RIL_REQUEST的数组元素,字面意思来看,明显是消息处理入口和返回的函数入口:
{RIL_REQUEST_SETUP_DATA_CALL, dispatchDataCall, responseSetupDataCall}

ril.cpp居然直接引入了这些定义,所有消息和对应的函数都存在s_commands数组里了:

ril.cpp
typedef struct {int requestNumber;void (*dispatchFunction) (Parcel &p, struct RequestInfo *pRI);int (*responseFunction) (Parcel &p, void *response, size_t responselen);
} CommandInfo;static CommandInfo s_commands[] = {
#include "ril_commands.h"
};

那么,这些消息对应的函数是怎样被匹配并对应执行的呢?下一节。


requested event的处理

我们要看消息是如何被处理的,先从消息的监听入手,就应该能找到对应的处理函数了。
既然这些消息是上层下发的,那么RIL就是通过socket来接收到消息。先回顾下socket监听是怎样建立的,应该就能找到消息处理的入口。
上一篇有讲到,RIL_register有监听RILD socket的代码:

ril.cpp::RIL_register/* 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,  NULL                      /* p_rs */startListen(RIL_SOCKET_1, &s_ril_param_socket);

startListen函数获取到socket的fd,初始化event并加入到eventloop,回调为listenCallback:

    fdListen = android_get_control_socket(socket_name);ret = listen(fdListen, 4);socket_listen_p->fdListen = fdListen;ril_event_set (socket_listen_p->listen_event, fdListen, false,listenCallback, socket_listen_p);rilEventAddWakeup (socket_listen_p->listen_event);

listenCallback再次向eventloop加入event,这次event的persistence值为1,回调为processCommandsCallback:

Ril::listenCallback()//persist值为1,eventloop会一直监听这个fd
ril_event_set (p_info->commands_event, p_info->fdCommand, 1,
p_info->processCommandsCallback, p_info);
rilEventAddWakeup (p_info->commands_event);  

event的persistence值为1,ev->fd会一直存在于readFds集合,eventloop因此会持续监听socket。persist值的实际逻辑后面再来讲。

继续先看看processCommandsCallback这个函数跟event的处理有没有关系,一看果然没让我失望:

ril::processCommandsCallback(int fd, short flags, void *param)
{for (;;) {//从fd读取数据,应该是event的数据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 *///数据传入这个函数,继续深入processCommandBuffer(p_record, recordlen, p_info->socket_id);}}
}//分发消息
ril::processCommandBuffer(void *buffer, size_t buflen, RIL_SOCKET_ID socket_id)
{Parcel p;status_t status;int32_t request;int32_t token;RequestInfo *pRI;int ret;/* Hook for current context *//* pendingRequestsMutextHook refer to &s_pendingRequestsMutex */pthread_mutex_t* pendingRequestsMutexHook = &s_pendingRequestsMutex;/* pendingRequestsHook refer to &s_pendingRequests */RequestInfo**    pendingRequestsHook = &s_pendingRequests;p.setData((uint8_t *) buffer, buflen);status = p.readInt32(&request);status = p.readInt32 (&token);pRI = (RequestInfo *)calloc(1, sizeof(RequestInfo));pRI->token = token;pRI->pCI = &(s_commands[request]); //获取消息对应的结构体成员pRI->socket_id = socket_id;pRI->p_next = *pendingRequestsHook;pRI->pCI->dispatchFunction(p, pRI); //分发消息到对应的消息
}

消息处理的地方就在processCommandBuffer,使用ID匹配到对应的s_commands成员,调用对应的处理函数。

消息定义、监听和分发消息的地方都找到了,接下来再看看ril_event_loop有哪些具体的处理:

ril_event.cppvoid ril_event_loop()
{int n;fd_set rfds;struct timeval tv;struct timeval * ptv;for (;;) {//make local copy of read fd_setmemcpy(&rfds, &readFds, sizeof(fd_set));if (-1 == calcNextTimeout(&tv)) {// no pending timers; block indefinitelyptv = NULL;} else {ptv = &tv;}//select轮询fd_set集合IO状态n = select(nfds, &rfds, NULL, NULL, ptv);if (n < 0) {if (errno == EINTR) continue;return;}// Check for timeoutsprocessTimeouts();// Check for read-readyprocessReadReadies(&rfds, n);// Fire awayfirePending();}
}

select返回值后,执行了3个函数:processTimeouts()/processReadReadies(&rfds, n)/firePending(),挨个看。

processTimeouts()检查timer_list是否有超时项,如果有则加入到pending_list:

static void processTimeouts()
{struct timeval now;struct ril_event * tev = timer_list.next;struct ril_event * next;getNow(&now);while ((tev != &timer_list) && (timercmp(&now, &tev->timeout, >))) {// Timer expirednext = tev->next;//移除eventremoveFromList(tev);//将event加入pending_listaddToList(tev, &pending_list);tev = next;}
}

processReadReadies 从watch_table获取到对应的event,并加入到pending_list。

static void processReadReadies(fd_set * rfds, int n)
{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--;}}
}

之前讲到event的persistence值,正是在processReadReadies里面用到,作用是控制event是否在触发后从watch_table移除,rild socket的监听设置persistence=1,因此其fd不会在这个被移除掉。

firePending()将event从pending_list移除出去,并调用ev->func执行消息处理:

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;}
}

ev->func对应什么函数?
还记得前面设置rild socket监听的时候,event参数是:

ril_event_set (p_info->commands_event, p_info->fdCommand, 1,
p_info->processCommandsCallback, p_info);

ev->func就是processCommandsCallback,没错,接着就是从s_commands里面找到对应的event:
pRI->pCI->dispatchFunction(p, pRI);

然后dispatchFunction:
{RIL_REQUEST_SETUP_DATA_CALL, dispatchDataCall, responseSetupDataCall}

responseSetupDataCall处理完成继续将数据传到下一个函数dispatchStrings

#define CALL_ONREQUEST(a, b, c, d, e) s_callbacks.onRequest((a), (b), (c), (d), (e))static void dispatchStrings (Parcel &p, RequestInfo *pRI) {CALL_ONREQUEST(pRI->pCI->requestNumber, pStrings, datalen, pRI, pRI->socket_id);
}

后面就是执行qcril onRequest函数,每个消息具体处理都有所不同,这里先作抛砖引玉。

至于上面提及的watch_table、pending_list还有对这些list的操作addToList等等,他们作用是什么,接着下一节来说。


ril_event的管理

在eventloop里面实际操作的是几个ril_event结构体数组和指针,来看看究竟用途是什么。

ril_event.cpp里面有3个与ril_event有关的list,还有4个list的操作函数:

static struct ril_event * watch_table[MAX_FD_EVENTS];
static struct ril_event timer_list;
static struct ril_event pending_list;static void addToList(struct ril_event * ev, struct ril_event * list);
static void removeFromList(struct ril_event * ev);
static void removeWatch(struct ril_event * ev, int index);
void ril_timer_add(struct ril_event * ev, struct timeval * tv);

addToList,加入一个event,ril_event通过链表方式保存:

static void addToList(struct ril_event * ev, struct ril_event * list)
{ev->next = list;ev->prev = list->prev;ev->prev->next = ev;list->prev = ev;dump_event(ev);
}//将ril_event移除
static void removeFromList(struct ril_event * ev)
{ev->next->prev = ev->prev;ev->prev->next = ev->next;ev->next = NULL;ev->prev = NULL;
}

watch_table是ril_event类型的数组,ril_event有fd和callback等字段,可见watch_table只是作为保存“监听器”之用,而真正要被处理的消息是在callback函数中从fd stream读取,个人觉得ril_event的命名有点不太准确了:

ril_event.h
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;
};

pending_list是即将回调(ev->func)的ril_event 列表,可以理解成被触发的监听器列表,ril_event处理后会从pending_list移除掉。

timer_list 设置了timeout的ril_event列表,从processTimeouts()函数可看出,超时的ril_event都会加进pending_list。

小结:
ril_event作用相当于监听器,并非实际的事件;
存在于watchtable中的ril_event提供了读写event的fd和callback函数,是事件处理的入口;
pending_list负责存放即将被处理的ril_event。


RIL_REQEUST执行结果的返回

消息处理完成,如何返回结果呢?
回想ril_commands.h里的定义:
{RIL_REQUEST_SETUP_DATA_CALL, dispatchDataCall, responseSetupDataCall}

responseSetupDataCall应该就是用来返回结果的,对应函数入口是responseFunction

typedef struct {int requestNumber;void (*dispatchFunction) (Parcel &p, struct RequestInfo *pRI);int(*responseFunction) (Parcel &p, void *response, size_t responselen);
} CommandInfo;

来搜索下调用responseFunction的地方

ril.cppRIL_onRequestComplete(RIL_Token t, RIL_Errno e, void *response, size_t responselen) {RequestInfo *pRI;int ret;int fd = s_ril_param_socket.fdCommand;size_t errorOffset;RIL_SOCKET_ID socket_id = RIL_SOCKET_1;pRI = (RequestInfo *)t;if (pRI->cancelled == 0) {Parcel p;p.writeInt32 (RESPONSE_SOLICITED);p.writeInt32 (pRI->token);errorOffset = p.dataPosition();p.writeInt32 (e);if (response != NULL) {//pRI->pCI->responseFunction并获得返回值ret = pRI->pCI->responseFunction(p, response, responselen);/* if an error occurred, rewind and mark it *///写入返回值到parcelif (ret != 0) {p.setDataPosition(errorOffset);p.writeInt32 (ret);}}//返回parcelsendResponse(p, socket_id);}
}

上一篇文章提过,RIL_onRequestComplete函数是s_rilEnv 的一个成员,RIL_init第一个参数就是s_rilEnv,可以推测是提供给qcril做回调函数之用:

rild.cstatic struct RIL_Env s_rilEnv = {RIL_onRequestComplete,RIL_onUnsolicitedResponse,RIL_requestTimedCallback
};
ril.h
struct RIL_Env {void (*OnRequestComplete)(RIL_Token t, RIL_Errno e,void *response, size_t responselen);void (*OnUnsolicitedResponse)(int unsolResponse, const void *data, size_t datalen, RIL_SOCKET_ID socket_id);void (*RequestTimedCallback) (RIL_TimedCallback callback,void *param, const struct timeval *relativeTime);
};

查找到OnRequestComplete的调用者是qcril.c 的qmi_ril_fw_send_request_response_epilog函数

if (token && !qcril_is_internal_token(token)){qcril_response_api[ instance_id ]->OnRequestComplete( token,resp_cause,resp_data,resp_data_len );}

找到了返回结果的地方,继续寻找返回数据的地方,既然监听到消息时是从fd读取出来消息,那么返回数据应该就是往fd写入数据。
回头再继续看pRI->pCI->responseFunction,还是以RIL_REQUEST_SETUP_DATA_CALL为例:
{RIL_REQUEST_SETUP_DATA_CALL, dispatchDataCall, responseSetupDataCall}

responseSetupDataCall调用了responseDataCallList,只是往parcel里面增加数据,并没有在这里向fd写入数据

static int responseDataCallList(Parcel &p, void *response, size_t responselen)
{RIL_Data_Call_Response_v11 *p_cur = (RIL_Data_Call_Response_v11 *) int i;for (i = 0; i < num; i++) {p.writeInt32((int)p_cur[i].status);p.writeInt32(p_cur[i].suggestedRetryTime);p.writeInt32(p_cur[i].cid);p.writeInt32(p_cur[i].active);writeStringToParcel(p, p_cur[i].type);writeStringToParcel(p, p_cur[i].ifname);writeStringToParcel(p, p_cur[i].addresses);writeStringToParcel(p, p_cur[i].dnses);writeStringToParcel(p, p_cur[i].gateways);writeStringToParcel(p, p_cur[i].pcscf);p.writeInt32(p_cur[i].mtu);}
}

函数返回到RIL_onRequestComplete,最后调用的函数是sendResponse,这里就是返回数据的地方:

static int
sendResponseRaw (const void *data, size_t dataSize, RIL_SOCKET_ID socket_id) {int fd = s_ril_param_socket.fdCommand;int ret;uint32_t header;header = htonl(dataSize);ret = blockingWrite(fd, (void *)&header, sizeof(header));ret = blockingWrite(fd, data, dataSize);
}

小结:
qcril.c完成处理后,调用RIL_onRequestComplete返回处理结果;
responseFunction将数据写入parcel;
RIL_onRequestComplete调用sendResponse,向socket的fd写入数据,返回到上层。


RIL_UNSOL_RESPONSE

这是一条RIL_UNSOL_RESPONSE消息:
{RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED, responseVoid, WAKE_PARTIAL},

RIL_UNSOL对应关系定义在ril_unsol_commands.h中,并存到s_unsolResponses数组:

ril.cpp
static UnsolResponseInfo s_unsolResponses[] = {
#include "ril_unsol_commands.h"
};//UnsolResponseInfo结构体
typedef struct {int requestNumber;int (*responseFunction) (Parcel &p, void *response, size_t responselen);WakeType wakeType;
} UnsolResponseInfo;

类似于CommandInfo结构体,UnsolResponseInfo结构体也有responseFunction,但没有dispatchFunction。RIL_onUnsolicitedResponse会调用responseFunction处理parcel的数据,然后通过sendResponse返回数据到上层

ril.cpp
void RIL_onUnsolicitedResponse(int unsolResponse, const void *data, size_t datalen){Parcel p;p.writeInt32 (RESPONSE_UNSOLICITED);p.writeInt32 (unsolResponse);ret = s_unsolResponses[unsolResponseIndex].responseFunction(p, const_cast<void*>(data), datalen);ret = sendResponse(p, soc_id);
}

调用这个RIL_onUnsolicitedResponse还是在qcril.c,qcril_send_unsol_response_epilog函数中:

qcril.cif ( param_ptr->instance_id < QCRIL_MAX_INSTANCE_ID ){qcril_response_api[ param_ptr->instance_id ]->OnUnsolicitedResponse( param_ptr->response_id, param_ptr->resp_pkt, param_ptr->resp_len );}

小结:
Unsolicited消息上报的路径类似于RIL_REQUEST,只是调用的函数不一样。


总结下:
本篇主要分析了RIL消息接收(RIL_REQUEST)和上报(RIL_UNSOL_RESPONSE),消息处理函数,还有eventloop中监听和分发的逻辑等代码。RILJ部分代码比较清晰好理解,有机会后面再补充。

下一篇具体分析一下SETUP_DATA_CALL是如何建立一个数据连接的。

(2)RIL简析(高通)——消息处理相关推荐

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

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

  2. 简谈高通Trustzone的实现

    从trust zone之我见知道,支持trustzone的芯片会跑在两个世界. 普通世界.安全世界,对应高通这边是HLOS,QSEE. 如下图: 如下是HLOS与QSEE的软件架构图 HLOS这两分为 ...

  3. 简谈高通Trustzone的实现【转】

    本文转载自:https://blog.csdn.net/hovan/article/details/42520879 从trust zone之我见知道,支持trustzone的芯片会跑在两个世界. 普 ...

  4. 个人以非货币性资产评估增值出资计征个人所得税问题简析——兼评国税总局 41 号通知和 20 号公告

    个人以非货币性资产评估增值出资计征个人所得税问题简析--兼评国税总局 41 号通知和 20 号公告  资本市场部 熊川 2015 年 3 月 30 日,财政部.国家税务总局发布<关于个人非货币性 ...

  5. Python4班平均成绩统计_郑州十一中2020届高考成绩简析(含新疆内高班)

    郑州十一中2020届高考成绩简析(含新疆内高班) 实际上十一中今年是小三甲中最为低调的一家,至今没有公布出红榜明细,陆续从家长口里传出的零星消息也都说明了十一中今年成绩不理想.笔者只能就十一中公布的简 ...

  6. 高通SDX62平台 MBIM搜网、查询信号等功能异常

    高通SDX62平台 MBIM搜网.查询信号等功能异常 1. 问题描述 按照高通SDX62平台产品规格,其支持RMNET.ECM.RNDIS.PPP.MBIM等拨号:但经测试,发现MBIM拨号功能正常, ...

  7. Android Handler与Looper原理简析

    一直感觉自己简直就是一个弱智,最近越来越感觉是这样了,真的希望自己有一天能够认同自己,认同自己. 本文转载于:https://juejin.im/post/59083d7fda2f60005d14ef ...

  8. SIGMOD 2021 论文简析:当公交网络连接满足通勤需求时的公共交通规划 Public Transport Planning

    SIGMOD-2021 论文简析:当公交网络连接满足通勤需求时的公共交通规划 - Public Transport Planning: When Transit Network Connectivit ...

  9. 高通SDX12:跨子系统数据共享实例分享

    高通SDX12:跨子系统数据共享实例分享 1. 实例背景 1.1 问题现象 1.2 初步分析 1.3 客户SDK版本显示SDK版本 svn号 1.4 SDK版本.模组厂商版本均显示SDK版本 svn号 ...

最新文章

  1. model存数据_Jepsen 测试框架在图数据库 Nebula Graph 中的实践
  2. FFT对信噪比的增益计算
  3. 四川阆中上演“万人同品腊八粥”
  4. KubeCon + CloudNativeCon北美2018年会议透明度报告:一项破纪录的CNCF活动
  5. 实战项目三:爬取QQ群中的人员信息
  6. wxWidgets:使用 Open Watcom 编译的 WxWidgets 的 DLL 版本
  7. EmEditor学习
  8. SpaceX再获美国宇航局价值1.525亿美元合同
  9. dnn模型 list index out of range_通过MalConv模型实现恶意软件的分类
  10. JetBrains IDEs
  11. Unity Physics.Raycast踩坑
  12. [转]Vue基于vue-quill-editor富文本编辑器使用心得
  13. 10-微信小程序商城 分类和产品 产品列表(微信小程序商城开发、小程序毕业设计、小程序源代码)(黄菊华-微信小程序开发教程)
  14. 判断tvs能抗住多少千伏浪涌的依据_手机电路浪涌防护和TVS应用
  15. oobar, foo, bar, baz和qux搅屎棍的含义
  16. LaTex 在图片上添加文字和公式
  17. Unity全面入门笔记6-常用数学类型
  18. 二、 Unity 游戏入门 创建角色 Main Character and First Script
  19. 选择 conforming 还是 non-conforming ?
  20. 第六章 网络学习相关技巧2(权重设置)

热门文章

  1. HTTPS代理的工作原理
  2. Linux查询sql显示井号,SQL井号标签的正则表达式表
  3. 1995年的一次访谈中,乔布斯谈到处于垄断地位的科技公司因为营销人员掌握了话语权,不再注重产品研发而衰败
  4. 一日一技:用Python做游戏有多简单
  5. 基于Docker使用HeuDiConv整理MRI数据(BIDS)
  6. 国内比较有实力的调查研究咨询公司
  7. Python正则表达式去掉字符串下划线末尾的纯数字
  8. 2021年高处作业登高架设证上机考试题库
  9. PCB的地与机壳(连接大地)为什么用阻容连接
  10. Broadcom 802.11n网络适配器,网络连接没有有效的ip配置问题解决