RIL 异步处理模型和命令队列详解

15 阅读6分钟

RIL处理命令和响应的方式是确保通信顺畅的关键。RIL实现了一个异步处理模型,命令发送和响应接收是分开进行的。命令发送之后,RIL等待无线模块的响应,响应到来时,RIL会根据响应数据的内容进行相应的处理。当有多个命令需要执行时,RIL会使用队列管理这些命令,确保它们按顺序执行。

处理命令和响应的过程大致如下:

命令队列 :所有待发送的命令放入队列中。

异步发送 :从队列中取出命令,并异步发送给硬件模块。

响应监听 :RIL监听来自硬件模块的响应。

结果回调 :当响应到达时,RIL将结果回调给发起请求的应用程序。

1. 核心数据结构

RequestInfo 结构体(待发送的命令信息)

// 来自 ril.cpp 第 126 行
static pthread_mutex_t s_pendingRequestsMutex = PTHREAD_MUTEX_INITIALIZER;
static RequestInfo *s_pendingRequests = NULL;  // 待处理请求的链表头

RequestInfo 包含:

  • token - 唯一标识符,用于匹配请求和响应
  • pCI - 指向 CommandInfo 结构体的指针
  • socket_id - SIM 卡 ID(多卡支持)
  • p_next - 链表指针,指向下一个待处理请求
  • wasAckSent - 标志异步确认是否已发送

多 SIM 卡支持

// 多 SIM 卡的独立队列
#if (SIM_COUNT >= 2)
static pthread_mutex_t s_pendingRequestsMutex_socket2  = PTHREAD_MUTEX_INITIALIZER;
static RequestInfo *s_pendingRequests_socket2          = NULL;
#endif

#if (SIM_COUNT >= 3)
static pthread_mutex_t s_pendingRequestsMutex_socket3  = PTHREAD_MUTEX_INITIALIZER;
static RequestInfo *s_pendingRequests_socket3          = NULL;
#endif
// ...

2. 命令队列的完整流程

步骤 1: 命令入队 - addRequestToList()

RequestInfo *
addRequestToList(int serial, int slotId, int request) {
    RequestInfo *pRI;
    int ret;
    RIL_SOCKET_ID socket_id = (RIL_SOCKET_ID) slotId;
    
    // 根据 slotId 选择对应的互斥锁和队列
    pthread_mutex_t* pendingRequestsMutexHook = &s_pendingRequestsMutex;
    RequestInfo** pendingRequestsHook = &s_pendingRequests;
    
    if (socket_id == RIL_SOCKET_2) {
        pendingRequestsMutexHook = &s_pendingRequestsMutex_socket2;
        pendingRequestsHook = &s_pendingRequests_socket2;
    }
    // ... 其他 SIM 卡处理 ...
    
    // 分配内存并初始化 RequestInfo
    pRI = (RequestInfo *)calloc(1, sizeof(RequestInfo));
    if (pRI == NULL) {
        RLOGE("Memory allocation failed for request %s", requestToString(request));
        return NULL;
    }
    
    // 保存请求信息
    pRI->token = serial;              // 唯一标识
    pRI->pCI = &(s_commands[request]); // 命令定义
    pRI->socket_id = socket_id;        // SIM 卡 ID
    
    // 原子操作:将请求添加到队列头部
    ret = pthread_mutex_lock(pendingRequestsMutexHook);
    assert (ret == 0);
    
    pRI->p_next = *pendingRequestsHook;  // 新请求指向原来的头
    *pendingRequestsHook = pRI;          // 新请求成为新的头
    
    ret = pthread_mutex_unlock(pendingRequestsMutexHook);
    assert (ret == 0);
    
    return pRI;  // 返回用作 token
}

关键点:

  • 使用互斥锁保护共享数据结构
  • 使用链表(LIFO - 后进先出)存储待处理请求
  • token 就是返回的 RequestInfo 指针本身

步骤 2: 异步发送命令

