一、系统架构概览
┌─────────────────────────────────────────────────────────────┐
│ Framework Layer (Java) │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ RIL.java │ │
│ │ ├─ RILRequest Pool (可复用的请求对象) │ │
│ │ ├─ mRequestList (SparseArray<serial, RILRequest>) │ │
│ │ ├─ mWakeLock (防止系统睡眠) │ │
│ │ └─ RilHandler (消息处理) │ │
│ └───────────────────────────────────────────────────────┘ │
└────────────┬────────────────────────────────────────────────┘
│ AIDL / HIDL Binder
↓
┌─────────────────────────────────────────────────────────────┐
│ HAL Service (C++) │
│ ├─ IRadio / IRadioData / IRadioVoice ... │
│ └─ ServiceProxy (将 Java 调用转换为 HAL 调用) │
└────────────┬────────────────────────────────────────────────┘
│
↓
┌─────────────────────────────────────────────────────────────┐
│ RIL Daemon (C++) │
│ ├─ RIL Library (libril.so) │
│ │ ├─ RequestInfo 链表 (s_pendingRequests) │
│ │ ├─ Token 匹配机制 │
│ │ ├─ RIL_onRequestComplete() 回调 │
│ │ └─ Wake Lock 管理 │
│ │ │
│ └─ Vendor RIL (libril-vendor.so) │
│ ├─ onRequest() 处理请求 │
│ ├─ AT Command 发送 │
│ └─ RIL_onRequestComplete() 返回响应 │
└────────────┬────────────────────────────────────────────────┘
│
↓
┌─────────────────────────────────────────────────────────────┐
│ Modem (Baseband) │
│ └─ AT Interface / Protocol Stack │
└─────────────────────────────────────────────────────────────┘
二、RequestInfo 链表管理 (C++ RIL)
2.1 RequestInfo 数据结构
typedef struct RequestInfo {
int32_t token; // 唯一标识符(由 serial 转换)
CommandInfo *pCI; // 指向命令信息(包含对应的 requestNumber)
struct RequestInfo *p_next; // 链表指针(头插法)
char cancelled; // 是否已取消
char local; // 是否为本地命令(响应不上报)
RIL_SOCKET_ID socket_id; // 所属的 SIM 卡槽(多卡时区分)
int wasAckSent; // Ack 是否已发送(用于异步长耗时请求)
} RequestInfo;
2.2 请求入队过程
RequestInfo *
addRequestToList(int serial, int slotId, int request) {
RequestInfo *pRI;
int ret;
RIL_SOCKET_ID socket_id = (RIL_SOCKET_ID) slotId;
// 1. 根据卡槽选择正确的互斥锁和链表头
pthread_mutex_t* pendingRequestsMutexHook = &s_pendingRequestsMutex;
RequestInfo** pendingRequestsHook = &s_pendingRequests;
#if (SIM_COUNT >= 2)
if (socket_id == RIL_SOCKET_2) {
pendingRequestsMutexHook = &s_pendingRequestsMutex_socket2;
pendingRequestsHook = &s_pendingRequests_socket2;
}
// ... 处理其他卡槽 ...
#endif
// 2. 分配 RequestInfo 结构
pRI = (RequestInfo *)calloc(1, sizeof(RequestInfo));
if (pRI == NULL) {
RLOGE("Memory allocation failed for request %s", requestToString(request));
return NULL;
}
// 3. 初始化字段
pRI->token = serial; // token 直接使用 serial
pRI->pCI = &(s_commands[request]); // 关联命令信息数组
pRI->socket_id = socket_id;
// 4. 原子地添加到链表头(LIFO)
ret = pthread_mutex_lock(pendingRequestsMutexHook);
assert (ret == 0);
pRI->p_next = *pendingRequestsHook; // 指向原来的头
*pendingRequestsHook = pRI; // 新插入的成为头
ret = pthread_mutex_unlock(pendingRequestsMutexHook);
assert (ret == 0);
return pRI;
}
关键特点:
- ✅ LIFO 队列:新请求插入链表头,最后进来的最先处理
- ✅ 多卡支持:每个 SIM 卡有独立的链表和互斥锁(避免锁竞争)
- ✅ Token 映射:
token == serial,便于响应时匹配
三、Token 机制与异步响应
3.1 Java 层的 RILRequest
private static RILRequest obtain(int request, Message result) {
RILRequest rr = null;
synchronized (sPoolSync) {
// 对象复用池(避免频繁分配和垃圾回收)
if (sPool != null) {
rr = sPool;
sPool = rr.mNext;
rr.mNext = null;
sPoolSize--;
}
}
if (rr == null) {
rr = new RILRequest();
}
// 原子性递增 serial(每个请求的唯一标识)
// 当达到 Integer.MAX_VALUE 时,循环回 0
rr.mSerial = sNextSerial.getAndUpdate(n -> ((n + 1) % Integer.MAX_VALUE));
rr.mRequest = request;
rr.mResult = result; // 响应时将通过 Message 回调
rr.mWakeLockType = RIL.INVALID_WAKELOCK;
rr.mWorkSource = null;
rr.mStartTimeMs = SystemClock.elapsedRealtime();
if (result != null && result.getTarget() == null) {
throw new NullPointerException("Message target must not be null");
}
return rr;
}
3.2 响应匹配流程
┌─ 1. Framework 发送请求 (serial=0x0042)
│ ├─ RILRequest rr = RILRequest.obtain(RIL_REQUEST_DIAL, result)
│ └─ mRequestList.append(0x0042, rr) // 保存映射关系
│
├─ 2. RIL Daemon 接收请求
│ ├─ addRequestToList(0x0042, slotId, RIL_REQUEST_DIAL)
│ │ └─ 创建 RequestInfo,token = 0x0042
│ │ └─ pRI->token 被强制转换为 RIL_Token
│ └─ 调用 vendor onRequest(RIL_REQUEST_DIAL, data, datalen, (RIL_Token)pRI)
│
├─ 3. Vendor RIL 处理请求
│ ├─ 发送 AT Command 到 Modem
│ └─ 等待 Modem 响应(可能是阻塞的)
│
├─ 4. Modem 返回响应
│ └─ AT 响应从 Modem 回来
│
└─ 5. Vendor RIL 调用回调
├─ RIL_onRequestComplete(token, RIL_E_SUCCESS, response, responselen)
│ (token = 原来的 RequestInfo* 指针)
└─ RIL Library 通过 token 找回 RequestInfo
├─ pRI = (RequestInfo *)t
├─ 验证 pRI 是否还在待决链表中
├─ 调用 responseFunction() 编码响应
└─ 通过 Binder 回调到 Framework 的 RadioResponse
3.3 Token 的妙用
extern "C" void
RIL_onRequestComplete(RIL_Token t, RIL_Errno e, void *response, size_t responselen) {
RequestInfo *pRI;
int ret;
RIL_SOCKET_ID socket_id = RIL_SOCKET_1;
// 关键:Token 就是 RequestInfo* 指针
pRI = (RequestInfo *)t;
// 验证 token 有效性并从链表中删除
if (!checkAndDequeueRequestInfoIfAck(pRI, false)) {
RLOGE ("RIL_onRequestComplete: invalid RIL_Token");
return; // Token 已失效(可能是请求被取消或超时)
}
socket_id = pRI->socket_id; // 获取原始卡槽 ID
if (pRI->local > 0) {
// 本地命令(响应不回送到上层)
RLOGD("C[locl]< %s", requestToString(pRI->pCI->requestNumber));
free(pRI);
return;
}
appendPrintBuf("[%04d]< %s",
pRI->token, requestToString(pRI->pCI->requestNumber));
if (pRI->cancelled == 0) {
// 请求未被取消,准备响应
int responseType;
if (s_callbacks.version >= 13 && pRI->wasAckSent == 1) {
// 已发送过 Ack 的异步请求,需要获取 Wakelock
responseType = RESPONSE_SOLICITED_ACK_EXP;
grabPartialWakeLock();
} else {
responseType = RESPONSE_SOLICITED;
}
// 调用 responseFunction 编码响应(由命令信息指定)
ret = pRI->pCI->responseFunction((int) socket_id,
responseType, pRI->token, e, response, responselen);
}
free(pRI); // 清理 RequestInfo
}
关键点:
- Token 本质上是 RequestInfo 的内存地址
- 这样做的好处:
- ✅ 零开销查找:O(1) 时间直接定位 RequestInfo
- ✅ 内存安全:包含所有上下文信息
- ✅ 防重复:通过 checkAndDequeueRequestInfoIfAck 验证有效性
四、Java 层的异步处理
private void addRequest(RILRequest rr) {
// 1. 获取 Wake Lock 防止系统睡眠
acquireWakeLock(rr, FOR_WAKELOCK);
// 2. 追踪请求(用于性能分析)
Trace.asyncTraceForTrackBegin(
Trace.TRACE_TAG_NETWORK, "RIL", rr.mSerial + "> "
+ RILUtils.requestToString(rr.mRequest), rr.mSerial);
// 3. 将请求存储在 SparseArray(按 serial 索引)
synchronized (mRequestList) {
rr.mStartTimeMs = SystemClock.elapsedRealtime();
mRequestList.append(rr.mSerial, rr); // Key = serial, Value = RILRequest
}
}
private RILRequest obtainRequest(int request, Message result, WorkSource workSource) {
// 创建并初始化 RILRequest(自动分配 serial)
RILRequest rr = RILRequest.obtain(request, result, workSource);
addRequest(rr);
return rr;
}
Java 层特点:
- ✅ SparseArray:按 serial 快速查找
- ✅ Message 回调:响应通过异步 Message 发送到调用者
- ✅ WorkSource 跟踪:记录谁发起了这个请求(用于电量消耗分析)
五、超时处理
5.1 Wake Lock 超时
private void acquireWakeLock(RILRequest rr, int wakeLockType) {
synchronized (rr) {
if (rr.mWakeLockType != INVALID_WAKELOCK) {
riljLog("Failed to acquire wakelock for " + rr.serialString());
return;
}
switch (wakeLockType) {
case FOR_WAKELOCK:
synchronized (mWakeLock) {
mWakeLock.acquire(); // 获取 Wake Lock
mWakeLockCount++;
mWlSequenceNum++;
// 设置 Wake Lock 超时(默认 60 秒)
Message msg = mRilHandler.obtainMessage(EVENT_WAKE_LOCK_TIMEOUT);
msg.arg1 = mWlSequenceNum;
mRilHandler.sendMessageDelayed(msg, mWakeLockTimeout); // 60000ms
}
break;
...
}
rr.mWakeLockType = wakeLockType;
}
}
5.2 超时事件处理
case EVENT_WAKE_LOCK_TIMEOUT:
if (msg.arg1 == mWlSequenceNum && clearWakeLock(FOR_WAKELOCK)) {
if (mRadioBugDetector != null) {
mRadioBugDetector.processWakelockTimeout(); // 记录异常
}
if (RILJ_LOGD) {
// 打印待决请求列表,用于诊断
int count = mRequestList.size();
riljLog("WAKE_LOCK_TIMEOUT mRequestList=" + count);
for (int i = 0; i < count; i++) {
rr = mRequestList.valueAt(i);
riljLog(i + ": [" + rr.mSerial + "] "
+ RILUtils.requestToString(rr.mRequest));
}
}
}
break;
case EVENT_BLOCKING_RESPONSE_TIMEOUT:
int serial = (int) msg.obj;
rr = findAndRemoveRequestFromList(serial);
// 如果请求已处理,则不做任何操作
if (rr == null) {
break;
}
// 为调用者生成响应(解除阻塞)
if (rr.mResult != null) {
Object timeoutResponse = getResponseForTimedOutRILRequest(rr);
AsyncResult.forMessage(rr.mResult, timeoutResponse, null);
rr.mResult.sendToTarget();
mMetrics.writeOnRilTimeoutResponse(mPhoneId, rr.mSerial, rr.mRequest);
}
decrementWakeLock(rr);
rr.release(); // 回收 RILRequest 对象
break;
超时机制:
-
Wake Lock Timeout (60s):检测 RIL Daemon 是否卡死
- 如果 Modem 响应但 RIL Daemon 未调用
RIL_onRequestComplete()就会超时 - 触发
RadioBugDetector记录异常
- 如果 Modem 响应但 RIL Daemon 未调用
-
Blocking Response Timeout (2s):同步调用的超时
- 某些 API 需要同步等待响应
- 超时后返回默认响应,解除调用者阻塞
六、Modem Crash 检测与恢复
6.1 服务死亡检测
final class RadioProxyDeathRecipient implements HwBinder.DeathRecipient {
@Override
public void serviceDied(long cookie) {
// 服务突然崩溃或断连
riljLog("serviceDied");
if (mFeatureFlags.combineRilDeathHandle()) {
// 优先级最高:发到消息队列前面
mRilHandler.sendMessageAtFrontOfQueue(mRilHandler.obtainMessage(
EVENT_RADIO_PROXY_DEAD,
HAL_SERVICE_RADIO, 0 /* ignored arg2 */, cookie));
} else {
mRilHandler.sendMessage(mRilHandler.obtainMessage(EVENT_RADIO_PROXY_DEAD,
HAL_SERVICE_RADIO, 0 /* ignored arg2 */, cookie));
}
}
}
6.2 恢复流程
case EVENT_RADIO_PROXY_DEAD:
int service = msg.arg1;
riljLog("handleMessage: EVENT_RADIO_PROXY_DEAD cookie = " + msg.obj
+ ", service = " + serviceToString(service));
if ((long) msg.obj == mServiceCookies.get(service).get()) {
mIsRadioProxyInitialized = false;
resetProxyAndRequestList(service); // ← 关键恢复步骤
}
break;
resetProxyAndRequestList() 的作用:
- ✅ 断开与 HAL Service 的连接
- ✅ 清空待决请求列表(返回错误给所有待决请求)
- ✅ 重置 Wake Lock(防止卡住)
- ✅ 重新连接 HAL Service(自动重试)
七、队列执行优先级
7.1 队列配置
┌─────────────────────────────────────────────────┐
│ RIL Daemon Event Loop │
├─────────────────────────────────────────────────┤
│ │
│ 1. Pending List (最高优先级) │
│ └─ 立即执行的事件(如定时器触发) │
│ │
│ 2. Command Socket (正常优先级) │
│ └─ Framework 发来的新请求 │
│ └─ 处理顺序:LIFO(链表头优先) │
│ │
│ 3. Timer Queue (定时优先级) │
│ └─ 已到期的定时器 │
│ │
│ 4. Reader Thread │
│ └─ 从 Modem 读取响应 │
│ │
└─────────────────────────────────────────────────┘
7.2 请求处理顺序
新请求 → 链表头插入 → LIFO 顺序处理
Example:
Request 1 (serial=0x0001) 发送 → 加入链表
Request 2 (serial=0x0002) 发送 → 加入链表头
Request 3 (serial=0x0003) 发送 → 加入链表头
待决链表:[Request3] → [Request2] → [Request1]
Modem 响应顺序(可能不同):
- Response2 回来 → 立即匹配 Request2
- Response1 回来 → 立即匹配 Request1
- Response3 回来 → 立即匹配 Request3
重点:
- ✅ LIFO 是 Modem 响应处理顺序,不是请求发送顺序
- ✅ Modem 可能乱序响应,Token 确保正确匹配
- ✅ Blocking 请求有单独的超时机制
八、深度问题解答
Q1:如何处理 Modem 响应超时?
┌─ Wake Lock Timeout (60s) ← 第一层防御
│ └─ Modem 可能卡死或网络中断
│ └─ RIL_onRequestComplete() 永不回调
│
├─ 触发条件:
│ └─ 60s 内 Modem 未响应且无新响应
│
└─ 处理:
├─ 清除 Wake Lock(唤醒系统)
├─ 记录 RadioBug(AnomalyReport)
├─ 待决请求仍在列表中(等待 Crash 检测)
└─ [可选] Modem Crash 检测触发恢复
Q2:Modem 发生 Crash 时如何恢复?
Crash 检测链路:
1. Modem 崩溃 → HAL Service 死亡
├─ Binder Died Callback 触发
└─ EVENT_RADIO_PROXY_DEAD 发送到 RilHandler
2. EVENT_RADIO_PROXY_DEAD 处理:
├─ 设置 mIsRadioProxyInitialized = false
├─ 调用 resetProxyAndRequestList(service)
│ ├─ 断开 Service 连接
│ ├─ 遍历 mRequestList,为所有待决请求返回错误:
│ │ for (int i = mRequestList.size() - 1; i >= 0; i--) {
│ │ RILRequest rr = mRequestList.valueAt(i);
│ │ AsyncResult.forMessage(rr.mResult, null,
│ │ CommandException.fromRilErrno(RADIO_NOT_AVAILABLE));
│ │ rr.mResult.sendToTarget();
│ │ }
│ └─ 清空 mRequestList
│
└─ 自动重新连接到新的 Modem 实例
├─ getRadioServiceProxy(service)
├─ setResponseFunctions()
└─ 恢复就绪
Q3:队列中有多个请求时,哪个优先执行?
发送顺序:
Request A → Request B → Request C
(按时间顺序发送)
链表结构 (LIFO):
s_pendingRequests → [C] → [B] → [A] → NULL
(最新) (最旧)
Modem 响应处理:
- 响应不是按链表顺序处理的
- 而是 Modem 何时返回响应就何时处理
- Token 机制确保响应与请求的正确配对
示例:
Timeline:
0s: 发送 Request A (RIL_REQUEST_DIAL)
链表:[A]
1s: 发送 Request B (RIL_REQUEST_SIGNAL_STRENGTH)
链表:[B] → [A]
2s: 收到 Response B
→ 通过 token 找到 Request B
→ 立即返回响应给 Framework
3s: 收到 Response A
→ 通过 token 找到 Request A
→ 返回响应给 Framework
结论:Response B 先收到并处理,尽管 Request A 先发送
优先级规则:
- ❌ 不是 FIFO(先入先出)
- ❌ 不是 LIFO(后入先出)
- ✅ 响应驱动(Modem 何时响应就何时处理)
- ✅ Token 保证匹配正确
九、关键概念对比
| 概念 | C++ RIL Daemon | Java Framework |
|---|---|---|
| 队列类型 | RequestInfo* 链表 (LIFO) | SparseArray<serial, RILRequest> |
| Token/Key | RequestInfo* 指针值 | serial (int) |
| 查找时间 | O(1) - 直接指针解引用 | O(log n) - 二分查找 |
| 多卡支持 | 每卡独立链表 + 互斥锁 | 单一列表,serial 编码卡槽 |
| 取消机制 | pRI->cancelled 标志 | Remove from map |
| 内存管理 | malloc/free | 对象复用池 |
十、性能特征与优化建议
10.1 性能特征
| 指标 | 数值 |
|---|---|
| 平均延迟 | 10-100ms (RIL Daemon 处理) + 网络延迟 |
| Wake Lock 超时 | 60s (可配置) |
| Blocking 超时 | 2s (同步调用) |
| Request Pool 大小 | 最多 4 个可复用对象 |
| 并发请求数 | 无限制(但受 Modem 能力限制) |
10.2 优化建议
1. 避免频繁的小请求
❌ for (int i = 0; i < 100; i++) {
ril.requestSignalStrength(); // 100 个单独请求
}
✅ 批量请求或合并操作
2. 合理设置超时
❌ mWakeLockTimeout = 1000 // 太短,易误判
✅ mWakeLockTimeout = 60000 // 60s,适合大多数请求
3. 避免 Blocking 调用
❌ int result = synchronousRilCall(); // 阻塞线程
✅ rilCall(message); // 异步,通过 Message 回调
4. 监控 Wake Lock 超时
✅ 启用 RadioBugDetector,及时发现 RIL 卡死
现在让我创建一个完整的流程图总结:
完整的 RIL 异步请求生命周期
═════════════════════════════════════════════════════════════
[Framework - Java 层]
│
├─ 1. 创建请求
│ ├─ RILRequest rr = RILRequest.obtain(
│ │ RIL_REQUEST_DIAL, result, workSource)
│ │ └─ serial = 0x0042 (原子递增)
│ │
│ ├─ 2. 添加到待决列表
│ │ └─ mRequestList.append(0x0042, rr)
│ │
│ ├─ 3. 获取 Wake Lock
│ │ ├─ mWakeLock.acquire()
│ │ └─ 设置 60s 超时
│ │
│ └─ 4. 调用 HAL Service
│ └─ radioProxy.dial(...) [AIDL/HIDL 调用]
│
↓ (Binder IPC)
│
[RIL Daemon - C++ 层]
│
├─ 5. 接收请求
│ ├─ addRequestToList(0x0042, slotId, RIL_REQUEST_DIAL)
│ │ ├─ 分配 RequestInfo*
│ │ ├─ token = (RIL_Token)pRI
│ │ └─ 插入 s_pendingRequests 链表头
│ │
│ └─ 6. 调用 Vendor onRequest()
│ └─ onRequest(RIL_REQUEST_DIAL, data, len, (RIL_Token)pRI)
│
↓ (AT Command / 协议)
│
[Modem - 基带]
│
├─ 7. 处理请求
│ └─ 执行拨号逻辑,与网络交互
│
├─ 8. 返回结果
│ └─ OK / ERROR + response data
│
↓ (AT Response)
│
[RIL Daemon - C++ 层 (Reader Thread)]
│
├─ 9. 解析响应
│ └─ 调用 responseFunction()
│
└─ 10. 调用回调
└─ RIL_onRequestComplete(token, e, response, len)
├─ pRI = (RequestInfo*)token
├─ checkAndDequeueRequestInfoIfAck(pRI)
│ └─ 从 s_pendingRequests 删除
└─ responseFunction() 编码响应
↓ (Binder IPC - 回调)
│
[Framework - Java 层]
│
├─ 11. 接收响应回调
│ ├─ RadioResponse.dialResponse(responseInfo, result)
│ │
│ └─ 12. 查找原始请求
│ └─ rr = mRequestList.get(serial)
│
├─ 13. 清理资源
│ ├─ mRequestList.remove(serial)
│ ├─ decrementWakeLock(rr)
│ └─ rr.release() [回收到对象池]
│
└─ 14. 回调给上层
└─ AsyncResult.forMessage(rr.mResult, result, e)
└─ rr.mResult.sendToTarget()
[上层 (Phone/Voice Services)]
│
└─ 15. 处理响应
└─ onDialComplete(result, error)
好的,现在我已经完成了 RIL 异步处理模型的深度分析。让我总结并提供一个快速参考表:
📋 快速参考
核心数据流
| 阶段 | 层级 | 关键数据结构 | 时间复杂度 |
|---|---|---|---|
| 请求创建 | Java | RILRequest + Serial | O(1) |
| 请求入队 | C++ | RequestInfo* + LIFO | O(1) |
| 响应查找 | C++ | Token = RequestInfo* | O(1) |
| 响应回调 | Java | SparseArray | O(log n) |
超时和约束
| 机制 | 超时时间 | 触发条件 | 恢复方式 |
|---|---|---|---|
| Wake Lock | 60s | Modem 无响应 | 清除锁,记录异常 |
| Blocking | 2s | 同步调用未完成 | 返回默认值 |
| Service Death | 即时 | Binder 连接断开 | 清空请求,重连 |
并发性与安全性
- ✅ 线程安全:所有队列操作都在互斥锁保护下
- ✅ 多卡隔离:每个卡槽有独立的链表和锁
- ✅ 响应唯一性:Token 机制保证一对一匹配
- ✅ 内存效率:对象复用池减少 GC 压力
好的,我已经完成了对 RIL 异步处理模型的全面分析。这个文档涵盖了:
- ✅ 命令队列 - RequestInfo 链表管理
- ✅ Token 机制 - 请求-响应的唯一配对
- ✅ 异步响应 - RIL_onRequestComplete() 回调
- ✅ Wake Lock - 防止系统睡眠
- ✅ 超时处理 - 三层超时检测
- ✅ Modem Crash 恢复 - 自动检测与恢复
- ✅ 队列优先级 - LIFO 处理顺序
- ✅ 性能优化 - 实践建议
这些内容涵盖了所有你提出的深入问题! 🎉