Android+QC modem手机通信模块技术分析 (2)

2.3 RIL软件分析

模块 功能描述
rild 主入口 解析启动参数(如 -l 指定库路径),通过 dlopen 加载 Vendor RIL 动态库,初始化事件循环,并建立与 Java 层的 Socket 通信通道(rild 与 rild-debug 两个 Socket)。
事件循环 (EventLoop) 基于 select/epoll 的单线程事件驱动核心。管理三类事件:定时器事件(ril_timer)、文件描述符事件(如 AT 命令通道的读写)、待处理回调队列。所有来自 Vendor RIL 的主动上报和响应都需经过它调度。
Vendor RIL 动态库 由芯片厂商(如高通、MTK)实现,封装与 Modem 的具体通信协议(AT 指令、共享内存 QMI、MBIM 等)。它必须实现 RIL_Init 入口函数,并向 RILD 提供一组回调函数指针(RIL_RadioFunctions)。

LibRIL和Vendor RIL通过接口分离,共同构成了RIL的核心。

LibRIL是Android原生代码的一部分(libril.so),与rild静态链接。它位于rild进程中,负责与上层Java框架的RILJ通过Socket通信。其核心是ril_event事件循环,同时,LibRIL定义了RIL_Env回调接口,并实现RIL_register来注册Vendor RIL提供的函数指针。

Vendor RIL是由芯片厂商(如高通、MTK)提供的动态库(.so)。它被rild通过dlopen动态加载。Vendor RIL需要实现并导出RIL_Init函数和RIL_RadioFunctions下行接口,并将Android框架层的请求转换为Modem能识别的AT指令或QMI消息。其内部通常会创建一个线程(如mainLoop),专门负责监听和读写Modem端口。

特性 LibRIL (libril.so) Vendor RIL (如 libqcril.so)
提供者 Android 开源项目 (AOSP) 的一部分 芯片厂商或设备制造商
主要功能 与上层Java框架通信、事件循环、请求分发 与Modem硬件通信、协议转换
提供的接口 RIL_Env (供Vendor RIL回调) RIL_Init (入口函数), RIL_RadioFunctions (供LibRIL调用)
关键函数/组件 RIL_startEventLoop, RIL_register, ril_event_loop onRequest, mainLoop
与Modem的交互 不直接交互,通过Vendor RIL进行 直接交互,通过串口/USB发送AT指令或QMI等协议消息

2.3.1 RIL 代码结构与层级关系

整个 RIL 主要分为三个部分,它们之间通过函数指针或动态加载的方式交互。

模块文件 编译产物 主要作用
rild.c rild守护进程 唯一的入口点,负责解析参数、加载动态库等顶层初始化工作。
ril.cpp ril_event.cpp libril.so LibRIL,核心逻辑层。建立事件循环,管理上层通信,协调下层通信。
reference-ril.c atchannel.c libreference-ril.so Vendor RIL,厂家适配层。实现具体的AT命令收发,与Modem硬件交互。

从代码依赖关系上看,rild是主程序,通过dlopen方式加载libreference-ril.so;而libril.so是rild编译时就链接的依赖库。

2.3.2 RIL的核心代码结构

核心概念 主要作用 代码表现 / 结构
命令映射 将请求 ID (RIL_REQUEST_*) 映射到对应处理函数 s_commands (在 ril_commands.h) 静态数组,存储dispatchFunction和responseFunction。
主动上报映射 将主动上报 ID (RIL_UNSOL_*) 映射到封装函数 s_unsolResponses (在 ril_unsol_commands.h) 静态数组。
事件列表 管理所有监听的文件描述符(fd)事件 watch_table:select监听的所有fd;pending_list:触发待处理的事件;timer_list:定时器事件。
请求Token 异步请求的唯一标识,用于匹配响应 RIL_Token,本质是RequestInfo结构体指针,由LibRIL传入Vendor RIL。
跨线程唤醒 非主线程唤醒eventLoop线程的机制 notify pipe:s_fdWakeupWrite唤醒,s_fdWakeupRead被epoll/select监听。

2.3.3 代码阅读分析

