RIL代码分析

代码位于:android/hardware/ril

1 rild.c中的main()函数提供了rild的入口

首先,通过main函数的传参,cmdline,内核选项等方式获取rild.libpath和rild.libargs。

我们这里主要是:/system/lib/libreference-ril.so。

2 RIL_startEventLoop():建立消息队列的机制,开始事件的监听

函数RIL_startEventLoop()开启了一标识符为s_tid_dispatch的线程,线程的入口函数为eventLoop()。

ril_event_init()进行消息队列的初始化,主要是初始化读文件描述符集readfds,time_list,pending_list和watch_table;后面三个都是ril_event的结构体变量

structril_event {

structril_event *next;

structril_event *prev;

intfd;

intindex;

boolpersist;

structtimeval timeout;

ril_event_cb func;

void*param;

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

};

由定义可以看出它是一个双向链表。(重要的数据结构,后面会反复使用)

通过pipe()创建一个无名管道,对这个管道的读写描述符分别为:

ret = pipe(filedes);

...

s_fdWakeupRead = filedes[0];

s_fdWakeupWrite = filedes[1];ret = pipe(filedes);

...

s_fdWakeupRead = filedes[0];

s_fdWakeupWrite = filedes[1];

创建一个s_wakeupfd_event的ril_event,消息的fd为s_fdWakeupRead,消息的处理函数为processWakeupCallback();这个处理函数主要是读取管道内的消息。

将s_wakeupfd_event加入消息队列并触发消息队列:

ril_event_add(ev):将ev加入watch_table[i];将ev->fd加入读文件描述符集readFds。

triggerEvLoop():如果s_tid_dispatch线程结束,通过s_fdWakeupWrite写一个变量触发消息队列。

ril_event_loop():将读文件描述符集加入select()中,阻塞等待其变化。

processTimeouts():遍历time_list链表,查看是否有消息超时,如果超时移除链表。

processReadReadies():将watch_table[i]中非空消息加入等待链表pending_list;并且从select()阻塞列表中移除watch_table[i]。

firePending():从pending_list中取出一条消息并执行消息请求函数。

上面几个函数反复涉及到双向链表的结点加入和删除操作。

总之,RIL_startEventLoop()并没有真正的接收消息并处理,只是通过一个无名管道唤醒消息队列。

3 RIL_Init:建立起和硬件基带模块的交互

通过dlopen打开rilLibPath下动态库,并找到函数RIL_Init()函数的入口地址。

可以看出RIL_Init函数返回一个RIL_RadioFunctions的结构体指针:

typedefstruct{

intversion;/* set to RIL_VERSION */

RIL_RequestFunc onRequest;

RIL_RadioStateRequest onStateRequest;

RIL_Supports supports;

RIL_Cancel onCancel;

RIL_GetVersion getVersion;

} RIL_RadioFunctions;typedef struct {

int version; /* set to RIL_VERSION */

RIL_RequestFunc onRequest;

RIL_RadioStateRequest onStateRequest;

RIL_Supports supports;

RIL_Cancel onCancel;

RIL_GetVersion getVersion;

} RIL_RadioFunctions;

RIL_RequestFunc等是ril.h@hardware/ril/include/Telephony/中定义的函数指针,通过RIL_Init函数指针回调的形式给结构体中的函数赋值。

首先通过switch case获取libargs中的s_device_path;然后创建一个标识符为s_tid_mainloop,入口地址为mainLoop的线程。

mainLoop():对获取各种s_device_path作处理,如果是模拟器,创建qemud Socket的客户端,建立Socket连接;如果是usb虚拟串口终端设备,则打开这个终端设备并关闭其回显功能。(后面的内容我就基于usb虚拟串口终端设备分析)

at_open(fd,onUnsolicited):对打开的标准usb转串口设备进行各种ioctl操作,并新开启一个标识符s_tid_reader,入口为readerLoop的线程。

readerLoop()----->readline():读取usb转串口设备上的各种AT命令并返回一个命令常量指针

processLine():解析上面传回的AT命令。这个包括对命令类型的判断和相应命令的Handler处理。(这里的AT命令限于被动请求命令,例如网络状态,新信息通知等)

到这里我们就来分析下具体的Handler处理:

首先,从at_open()中将onUnsolicited赋值给函数指针类型的全局变量s_unsolHandler,在processLine()中直接调用handleUnsolicited()函数,继而handleUnsolicited()函数再调用s_unsolHandler(line)进行AT命令解析。所以最终的被动请求命令解析发生的onUnsolicited()函数中:

通过一系列if else语句对各种被动请求命令做判断。最终都会调到在ril.h中定义的一个函数指针