// ril_service.cpp 中的命令分发
bool dispatchVoid(int serial, int slotId, int request) {
    // 1. 将请求加入队列(创建 RequestInfo)
    RequestInfo *pRI = android::addRequestToList(serial, slotId, request);
    if (pRI == NULL) {
        return false;
    }
    
    // 2. 异步发送给硬件模块
    CALL_ONREQUEST(request, NULL, 0, pRI, slotId);
    // 这会调用到 vendor RIL 实现(如 QCRIL)
    return true;
}

实际调用链:

dispatchVoid()
    → addRequestToList() 创建 RequestInfo 并加入队列
    → CALL_ONREQUEST() 调用 s_vendorFunctions->onRequest()
        → vendor RIL (QCRIL) 处理请求
            → 与硬件模块通信
                → 等待响应...

步骤 3: 响应监听和回调 - RIL_onRequestComplete()

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;
    
    pRI = (RequestInfo *)t;  // 从 token 恢复 RequestInfo
    
    // 从队列中移除该请求
    if (!checkAndDequeueRequestInfoIfAck(pRI, false)) {
        RLOGE ("RIL_onRequestComplete: invalid RIL_Token");
        return;
    }
    
    socket_id = pRI->socket_id;
    
    // ... 异常处理 ...
    
    if (pRI->cancelled == 0) {
        int responseType;
        
        // 判断响应类型
        if (s_callbacks.version >= 13 && pRI->wasAckSent == 1) {
            // 异步响应:先前发送过 ack,现在发送最终响应
            responseType = RESPONSE_SOLICITED_ACK_EXP;
            grabPartialWakeLock();  // 获取 wake lock
        } else {
            // 同步响应
            responseType = RESPONSE_SOLICITED;
        }
        
        // 调用响应函数,返回给 Java 层
        ret = pRI->pCI->responseFunction((int) socket_id,
                responseType, pRI->token, e, response, responselen);
    }
    
    // 释放内存
    free(pRI);
}

步骤 4: 移除请求 - checkAndDequeueRequestInfoIfAck()

static int
checkAndDequeueRequestInfoIfAck(struct RequestInfo *pRI, bool isAck) {
    int ret = 0;
    pthread_mutex_t* pendingRequestsMutexHook = &s_pendingRequestsMutex;
    RequestInfo ** pendingRequestsHook = &s_pendingRequests;
    
    if (pRI == NULL) {
        return 0;
    }
    
    // 根据 socket_id 选择对应的队列
    if (pRI->socket_id == RIL_SOCKET_2) {
        pendingRequestsMutexHook = &s_pendingRequestsMutex_socket2;
        pendingRequestsHook = &s_pendingRequests_socket2;
    }
    // ...
    
    pthread_mutex_lock(pendingRequestsMutexHook);
    
    // 从链表中查找并移除该请求
    for(RequestInfo **ppCur = pendingRequestsHook
        ; *ppCur != NULL
        ; ppCur = &((*ppCur)->p_next)
    ) {
        if (pRI == *ppCur) {
            ret = 1;
            
            if (isAck) {
                // 标记 ack 已发送(用于异步长时间请求)
                pRI->wasAckSent = 1;
            } else {
                // 从链表中移除该请求
                *ppCur = (*ppCur)->p_next;
            }
            break;
        }
    }
    
    pthread_mutex_unlock(pendingRequestsMutexHook);
    
    return ret;
}

3. 异步处理模型时间线

时间轴:

T0: 应用发起请求(如拨号)
    ↓
T1: addRequestToList() 创建 RequestInfo,加入 s_pendingRequests
    │ [RequestInfo 已在队列中]
    ↓
T2: CALL_ONREQUEST() 发送命令给 vendor RIL
    │ [命令异步发送,不等待]
    ↓
T3: Vendor RIL 与硬件通信,处理请求
    │ [可能耗时几秒钟]
    ↓
T4a: 如果支持异步确认 (RIL version >= 13)
    │   → RIL_onRequestAck() 被调用
    │   → pRI->wasAckSent = 1(标记但不移除)
    │   → 返回 ACK 给应用(让应用知道已收到)
    │
    ↓ (几秒后)
    │