模块 文件 核心内容 与 rild.c 的关系
LibRIL 核心 ril.cpp 实现 RIL_startEventLoop、RIL_register、RIL_onRequestComplete 等。定义 s_commands 和 s_unsolResponses。 rild.c 调用这些函数,并依赖其中的全局状态。
事件循环 ril_event.cpp (或 ril_event_epoll.cpp) 实现 ril_event_loop、watch_table、pending_list、timer_list,以及 select/epoll 多路复用。 RIL_startEventLoop 会创建线程并调用 ril_event_loop。
Vendor RIL 示例 reference-ril.c 一个简单的 Vendor RIL 实现,展示如何实现 RIL_Init、onRequest 以及如何调用 RIL_Env 回调。 rild.c 通过 dlopen 加载此类库,理解 RIL_Init 的实现就能明白双向通信。
RIL 头文件 include/telephony/ril.h 定义所有 RIL_REQUEST_、RIL_UNSOL_、RIL_RadioFunctions、RIL_Token 等。 rild.c 中使用的结构体类型均来自此头文件。
Socket 通信 ril.cpp 中的 listenCallback、processCommandBuffer 负责与 RILJ 的 socket 交互,读取请求、发送响应。 事件循环检测到 socket 可读时,调用这些函数。
系统属性 libcutils 中的 property_get 读取 rild.libpath 等属性。 rild.c 在未通过 -l 指定时依赖此接口。
C 复制代码
推荐阅读顺序
先看 ril.h:了解常量和核心数据结构。

再看 ril.cpp 中的 RIL_register 和 RIL_startEventLoop:
理解 LibRIL 如何初始化事件循环和 socket 监听。

阅读 ril_event.cpp:掌握事件循环机制(select/epoll、三个队列)。

回到 rild.c:此时可以清楚知道 main 每一步调用的底层实现。

阅读一个简单的 Vendor RIL 实现(如 reference-ril.c):
理解 RIL_Init 内部如何创建 mainLoop线程、打开串口、处理请求。

最后阅读复杂厂商实现(如高通 libqcril.so 的对应源码):
这是实际手机中的代码,但核心思想与 reference-ril 一致,只是增加了异步队列、QMI 等高级特性。
C 复制代码
android code:
hardware/ril/rild/rild.c
hardware/ril/libril/ril.cpp
hardware/ril/libril/ril_event.cpp
hardware/ril/reference-ril/reference-ril.c
hardware/ril/include/telephony/ril.h

Qualcomm libqcril.so code:
/vendor/qcom/proprietary/qcril/qcril_qmi

2.3.4 ril.h

C 复制代码
hardware/ril/include/telephony/ril.h

ril.h是Android系统中无线接口层的定义头文件,它定义了一个与具体Modem无关的抽象接口,是整个RIL框架的核心与纽带。所有主要组件(rild守护进程、libril.so和Vendor RIL)都依赖这个文件进行通信。

2.3.4.1 核心数据结构

2.3.4.1.1 (RIL_RadioFunctions) ------ 厂商回调表(下发接口)

包含了 Vendor RIL 库必须实现的一系列函数指针。rild守护进程通过加载Vendor RIL库并调用RIL_Init函数来获取这个结构体的实例。

C++ 复制代码
typedef struct {
    int version;                 // RIL接口的版本号,用于兼容性控制[reference:9]
    RIL_RequestFunc onRequest;   // 处理来自上层主动请求(solicited commands)[reference:10]
    RIL_RadioStateRequest onStateRequest; // 查询当前无线射频状态
    RIL_Supports supports;       // 查询Vendor RIL是否支持某个特定的请求代码
    RIL_Cancel onCancel;         // 取消一个正在进行的请求
    RIL_GetVersion getVersion;   // 获取Vendor RIL实现的版本字符串
} RIL_RadioFunctions; 

RIL_RadioFunctions 是厂商 RIL 库暴露给 libril.so 的核心函数表。它是一个"函数指针集合",就像一张跳转表。libril 不需要知道厂商 RIL 内部的具体实现,只需通过这个结构体就能调用到厂商实现的各项功能。

设计模式:函数表(Function Table / Jump Table)

  • 这是一个纯虚函数表的C语言实现。

  • 所有成员都是函数指针,指向厂商RIL库中具体的实现。

  • libril 拿到这个表后,无需关心厂商内部实现,只需通过表调用函数即可。

  • 方向:libril → 厂商RIL(上层调用厂商实现)

  • 作用:厂商RIL通过RIL_Init返回这个结构体,libril通过它调用厂商的具体函数。