structRIL_Env {

void(*OnRequestComplete)(RIL_Token t, RIL_Errno e,

void*response, size_t responselen);

void(*OnUnsolicitedResponse)(intunsolResponse,constvoid*data,

size_t datalen);

void(*RequestTimedCallback) (RIL_TimedCallback callback,

void*param,conststructtimeval *relativeTime);

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

void (*RequestTimedCallback) (RIL_TimedCallback callback,

void *param, const struct timeval *relativeTime);

};

那么这个函数指针是如何被赋值的呢?

1)在RIL_Init()函数开始的时候有一个赋值操作:

这个s_rilenv是一个RIL_Env类型的全局结构体指针变量

2).作为参数传递过来的env,定义在rild.c中

s_rilenv = env;

staticstructRIL_Env s_rilEnv = {

RIL_onRequestComplete,

RIL_onUnsolicitedResponse,

RIL_requestTimedCallback

};s_rilenv = env;

static struct RIL_Env s_rilEnv = {

RIL_onRequestComplete,

RIL_onUnsolicitedResponse,

RIL_requestTimedCallback

};

这样我们就获取了RIl_Env结构体的函数指针具体地址。

从声明我们发现这个RIL_onUnsolicitedResponse定义在ril.cpp@/hardware/libril中

3)从RIL_onUnsolicitedResponce中我们还是不能获取具体被动请求命令(例如:RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT)的句柄。

4)后来发现,在RIL_onUnsolicitedResponce函数的开始有一个关于s_registerCalled的判断,如果没有调用过RIL_register()则返回。从此,我们可以断定在RIL_register()中对具体的函数指针进行了赋值。

4 RIL_register()

代码位于/hardware/libril/ril.cpp中。

将RIL_Init()函数返回的RIL_RadioFunctions结构体memcpy到s_callbacks中(后面使用)

并对两个大的结构体数组进行自检:

typedefstruct{

intrequestNumber;

void(*dispatchFunction) (Parcel &p,structRequestInfo *pRI);

int(*responseFunction) (Parcel &p,void*response, size_t responselen);

} CommandInfo;

typedefstruct{

intrequestNumber;

int(*responseFunction) (Parcel &p,void*response, size_t responselen);

WakeType wakeType;

} UnsolResponseInfo;

staticCommandInfo s_commands[] = {

#include "ril_commands.h"

};

staticUnsolResponseInfo s_unsolResponses[] = {

#include "ril_unsol_commands.h"

};typedef struct {

int requestNumber;

void (*dispatchFunction) (Parcel &p, struct RequestInfo *pRI);

int(*responseFunction) (Parcel &p, void *response, size_t responselen);

} CommandInfo;

typedef struct {

int requestNumber;

int (*responseFunction) (Parcel &p, void *response, size_t responselen);

WakeType wakeType;

} UnsolResponseInfo;

static CommandInfo s_commands[] = {

#include "ril_commands.h"

};

static UnsolResponseInfo s_unsolResponses[] = {

#include "ril_unsol_commands.h"

};

定义的过程相当于给每个request的确定了操作句柄。(确定了上面的推断)

创建一个s_fdListen的socket与rild连接并监听;将s_fdListen加入消息队列;

创建一个s_fdDebug的socket与rild-debug连接并监听;将s_fdDebug加入消息队列

在这里我们可以总结一下RIL中的消息队列:

1.s_fdListen:与上层的rild socket通信;

2.s_fdDebug:与上层的rild-debug通信;

3.s_fdWakeup_event:无名管道,用于队列的唤醒;

这三个描述符最终都被加入readFds,通过selects()函数阻塞等待。

接着上面分析:

通过s_fdCommand = accept(s_fdListen, (sockaddr *) &peeraddr, &socklen)获得第一个连接请求之后创建了一个新的socket:s_fdCommand。

通过调用ril_event_set()和rilEventAddWakeup()将新创建的socket加入消息队列。新消息的处理函数为processCommandsCallback()

然后再调用processCommandBuffer()对命令进行解析(解析的过程类似于刚刚分析的RIL_Init中对虚拟串口设备发送的状态的解析。)

定义一个Parcel用来装载上层传来的主动请求命令,对传来的命令自检(这里的主动请求命令定义在全局结构体数组变量s_commands中)。然后通过调用

对于90多个主动请求命令的处理句柄,这里不一一分析了。

但每个dipatchFunction()函数最终都会调用

这个s_callbacks是一个全局变量的RIL_RadioFunctions变量。

(RIL_Register开始的时候被初始化)

onRequest()@/hardware/ril/reference-ril/reference-ril.c:主动请求命令的最主要函数。

通过switch和大量的case对各种主动请求命令处理。例如:

pRI->pCI->dispatchFunction(p, pRI);

然后requestSendSMS()---->at_send_command_sms()--->at_send_command_full()----->at_send_command_full_nolock------>writeline (command)--->written = write (s_fd, s + cur, len - cur)

