Android RIL源码梳理(1) ——rild启动流程 .

本文详细介绍了Android系统的RIL(Radio Interface Layer)架构,包括RIL的基本组成及其启动流程,重点分析了rild进程如何与底层硬件交互,以及在MTK平台上的实现差异。

一、RIL的基本架构

Android RIL (Radio Interface Layer)提供了Telephony服务和Radio硬件之间的抽象层。RIL负责数据的可靠传输、AT命令的发送以及response的解析。一般的,应用处理器(AP)通过AT命令集与无线通讯模块(基带/BP)通信。通信的方式又分为主动请求的request(诸如拨号、发短信……),以及Modem主动上报的例如信号强度、基站信息、来电、来短信等,称之为unsolicited response。

二、ril-daemon的启动:

首先我们来看一下ril-daemon进程的启动:ril-daemon进程是由init进程在系统开机时负责启动的,该进程在我们系统启动之后就一直存在在系统里面了。在init.rc(system/core/rootdir/)中我们可以看到如下代码:

//ril-daemon守护进程指的是system/bin/下面的可执行程序rild (此处的rild可执行程序,又是指的什么呢?)

service ril-daemon /system/bin/rild

class main

socket rild stream 660 root radio

socket rild-debug stream 660 radio system

user root

group radio cache inet misc audio sdcard_rw log

从代码中我们可以看到rild可执行程序是由hardware/ril/rild/目录下的rild.c文件编译生成的。

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)

#编译的资源文件

LOCAL_SRC_FILES:= \

rild.c

LOCAL_SHARED_LIBRARIES := \

libcutils \

libril

ifeq ($(TARGET_ARCH),arm)

LOCAL_SHARED_LIBRARIES += libdl

endif # arm

LOCAL_CFLAGS := -DRIL_SHLIB

#编译生成rild可执行程序

LOCAL_MODULE:= rild

include $(BUILD_EXECUTABLE)

在该Android.mk文件中,还将radiooptions.c编译成二进制可执行文件radiooptions(system/bin/)

我们可以在shell下进入system/bin目录,执行以下radiooptions这个命令,我们就可以得到如下结果:

(注意:MTK平台对该模块进行了重新封装,因此在MTK手机的system/bin目录下我们找不到radiooptions可执行文件,如果有兴趣的同学想试着执行以下该命令,可以创建一个模拟器,然后在进入模拟器的/system/bin下面就可以看到上面的结果了)。

三、rild启动流程分析。

前面我们讲了,ril-daemon其实质就是rild可执行程序,rild又是由init进程启动的,那么它具体又完成了哪些工作呢? 这就需要我们具体来看rild的入口函数rild.c(hardware/ril/rild/)的实现了。这之前我们先 来了解一下几个动态库文件。

1、rild、libreference-ril.so 、libril.so、radiooptions的作用:

1)、rild(hardware/ril/rild/rild.c):仅实现一main函数作为整个ril层的入口点,负责完成初始化。