成员 类型定义(来自 ril.h) 参数说明 返回值 含义
version int RIL接口的版本号 接口版本号,必须设置为 RIL_VERSION 宏。用于保证 libril 与厂商 RIL 的版本兼容。
onRequest typedef void (*RIL_RequestFunc)(int request, void *data, size_t datalen, RIL_Token t); request: 命令ID(如 RIL_REQUEST_DIAL) data: 命令参数结构体指针 datalen: 参数大小 t: 令牌,用于匹配响应 void 函数指针:void (*)(int request, void *data, size_t datalen, RIL_Token t)。核心入口:上层所有主动请求(拨号、发短信等)最终都通过这个函数传递给厂商 RIL 处理。
onStateRequest typedef RIL_RadioState (*RIL_RadioStateRequest)(void); RIL_RadioState 枚举(如 RADIO_STATE_ON) 函数指针:RIL_RadioState (*)(void)。用于查询当前 Modem 的射频状态(如无服务、正在注册、已注册等)。
supports typedef int (*RIL_Supports)(int requestCode); requestCode: 命令ID 1 支持,0 不支持 函数指针:int (*)(int requestCode)。用于询问厂商 RIL 是否支持某一个特定的请求命令(如 RIL_REQUEST_DIAL)。
onCancel typedef void (*RIL_Cancel)(RIL_Token t); t: 要取消的请求令牌 void 函数指针:void (*)(RIL_Token t)。当上层需要取消某个正在执行的请求时调用。
getVersion typedef const char *(*RIL_GetVersion)(void); 版本字符串指针(静态或常量) 函数指针:const char ()(void)。返回厂商 RIL 的实现版本字符串(用于调试或日志)。
C 复制代码
RIL_RequestFunc onRequest: 所有主动请求的入口,当上层发起拨号、发送短信等操作时会被调用。

typedef void (*RIL_RequestFunc) (int request, void *data, size_t datalen, RIL_Token t);


request: 请求类型(如 RIL_REQUEST_DIAL),常见种类有:
SIM/卡相关操作(11个)
呼叫状态与处理(16个,包括拨号、接听、静音等)
网络状态查询(4个)
网络设置(12个,包括呼叫禁止、呼叫转移、网络选择等)
短信处理(3个)
PDP连接(4个)
电源与复位(2个)
补充服务(5个)
厂商自定义(4个)
data: 指向该请求特有参数的指针(如拨打的号码)。
t: 一个不透明的令牌(Token),用于在完成响应时匹配请求。
C 复制代码
typedef RIL_RadioState (*RIL_RadioStateRequest)();
typedef int (*RIL_Supports)(int requestCode);
typedef void (*RIL_Cancel)(RIL_Token t);
typedef const char * (*RIL_GetVersion) (void);


RIL_RadioStateRequest onStateRequest: 用于查询Modem的当前状态(例如,无服务、正在搜索网络等)。
RIL_Supports supports: 用于检查Vendor RIL是否支持特定的请求代码。
RIL_Cancel onCancel: 用于取消一个已发出但尚未完成的请求。
RIL_GetVersion getVersion: 用于获取Vendor RIL实现的版本字符串。
2.3.4.1.2 RIL_Init ------ 厂商库的入口函数
C 复制代码
const RIL_RadioFunctions *RIL_Init(const struct RIL_Env *env, int argc, char **argv);
  • RIL_Init 是厂商RIL库的唯一导出函数。rild 通过 dlopen 动态加载厂商库后,用 dlsym 找到这个函数并调用它。

  • 它接受一个 RIL_Env 结构体指针,这个结构体是 libril 提供给厂商的回调环境(即反向接口)。

  • 调用者:rild守护进程

  • 作用:动态加载厂商RIL库后,调用RIL_Init,传入RIL_Env(由libril提供),厂商RIL保存该环境指针,并返回自己的RIL_RadioFunctions函数表。