T4b: 硬件响应,回调 RIL_onRequestComplete()
    │ → checkAndDequeueRequestInfoIfAck(pRI, false)
    │ → 从 s_pendingRequests 中移除
    │ → 调用 responseFunction() 返回结果给 Java 层
    │ → free(pRI) 释放内存
    ↓
T5: 应用收到最终响应

4. 多并发请求的处理

当多个请求同时到达时:

请求队列状态变化:

初始状态:
s_pendingRequests → NULL

请求 1 (拨号) 到达:
s_pendingRequests → [RequestInfo-1] → NULL

请求 2 (获取信号强度) 到达:
s_pendingRequests → [RequestInfo-2][RequestInfo-1] → NULL

请求 3 (设置网络) 到达:
s_pendingRequests → [RequestInfo-3][RequestInfo-2][RequestInfo-1] → NULL

请求 2 完成,响应:
checkAndDequeueRequestInfoIfAck(RequestInfo-2, false)
s_pendingRequests → [RequestInfo-3][RequestInfo-1] → NULL
(RequestInfo-2 被移除并释放)

请求 1 完成,响应:
s_pendingRequests → [RequestInfo-3] → NULL

5. 同步保护机制

互斥锁使用

// 关键的互斥保护
pthread_mutex_lock(pendingRequestsMutexHook);
    // 原子操作:增加或遍历链表
    pRI->p_next = *pendingRequestsHook;
    *pendingRequestsHook = pRI;
pthread_mutex_unlock(pendingRequestsMutexHook);

保护的操作:

  • ✓ 添加请求到队列
  • ✓ 查找并移除请求
  • ✓ 遍历列表查找特定请求

6. 异步应答机制(ACK)

对于耗时较长的请求(如拨号):

双阶段确认:

Phase 1: 快速 ACK
────────────────
请求来到时(T2)
    ↓
RIL_onRequestAck() 
    ├─ pRI->wasAckSent = 1
    ├─ 调用 radio::acknowledgeRequest()
    └─ 立即返回给应用

应用:「收到 ACK,知道正在处理」

Phase 2: 最终响应
─────────────────
硬件完成操作(T4b)
    ↓
RIL_onRequestComplete()
    ├─ responseType = RESPONSE_SOLICITED_ACK_EXP
    ├─ grabPartialWakeLock()
    ├─ 调用 responseFunction()
    └─ 返回实际结果

应用:「收到最终响应」

7. 关键 API 总结

API功能调用方
addRequestToList()创建 RequestInfo,加入队列RIL Service (ril_service.cpp)
CALL_ONREQUEST()异步发送命令给 vendor RILRIL Service
RIL_onRequestAck()Vendor RIL 发送快速确认Vendor RIL
RIL_onRequestComplete()Vendor RIL 发送最终响应Vendor RIL
checkAndDequeueRequestInfoIfAck()从队列移除请求ril.cpp 内部

8. 超时和 Wake Lock 管理

// 获取 wake lock,防止系统休眠
static void grabPartialWakeLock() {
    if (s_callbacks.version >= 13) {
        acquire_wake_lock(PARTIAL_WAKE_LOCK, ANDROID_WAKE_LOCK_NAME);
        
        // 200ms 后自动释放(防止长期占用)
        UserCallbackInfo *p_info =
            internalRequestTimedCallback(wakeTimeoutCallback, NULL, 
                                        &TIMEVAL_WAKE_TIMEOUT);
        s_last_wake_timeout_info = p_info;
    }
}

总结

特性实现方式
队列管理链表 + 互斥锁
并发控制Pthread mutex
请求标识Token (RequestInfo 指针)
多 SIM 支持每个 SIM 独立队列 + 互斥锁
异步处理命令发送和响应分离
快速确认RIL_onRequestAck()
最终响应RIL_onRequestComplete()
系统睡眠保护Wake lock + 超时释放

这个设计确保了 RIL 能够高效地处理多个并发请求,同时保持通信的可靠性和系统的电源管理。