2)、libril.so(hardware/ril/libril/*):与rild结合相当紧密,是其共享库,编译时就已经建立了这一关系(感兴趣的同学可以看看hardware/ril/libril/Android.mk文件中动态库的调

用关系)。组成部分为ril.cpp,ril_event.cpp。libril.so驻留在rild这一守护进程中,主要完成同上层通信的工作,接受ril请求并传递给librefrence_ril.so,同时把librefrence_ril.so的

反馈回传给调用进程。

3)、librefrence_ril.so(hardware/ril/librefrence_ril/*):rild通过手动的dlopen方式加载,结合稍微松散,这也是因为librefrence.so主要负责跟Modem硬件通信的缘故。这样

做更方便替换或修改以适配更多的Modem种类。它转换来自libril.so的请求为AT命令,同时监控Modem的反馈信息,并传递回libril.so。在初始化时, rild通过符号RIL_Init获取一组函数指针并

以此与之建立联系。

4)、radiooptions(hardware/ril/rild/radiooptions.c):radiooptiongs通过获取启动参数,利用socket与rild通信,可供调试时配置Modem参数。

2、下面我们就来介绍一下rild的启动的流程,首先先来看看rild.c的main方法。在rild.c的main方法中,其实主要就完成了3件事,下面我们来一一解析这3件事。

1)、RIL_startEventLoop()

Ril_event_loop就是一个for的无限循环,在这个for内部看到select函数了,其实select只监测read的fd_set,所要监听的fd都存放在全局变量readFds中,ptv决定select block的形态,

要么设定时间block直到到期,要么无限block直到有监听fd上数据可读,当select返回后就会查找是哪个事件的fd的触发的,然后通过firePending()呼叫该事件的callback。注意这是循环的内部,也

就是说每当select返回并执行其他动作之后,又会重新把readFds加到select中。熟悉Linux的同学应该很清楚这种IO多路复用的select机制(我们也可以在命令提示行下执行man select来

查看select函数的详细介绍)。这样就完成了我们rild启动的第一步就完成了,接下来我们再来看看第二步。

2)、RIL_Init

在rild.c的main方法是通过如下方式来调用RIL_Iint方法的:

dlHandle = dlopen(rilLibPath, RTLD_NOW); //加载libreference-ril.so动态库文件

rilInit = (const RIL_RadioFunctions *(*)(const struct RIL_Env *, int, char **))dlsym(dlHandle, "RIL_Init");

此处的rilLibPath是通过property属性值的方式来获取的。它的值为:“/system/lib/libreference-ril.so “动态库文件的属性值。下面就会通过调用动态库的方式来调用

reference-ril.c中RIL_Init()方法。

RIL_Init首先通过参数获取硬件接口的设备文件或模拟硬件接口的socket. 接下来便新开一个线程继续初始化,mainLoopmainLoop的主要任务是建立起与硬件的通信,然后通过read方法阻塞

等待硬件的主动上报或响应在注册一些基础回调(timeout,readerclose)后,mainLoop首先打开硬件设备文件,建立起与硬件的通信,s_device_paths_port是前面获取的设备路径参数,将其打开。

上面我们就完成了RIL_Init的整个过程了。

3)、RIL_register()

在RIL_init结束时的返回值为rilInit(RIL_RadioFunctions结构体类型),先来看看RIL_RadioFunctions结构体的构成,其中最重要的是onRequest,上层来的请求都由这个函数进行映射

后转换成对应的AT命令发给硬件。

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;

}

funcs = rilInit(&s_rilEnv, argc, rilArgv);

RIL_register(funcs); //rild通过RIL_register注册这一指针

RIL_register的另外一个重要的作用是:打开和上层通信的socket管道。有了这样一个通道,上层App就可以同过这个通道来和Modem端通信,Modem端也可以通过这个通道向上层App发送

响应消息了

我们也可以看到“rild“ socket的通信处理,是在它的回调函数listenCallback中进行处理的,有兴趣的同学可以自行学习一下。到此,rild的启动的流程我们就已经分析完了。

以上的分析都是基于google 4.0的源码的基础上进行的,和我们目前开发的MTK平台有一些差异,MTK自己重新实现了rildlibreference-ril.so、libril.so……,但是它基本的流程任然是一样的,

下面我们再简单看看彼此的差异点。

四、MTK与Google原生的一些差异

1、首先我们先来看一下init.rc的差异

上边是MTK4.0平台的ril-daemon,下边是google4.0的源代码的。ril-daemon都是通过rild可执行程序来实现的;MTK平台实现了双卡功能,因此它在初始化时创建了两个Socket管道

“rild“和rild2,这两个socket就是用来上层app和Modem通信的通道。同时他还创建了rild-mtk-ut、rild-mtk-ut-2、rild-mtk-modem、rild-atci等socket通道,这些socket通道具体的作

用,希望有了解的同学共享一下。

2 在MTK4.1的代码中,我们在hardware/ril/rild/目录下也可以看到一个rild.c文件编译生成的rild可执行文件,但是前面我们又讲到MTK是有自己的实现的,那么这又是怎么回事了?

在ril目录下有一个CleanSpec.mk文件,里面只有如下一句:$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/rild)

也就是说,hardware/ril/*生成的rild可执行程序会被删除掉,也就是说init.rc中定义的ril-daemon的可执行文件并不是此处的rild。

那么那个init.rc中启动的那个rild在哪里呢?在vendor下,我们找到了MTK的rild,这个是以可执行程序的形式Release给我们的。因此我们也看不到它具体的实现的。

3、libreference-ril.so和libril.so动态库文件

前面我们讲到,在rild.c的main方法中会同过dlopen()的方式来加载动态库文件,而动态库文件的路径又是通过property属性文件的方式获取的,既然这样,那么我们就可以尝

试在adb shell下执行getprop方法来查看这个属性

得到的结果是

同样我们在vendor下找到了mtk-ril.so以及librilmtk.so库文件。

<think>嗯,用户这次聚焦在AndroidRIL架构中qcrilnrd和rild的关系上,还提供了三段站内引用。看来用户对底层通信机制很感兴趣,可能是系统开发或调试人员。从引用内容看,用户已经掌握了基础概念:rildAndroid原生守护进程,而qcrilnrd是高通扩展的实现层。但用户更想了解二者协作细节,这需要从架构层级解释。先梳理关键点:rild作为标准接口层,通过RIL_Env结构体向qcrilnrd提供回调入口。而qcrilnrd在RIL_Init()时不仅注册自己的处理函数(如onRequest),还会创建独立线程处理Modem消息——这点在引用[1]的qcril_event_init()描述中很重要。用户引用的代码片段其实已经揭示了核心交互:1.rild加载qcrilnrd库时调用RIL_Init()2.qcrilnrd保存rild的回调表(用于主动上报事件)3.qcrilnrd返回自己的RadioFunctions结构体(包含onRequest等函数指针)4.此后rild下发的请求全部转入qcril_request_api[].onRequest特别要注意线程模型差异:rild主线程处理框架请求,而qcrilnrd通过qcril_event_start()创建专属事件循环线程处理Modem响应,避免阻塞。这种双线程设计在引用[1]的注释里提到但没展开,需要重点说明。用户可能遇到的困惑点在于:为什么需要qcrilnrd这个中间层?这就要提到高通基带的封闭性——qcrilnrd相当于把高通私有的QMI协议转换成标准RIL接口。在回答里用“协议转换器”的比喻应该更直观。最后补充调试提示:当出现“NoService”时,用adblogcat-bradio|grep-E'rild|qcrilnrd'对比日志能看出请求在哪个环节断链。这个实战技巧用户应该用得上。</think>在 Android RIL(Radio Interface Layer)架构中,**rild** 与 **qcrilnrd** 是协同工作的核心组件,二者形成分层处理模型。以下是功能区别与协作关系详解: --- ### **功能定位与层级关系** | **组件** | **层级** | **功能描述** | **代码定位** | |------------|-----------------|-----------------------------------------------------------------------------|---------------------------------| | **rild** | Android 框架层 | 系统级守护进程,作为 RIL 代理:<br>• 接收 Telephony 服务的请求(如拨号、短信)<br>• 通过动态库接口调用硬件抽象层实现<br>• 管理多 SIM 卡路由 | `/system/core/rild/rild.c` | | **qcrilnrd** | 芯片厂商实现层 | 高通专属 RIL 守护进程:<br>• 实现高通基带(Modem)的通信协议(如 QMI)<br>• 转换 RIL 请求为基带指令<br>• 处理 Modem 上报的原始数据 | `/hardware/qcom-caf/telephony/qcril-nrd/` | --- ### **协作流程剖析** #### **1. 初始化阶段** 当 `rild` 启动时,会动态加载高通实现的 `libqcril_nrd.so`,并调用其 `RIL_Init()` 函数: ```c // rild流程 void RIL_start() { const RIL_RadioFunctions *(*RIL_Init)(const struct RIL_Env *env, int argc, char **argv); void *handle = dlopen("libqcril_nrd.so", RTLD_NOW); RIL_Init = dlsym(handle, "RIL_Init"); funcs = RIL_Init(&s_rilEnv, argc, argv); // 传入 rild 回调函数表 } ``` 在 `qcrilnrd` 的 `RIL_Init()` 中: 1. 保存 `rild` 传入的 `RIL_Env` 回调函数表(用于异步上报事件) 2. 初始化高通专属环境(如 QMI 接口、IMS 模块) 3. 启动事件循环线程(`qcril_event_start()`)监听 Modem 消息 4. 返回 `qcril_request_api[]` 函数表给 `rild` ```c // qcrilnrd 返回的处理函数表 static const RIL_RadioFunctions qcril_request_api[] = { { RIL_VERSION, onRequest_rid, currentState_rid, ... } }; ``` [^1][^3] #### **2. 请求处理流程** ```mermaid sequenceDiagram Telephony Service->>rild: 发起请求(如 dialCall) rild->>qcrilnrd: 调用 qcril_request_api[].onRequest() qcrilnrd->>Modem: 通过 QMI 协议发送二进制指令 Modem-->>qcrilnrd: 返回原始响应数据 qcrilnrd->>qcrilnrd: 解析并转换响应格式 qcrilnrd->>rild: 通过 RIL_Env.onRequestComplete() 回调 rild->>Telephony Service: 返回结构化结果 ``` #### **3. 事件上报流程** ```mermaid sequenceDiagram Modem->>qcrilnrd: 主动上报事件(如来电) qcrilnrd->>qcrilnrd: 转换事件为 RIL_UNSOL_ 格式 qcrilnrd->>rild: 调用 RIL_Env.OnUnsolicitedResponse() rild->>Telephony Service: 转发 RIL_UNSOL_RESPONSE 事件 ``` --- ### **关键设计差异** | **特性** | **rild** | **qcrilnrd** | |------------------|-------------------------------------|-----------------------------------| | **标准化程度** | Android 原生接口,所有芯片通用 | 高通私有实现,依赖基带型号 | | **线程模型** | 单线程处理框架请求 | 独立事件循环线程(`qcril_event`)处理 Modem 通信 [^1] | | **协议转换** | 无 | 将 RIL 请求转换为 QMI/AT 命令 | | **多 SIM 支持** | 路由请求到对应 SIM 卡槽 | 按卡槽实例化处理(`qcril_response_api[SLOT_ID]`)[^1] | --- ### **典型问题定位** 若出现 **"No Service"** 故障: 1. 检查 `rild` 日志确认请求是否发出 ```bash adb logcat -b radio | grep "RIL_REQUEST_GET_SIM_STATUS" ``` 2. 验证 `qcrilnrd` 是否收到请求 ```bash adb logcat -b radio | grep "qcrilnrd.*onRequest" ``` 3. 若 `qcrilnrd` 无响应,需排查: - Modem 固件兼容性 - SIM 卡状态(`qcrilnrd` 的 `qmi_ril_get_sim_state()`) - 基带-AP 通信链路(QXDM 抓取 QMI 日志) ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值