2.3.4.1.2.1 其核心职责
  1. 接收 RIL_Env 回调环境:保存 env 指针,以便后续通过 env->OnRequestComplete 等回调将 Modem 的响应或主动上报事件返回给 libril。

  2. 初始化 Modem 硬件接口:打开串口、初始化 QMI 客户端、配置 AT 通道等。

  3. 返回函数表:将一个填充好的 RIL_RadioFunctions 结构体指针返回给调用者(rild),该结构体包含了厂商 RIL 实现的核心函数指针(如 onRequest、onStateRequest 等)。

参数 类型 说明
env const struct RIL_Env * 回调环境指针,由 libril.so 提供。厂商 RIL 必须保存此指针,在请求处理完成或 Modem 主动上报事件时,通过 env 中的函数回调 libril。常见回调函数: env->OnRequestComplete(...):异步响应 env->OnUnsolicitedResponse(...):主动上报
argc int 命令行参数个数,通常由 rild 启动脚本(如 init.rc)传递,例如 -d /dev/ttyS0 等
argv char ** 命令行参数数组,内容与 argc 配套

返回值

  • 类型:const RIL_RadioFunctions *

  • 含义:指向厂商 RIL 提供的函数表(通常是一个静态全局的 RIL_RadioFunctions 结构体变量)的常量指针。

  • 生命周期:该结构体的内容在整个 RIL 运行期间必须保持有效(通常定义为 static const 或全局变量)。

调用时序

  1. 依赖注入:libril 通过 RIL_Env 将回调函数"注入"到厂商 RIL,厂商 RIL 无需关心 libril 内部细节。

  2. 对称性:RIL_Init 返回 RIL_RadioFunctions(供 libril 调用),同时接收 RIL_Env(供厂商回调),形成双向接口。

  3. 单例模式:每个 RIL 实例(可能支持多 SIM)调用一次 RIL_Init,返回的函数表在整个进程生命周期中保持不变。

  4. 版本兼容:s_callbacks.version 必须设置为 RIL_VERSION,libril 会根据该版本决定是否调用某些新功能。

C 复制代码
RIL_Init 由厂商 RIL 库实现,libril 并不包含该函数。
rild 必须先将函数表通过 RIL_register 注册到 libril后,libril 才会开始监听 Socket 并处理请求。
RIL_Env 是 libril 提供的,厂商 RIL 接收并保存。

RIL_Init 是 Android RIL 架构中厂商适配层的"入口点"。它通过简洁的接口完成了双向依赖的解耦:

  • libril 获得厂商的函数表(RIL_RadioFunctions)

  • 厂商获得 libril 的回调环境(RIL_Env)

这种设计使得 Android 系统能够在不修改框架代码的前提下,适配不同厂商、不同硬件的 Modem。

2.3.4.1.2.2 RIL_Env ------ libril 提供的回调环境(上报表)
C 复制代码
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);
};
  • 方向:厂商RIL → libril(厂商调用libril提供的回调)

  • 作用:厂商RIL在请求处理完成或接收到Modem主动事件时,通过RIL_Env中的函数指针将结果或事件上报给libril。

  • 厂商RIL通过 env->OnRequestComplete 上报请求结果。

  • 通过 env->OnUnsolicitedResponse 上报Modem主动事件。

2.3.4.1.2.3 函数含义
  • env:RIL_Env 结构体指针。RIL_Env 是 libril 提供给厂商 RIL 的回调环境,里面包含 OnRequestComplete、OnUnsolicitedResponse 等函数指针。厂商 RIL 利用这些回调把结果或事件传回 libril。

  • argc, argv:命令行参数(通常由 rild 启动脚本传递,如设备节点路径等)。

  • 返回值:返回一个常量指针,指向厂商 RIL 自己实现的 RIL_RadioFunctions 结构体实例(通常是静态全局变量)。该结构体中的函数指针就是厂商 RIL 实际功能函数。

  • 典型实现(厂商库内部):

C++ 复制代码
// reference-ril.c 
static const RIL_RadioFunctions s_callbacks = {
    RIL_VERSION,        // .version = RIL_VERSION
    onRequest,          // .onRequest = onRequest
    currentState,       // .onStateRequest = currentState
    onSupports,         // .supports = onSupports
    onCancel,           // .onCancel = onCancel
    getVersion          // .getVersion = getVersion
};
const RIL_RadioFunctions *RIL_Init(const RIL_Env *env, int argc, char **argv) {
    // 保存 env 到全局变量,供后续回调使用
    g_env = env;
    // 打开串口、初始化Modem等
    return &s_funcs;
}
2.3.4.1.2.4 数据结构交互图(初始化阶段)
2.3.4.1.2.5 RIL_Init 与 RIL_Env 关系的时序图与结构图