说明一下:全局变量s_fd在RIL_Init的at_open()函数中被赋值(即USB虚拟串口设备描述符)

另外,执行完命令发送后必须要执行

RIL_onRequestComplete(t, RIL_E_SUCCESS, &response, sizeof(response))进行状态返回。

我们这里对RIL_Regisger进行下总结,这部分主要是创建一个socke与上层进行通信,接受来自上层的主动请求命令,然后Vendor-ril.将这些主动命令转化成AT命令发送到USB虚拟串口终端。

android ril 模拟,Android RIL代码详细分析相关推荐

  1. 【SemiDrive源码分析】【X9芯片启动流程】30 - AP1 Android Kernel 启动流程 start_kernel 函数详细分析(一)

    [SemiDrive源码分析][X9芯片启动流程]30 - AP1 Android Kernel 启动流程 start_kernel 函数详细分析(一) 一.Android Kernel 启动流程分析 ...

  2. android ListView 九大重要属性详细分析

    2019独角兽企业重金招聘Python工程师标准>>> android ListView 九大重要属性详细分析 1.android ListView 一些重要属性详解,兄弟朋友可以参 ...

  3. Blueprint代码详细分析-Android10.0编译系统(七)

    摘要:Blueprint解析Android.bp到ninja的代码流程时如何走的? 阅读本文大约需要花费18分钟. 文章首发微信公众号:IngresGe 专注于Android系统级源码分析,Andro ...

  4. seq2seq翻译任务代码详细分析

    文章目录 题目 代码 总结 题目 ''' Description: seq2seq代码详细分析 Autor: 365JHWZGo Date: 2021-12-16 19:59:38 LastEdito ...

  5. linux加密模块,Linux加解密支持模块代码详细分析之演示验证方案1实验代码及结果...

    原标题:Linux加解密支持模块代码详细分析之演示验证方案1实验代码及结果 3.1.5.实验代码 #include #include #include #include #include #inclu ...

  6. [细读经典]Megatron论文和代码详细分析(1)

    [细读经典]Megatron论文和代码详细分析(1) 导航: 迷途小书僮:[细读经典]Megatron论文和代码详细分析(2)102 赞同 · 41 评论文章正在上传-重新上传取消 前言 作为一款支持 ...

  7. Android RIL代码详细分析

    RIL代码分析 代码位于:android/hardware/ril 1 rild.c中的main()函数提供了rild的入口 首先,通过main函数的传参,cmdline,内核选项等方式获取rild. ...

  8. 全志 android 编译,全志A20启动代码流程分析 ——Android

    现在的CPU都固化了内部 ROM,内部 ROM中有一般都有一段程序,一般有如下几个功能: 1,初始化,部分外设,如USB,SDCARD 2,初始化DDR(内存)和NandFlash 3,加载boot( ...

  9. 全志android 编译,全志A20启动代码流程分析 ——Android

    现在的CPU都固化了内部 ROM,内部 ROM中有一般都有一段程序,一般有如下几个功能: 1,初始化,部分外设,如USB,SDCARD 2,初始化DDR(内存)和NandFlash 3,加载boot( ...

最新文章

  1. 杰奇数据库mysql_杰奇模板出现Unable to save result set in…可尝试修复数据库
  2. 2. JSF---托管Bean
  3. 014_TimePicker时间选择器
  4. linspace--创建线性等分向量
  5. 数字孪生技术从概念走向实际应用
  6. LeetCode139:Word Break
  7. 使用PYQT5打开海康威视工业相机并获取图像进行显示
  8. Frank and Hall
  9. Office XP 试用感受
  10. 蚂蚁呀嘿 App,七天就下架了!
  11. 利用Vue制作一个简单的走马灯
  12. Springcloud使用全局捕获异常管理接口异常
  13. HDU 5804 Price List(水~)
  14. v2ray各种版本+一键搭建+bbr提速脚本
  15. android 7.0 手机拍照裁剪问题
  16. 程序员应该保持危机感
  17. android布局优化!Android动态换肤实现原理解析,灵魂拷问
  18. 元宇宙的本质特征是五大融合
  19. 简析NFT交易平台的发展历程及4F评估模型
  20. 微信公众号提示:redirect_uri 参数错误

热门文章

  1. python实现跨进程(跨py文件)通信
  2. TrueTpye字体和postscript字体
  3. JS--JavaScript访问节点(childNodes、parentNode、firstChild、lastChild、nextSibling、previousSibling)
  4. 马光远:“全球最贵股市”是一针清醒剂
  5. 橡胶促进剂MBTS(DM)市场现状及未来发展趋势
  6. Linux命令-按照与使用(10)linux清空历史命令(history)
  7. javascript code all (转转)
  8. Python为何被其父亲抛弃?
  9. cpu和gpu(cpu和gpu温度一般在多少)
  10. 第1章 矿物加工学概述