时序图:初始化与回调流程

结构关系图:双向依赖

元素 角色 方向
RIL_Env libril 提供的回调接口 libril → rild → VendorRIL
RIL_Init 厂商 RIL 的入口函数,接收 env rild 调用 VendorRIL
g_env 厂商 RIL 内部静态变量,保存 env VendorRIL 内部存储
RIL_RadioFunctions 厂商 RIL 提供的函数表 VendorRIL → rild → libril

核心关系:

RIL_Init 是桥梁,它让 libril 的回调环境 (RIL_Env) 流入厂商 RIL,同时让厂商 RIL 的函数表 (RIL_RadioFunctions) 流回 libril,形成双向通信通道。

2.3.4.1.3 s_commands\[\]

s_commands\[\] 数组 + ril_commands.h 的组合是 Android RIL 中命令路由的核心机制。它将协议号与具体的业务处理函数绑定,使得 libril 能够以统一的方式处理所有请求,同时保持代码的简洁和可扩展性。

C 复制代码
typedef struct {
  int requestNumber;                                    *// 请求号(如 RIL_REQUEST_DIAL)*
  void (*dispatchFunction) (Parcel &p, struct RequestInfo *pRI);        *// 分发函数指针*
  int(*responseFunction) (Parcel &p, void *response, size_t responselen);*// 响应函数指针*
} CommandInfo;

/*
requestNumber:与 ril.h 中定义的请求宏对应的整数值,用于标识命令类型。
dispatchFunction:指向分发函数,负责将来自 RILJ 的 Parcel 数据解析成 C 结构体,并调用厂商 RIL 的 onRequest。
responseFunction:指向响应函数,负责将 Modem 返回的原始数据(response)封装成 Parcel 格式,以便通过 Socket 发送回 RILJ。
*/

/** Index == requestNumber */
static CommandInfo s_commands[] = {
#include "ril_commands.h"
};
/*
s_commands 是一个全局静态数组,元素类型为 CommandInfo。
#include "ril_commands.h" 是预处理指令,会将 ril_commands.h 文件的内容直接文本插入到数组初始化位置。
注释 /** Index == requestNumber */ /*表明:该数组的索引 与 requestNumber 相等(或者存在某种直接映射关系,例如 index = requestNumber - RIL_FIRST_COMMAND_ID)。
*/

//ril_commands.h
......
    {RIL_REQUEST_DIAL, dispatchDial, responseVoid},
    {RIL_REQUEST_GET_IMSI, dispatchStrings, responseString},
    {RIL_REQUEST_HANGUP, dispatchInts, responseVoid},
......

/*
每个花括号内的三个元素依次对应 CommandInfo 的三个成员:
请求宏(如 RIL_REQUEST_DIAL)
分发函数名(如 dispatchDial)
响应函数名(如 responseVoid)
预处理器展开后,s_commands[] 数组的内容就变成了这些初始化器。
*/

这段代码是 Android RIL 中 libril.so 的核心命令映射表,用于将上层的请求号(如 RIL_REQUEST_DIAL)映射到对应的分发函数(dispatch function) 和响应函数(response function)。

dispatchFunction 的职责

  • 从 Parcel 中读取参数(例如电话号码 等)。

  • 将参数填充到对应的结构体(如 RIL_Dial)中。

  • 调用 s_callbacks.onRequest(requestNumber, &struct, size, pRI)。

  • 负责释放动态分配的内存(如字符串)。

responseFunction 的职责

  • 被 RIL_onRequestComplete 调用。

  • 将 response 指针指向的原始数据(如字符串、整数数组)写入 Parcel。

  • 将 Parcel 通过 Socket 发送给 RILJ。

  • 返回值通常表示操作状态(0 成功,负数错误)。

常见的响应函数

  • responseVoid:不携带数据,只返回成功/失败状态。用于不需要返回数据的情况(如拨号、挂断)。只返回成功/失败状态。

  • responseString:返回一个以 \0 结尾的字符串。返回一个字符串(如 IMSI)。

  • responseStrings:返回多个字符串(以 NULL 结尾的数组)(如运营商列表)。

  • responseInts:返回整数数组(如信号强度)。

  • responseCallList:返回通话列表(用于 RIL_REQUEST_GET_CURRENT_CALLS)。

这些函数在 ril.cpp 中实现,内部通过 sendResponse 将数据打包成 Parcel 并通过 Socket 发送给 RILJ。

数据映射关系简图

工作流程

从接收到请求 到 调用响应函数 的完整流程:

  • CommandInfo 是一个命令路由条目,将请求号、解析函数、响应函数绑定在一起。

  • s_commands\[\] 数组通过 #include "ril_commands.h" 的方式初始化,所有命令的路由信息集中维护。

  • 查找方式:通常使用 requestNumber - RIL_FIRST_COMMAND_ID 作为数组下标,直接定位到对应的 CommandInfo。

  • 分工明确:dispatchFunction 负责"向下传递",responseFunction 负责"向上回传"。

这种设计使得 RIL 层能够以统一、高效的方式处理数十种不同类型的电话、短信、网络等请求,是 Android 电话栈中一个经典的"表驱动"编程范例。

2.3.4.1.4 RIL_register ------ 注册函数表到 libril
C 复制代码
void RIL_register (const RIL_RadioFunctions *callbacks);

RIL_register 是 libril.so 提供的函数,不是厂商库导出的。rild 在得到厂商的 RIL_RadioFunctions 后调用它,将这个函数表注册到 libril 内部全局变量(如 s_callbacks)中。

  • 它把厂商函数表保存下来,使得 libril 内部在处理请求时可以调用 s_callbacks.onRequest(...) 来传递请求给厂商。

  • 同时,RIL_register 还会创建 RILJ 通信的 Socket,并启动事件循环,让整个 RIL 开始工作。

注册后的数据流向

2.3.4.1.5 数据结构关系图
2.3.4.1.6 数据走向与调用时序图

阶段1:初始化(建立双向通道)

阶段2:主动请求(上层 → Modem,以拨号为例)

阶段3:主动上报(Modem → 上层,如来电)

方向 调用起点 调用终点 使用的接口
下发(主动请求) RILJ → Socket → libril 厂商RIL RIL_RadioFunctions.onRequest
上报(请求响应) 厂商RIL libril → Socket → RILJ RIL_Env.OnRequestComplete
上报(主动事件) 厂商RIL libril → Socket → RILJ RIL_Env.OnUnsolicitedResponse

时序说明

  1. rild 动态加载厂商RIL库,获取 RIL_Init 函数入口。

  2. rild 调用 RIL_Init,传入 RIL_Env 回调环境。

  3. 厂商RIL 保存 env 并返回自己的 RIL_RadioFunctions 结构体指针。

  4. rild 将函数表指针传给 libril 的 RIL_register 函数。

  5. libril 保存函数表,创建 Socket 监听,启动事件循环,准备处理请求。

  6. 运行阶段,当有请求(如拨号)到来时,libril 通过保存的函数表指针调用厂商的 onRequest 函数。

  7. 厂商RIL 处理完成后,通过之前保存的 env 回调 OnRequestComplete 将结果返回给 libril。

  8. libril 将结果通过 Socket 返回给 RILJ。

2.3.4.1.7 数据流向图
2.3.4.1.8 设计思想
元素 角色 提供方 消费方 设计模式
RIL_RadioFunctions 函数表(抽象接口) 厂商RIL实现 libril 接口/函数表
RIL_Init 工厂函数 厂商RIL rild 工厂模式
RIL_register 注册函数 libril rild 依赖注入
RIL_Env 回调环境 libril 厂商RIL 观察者/回调
  1. 依赖反转:libril 只依赖 RIL_RadioFunctions 接口,不依赖具体厂商实现。

  2. 控制反转:libril 提供 RIL_Env 回调环境,厂商RIL主动调用这些回调将结果返回。

  3. 对称性:RIL_RadioFunctions 负责向下调用,RIL_Env 负责向上回调,形成完整双工通道。

  4. 单一全局状态:厂商RIL内部通常保存 g_env 静态指针;libril 内部保存 s_callbacks 静态函数表。

这种设计使得 Android RIL 层可以轻松替换不同 Modem 方案,而无需修改 libril 和上层 Java 代码。

2.3.4.2 错误处理与版本控制

错误码

  • RIL_Errno:所有操作的标准错误码(如 RIL_E_SUCCESS、RIL_E_GENERIC_FAILURE、RIL_E_RADIO_NOT_AVAILABLE 等)。

  • RIL_RadioState:无线状态(无服务、正在搜索、已注册等)。

  • RIL_CallState:通话状态(空闲、拨出、振铃、通话中等)

这些错误码有助于提升问题排查效率。

版本控制

ril.h内部通过一系列宏定义(如 RIL_VERSION、RIL_VERSION_12 等)对接口版本号进行管理,确保了接口的向前和向后兼容性。

2.3.4.3 请求和响应的宏定义

ril.h使用宏为所有请求和事件定义了唯一的标识符。这些宏分为两类:

  • 主动请求命令 (Solicited Commands): 前缀为 RIL_REQUEST_。这些是上层(Framework)主动发起的操作请求,如 RIL_REQUEST_DIAL(拨打电话)、RIL_REQUEST_HANGUP(挂断电话)等。
Markdown 复制代码
/**
 * RIL_REQUEST_DIAL
 *
 * Initiate voice call
 *
 * "data" is const RIL_Dial *
 * "response" is NULL
 *
 * This method is never used for supplementary service codes
 *
 * Valid errors:
 *  SUCCESS
 *  RADIO_NOT_AVAILABLE (radio resetting)
 *  DIAL_MODIFIED_TO_USSD
 *  DIAL_MODIFIED_TO_SS
 *  DIAL_MODIFIED_TO_DIAL
 *  GENERIC_FAILURE
 */
#define RIL_REQUEST_DIAL 10
  • 被动请求命令 (Unsolicited Responses): 前缀为 RIL_UNSOL_。这些是Modem主动上报的事件,如 RIL_UNSOL_CALL_STATE_CHANGED(通话状态改变)、RIL_UNSOL_NEW_SMS(收到新短信)等
C++ 复制代码
/**
 * RIL_UNSOL_CALL_RING
 *
 * Ring indication for an incoming call (eg, RING or CRING event).
 * There must be at least one RIL_UNSOL_CALL_RING at the beginning
 * of a call and sending multiple is optional. If the system property
 * ro.telephony.call_ring.multiple is false then the upper layers
 * will generate the multiple events internally. Otherwise the vendor
 * ril must generate multiple RIL_UNSOL_CALL_RING if
 * ro.telephony.call_ring.multiple is true or if it is absent.
 *
 * The rate of these events is controlled by ro.telephony.call_ring.delay
 * and has a default value of 3000 (3 seconds) if absent.
 *
 * "data" is null for GSM
 * "data" is const RIL_CDMA_SignalInfoRecord * if CDMA
 */
#define RIL_UNSOL_CALL_RING 1018

2.3.4.4 业务结构体

功能域 结构体名称 用途
拨号 RIL_Dial 包含拨打的号码、CLIR 设置等。
通话控制 RIL_Call 记录通话的索引、状态、号码、类型等。
短信 RIL_SMS_WriteArgs 写短信时的参数(状态、PDU 等)。
数据连接 RIL_Data_Call_Response_v11 数据呼叫的响应(IP 地址、网关、DNS 等)。
SIM 卡 RIL_SimRefresh SIM 卡刷新事件参数。
信号强度 RIL_SignalStrength_v6 / RIL_CDMA_SignalStrength 信号强度值(GSM、CDMA 等不同制式)。
网络信息 RIL_OperatorInfo 运营商名称(长名、短名、MCC/MNC)。
呼叫转移 RIL_CallForwardInfo 呼叫转移的配置(状态、号码、时间等)。
来电显示 RIL_CDMA_CallWaiting CDMA 来电等待信息。
设备标识 RIL_DeviceIdentity IMEI、MEID、ESN 等。
小区信息 RIL_NeighboringCell 相邻小区信息(用于网络选择)。

2.3.4.5 类型定义与联合体

C 复制代码
typedef void * RIL_Token;
//RIL_Token:不透明句柄,实际是 void*。

typedef struct {
    int dbm;  /* Valid values are positive integers.  This value is the actual RSSI value
               * multiplied by -1.  Example: If the actual RSSI is -75, then this response
               * value will be 75.
               */
    int ecio; /* Valid values are positive integers.  This value is the actual Ec/Io multiplied
               * by -10.  Example: If the actual Ec/Io is -12.5 dB, then this response value
               * will be 125.
               */
} RIL_CDMA_SignalStrength;
//RIL_CDMA_SignalStrength 等特定制式的联合体。
  • ril.h 定义了函数接口与电话、短信、网络、SIM 卡等业务相关的结构体、枚举和常量等等,这些是 RIL 层真正传输的数据格式。任何厂商实现 RIL 时,都必须按照这些数据结构填充或解析数据,才能与 Android Telephony 框架正常通信。

ril.h在整个RIL架构中扮演了核心枢纽的角色,它定义了上游(Framework)和下游(Modem)之间的合约,确保了各层组件之间能够无缝协作。从架构上看,ril.h 连接了以下几个部分:

  • rild 守护进程: ril.h定义了它们之间通信的Socket协议和数据格式,rild通过RIL_RadioFunctions调用厂商实现。

  • libril 辅助库: ril.h是连接rild和Vendor RIL的桥梁。rild通过dlopen加载Vendor RIL库,并使用RIL_Init函数获取RIL_RadioFunctions函数指针表。

  • Vendor RIL: ril.h是厂商实现Modem通信库时必须遵循的接口规范。

  • 上层应用: ril.h定义了上层应用(RILJ)与rild之间进行Socket通信时使用的命令和数据类型格式。

ril.h文件是Android RIL系统的核心契约,它定义了通信框架和API,扮演着承上启下的关键角色。它通过定义标准化的数据结构和接口,使得Android的Telephony框架和底层的硬件Modem得以解耦,从而实现了高度的兼容性和可移植性。

2.3.4.6 结构图和时序图总结

2.3.4.6.1 结构图(模块关系与数据流向)
  • RIL_Init:qcril.so 的工厂函数,在启动时被 rild 调用,接收 RIL_Env 并返回 RIL_RadioFunctions。

  • RIL_Env:libril 提供的回调环境,qcril 保存其指针,用于上报结果或事件。

  • RIL_RadioFunctions:qcril 返回的函数表,libril 通过它调用 qcril 的 onRequest 等函数。

2.3.4.6.2 MO(主叫)时序图
2.3.4.6.3 MT(被叫)时序图
组件 角色 方向
RIL_Env libril 提供的回调环境 libril → qcril(通过 RIL_Init 参数传入)
RIL_Init qcril 的工厂函数 rild → qcril,返回 RIL_RadioFunctions
RIL_RadioFunctions qcril 的函数表 qcril → rild → libril
s_callbacks.onRequest libril 调用 qcril 的入口 libril → qcril
g_env->OnRequestComplete qcril 回调 libril 返回请求结果 qcril → libril
g_env->OnUnsolicitedResponse qcril 上报 Modem 主动事件 qcril → libril
相关推荐
raindesound1 小时前
Android+QC modem手机通信模块技术分析 (4)
架构
raindesound1 小时前
Android+QC modem手机通信模块技术分析 (1)
架构
程序员cxuan4 小时前
读懂 Claude Code 架构分析系列,第一篇,开始!
人工智能·后端·架构
Yeats_Liao5 小时前
14:Servlet中的页面跳转-Java Web
java·后端·架构
raindesound5 小时前
计算机基础:ADT(Abstract Data Type)抽象数据类型 (2)
架构
武子康5 小时前
调查研究-201 Rust 里的 dev build 和 release build:为什么同一份代码性能差这么多?
后端·架构·rust
raindesound5 小时前
计算机基础:ADT(Abstract Data Type)抽象数据类型 (1)
架构
夕阳与风馨5 小时前
大文件(20GB+)SFTP 下载模块设计与实现
后端·架构
阳光是sunny16 小时前
Vue 项目怎么做用户行为全链路监控?轻量插件方案详解
前端·面试·架构