[Android6.0] RILC 系统结构及 LibRIL 运行机制

来源:互联网 发布:淘宝网400电话 编辑:程序博客网 时间:2024/06/01 18:30

    • RILC 代码结构
    • RILC 运行机制
      • RILC 启动过程
        • 1 RILC 加载入口
        • 2 解析 RILC 加载方法
      • RILC 运行过程
      • RILC Runtime LibRIL
        • 1 代码架构
        • 2 结构体 RIL_Env
        • 3 结构体 RIL_RadioFunctions
      • LibRIL Runtime 加载
        • 1 RIL_startEventLoop
        • 2 RIL_register 函数引入三方 RIL_RadioFunctions
      • ril_event 事件处理机制
        • 1 ril_event 数据结构
        • 2 RIL 事件生命周期控制的处理函数
        • 3 ril_event_loop 处理机制
      • LibRIL 运行机制

RILC 代码结构

hardware/ril/ |- CleanSpec.mk            // 编译文件 |- include                 // 关键头文件目录,包括 ril.h ril_cdma_sms.h |- libril                  // LibRIL Runtime 运行环境的源文件目录 |- mock-ril                 |- reference-cdma-sms      // CDMA 短信相关代码 |- reference-ril           // RIL Stub 实现源码文件目录 |- rild                    //守护进程源码文件目录

重点在于 libril、reference-ril 和 rild 三个目录中的 C/C++ 代码文件。
mmm 模块编译的结果分别为 libril.so、libreference-ril.so、rild
从编译 Log 中我们也可以看到

Instal:out/target/product/rk3399_mid/system/lib64/libril.soInstal:out/target/product/rk3399_mid/system/lib64/libreference-rilsoInstal:out/target/product/rk3399_mid/system/bin/rild

RILC 运行机制

RILC 运行在 UserLibraries 系统运行库中的 HAL 层,它使用 HAL Stub 运行结构。
最关键的为 Runtime 对外提供 Proxy 代理接口,Stub 向 Runtime 提供 Operations 操作函数,Runtime 向 Stub 提供 Callback 函数。

RILC 运行机制主要围绕 LibRIL 与 Reference-RIL 相互调用,从而完成 Solicited 和 UnSolicited 消息处理,运行结构如下。
包括两个过程:启动和运行。
启动过程即 1.2.3. 是由 rild 完成的。
运行过程的核心为 LibRIL 和 Reference-RIL 消息交互。

LibRIL 和 Reference-RIL 交互过程,符合 HAL层中基于 Stub 方式的运行机制。
LibRIL 为 Runtime ,Reference-RIL 实现了 RIL 请求转换成 AT 命令,并执行 AT 命令逻辑。
LibRIL 提供了 Reference-RIL 的 Proxy 代理接口。RILJ 基于 Socket 网络连接完成 Solicited 和 UnSolicited 消息 和 LibRIL 进行交互。最终交给 Reference-RIL 进行处理。
Reference-RIL 和 Modem 之间通过串口通信,主要用于 AT 命令的执行。

1. RILC 启动过程

1.1 RILC 加载入口

device/rockchip/rk3399/init.rc

565 service ril-daemon /system/bin/rild -l /system/lib/libreference-ril.so566     class main567     socket rild stream 660 root radio568     socket rild-debug stream 666 radio system569     user root570     group radio cache inet misc audio log

Linux 在启动过程中,会加载此配置文件中配置的系统服务。
所以 Android 在开机过程中,Linux Kernel 会运行 rild 可执行文件加载和启动 LibRIL。

  1. 从上面可以看出建立了两个 Socket 连接,端口号分别是 rild 和 rild-debug。
  2. 基于安全考虑,启动 ril-daemon 系统 service 服务的用户为 radio,进入控制台可以查看其进程信息
root@rk3399_mid:/ # ps | grep rild                                             root      226   1     8416   2480  hrtimer_na 7fac161344 S /system/bin/rild

1.2 解析 RILC 加载方法

hardware/ril/rild/rild.c
关键函数是 RIL_startEventLoop、RIL_Init、RIL_register

main(){// 调用 ril.cpp 中的 RIL_startEventLoop,LibRIL 开始循环监听 Socket 事件// 即 可开始接受 RILJ 发起的 Socket 连接请求和 RIL Solicited 消息请求   RIL_startEventLoop();// 通过 referece-ril.so 动态链接库,获取指向 RIL_Init 函数的指针 rilInit    rilInit = (const RIL_RadioFunctions *(*)(const struct RIL_Env *, int, char **))        dlsym(dlHandle, "RIL_Init");// 异常处理    if (rilInit == NULL) {        RLOGE("RIL_Init not defined or exported in %s\n", rilLibPath);        exit(EXIT_FAILURE);    }// 调用 reference-ril.so 动态链接库的 RIL_Init 函数,传递 s_rilEnv 给 reference-ril.so// 首先,前面 dlsym 方法获取了指向 RIL_init 的指针// 其次,调用 RIL_init 完成 RIL Stub 的初始化,即 reference-ril.so 动态链接库// 其参数 s_rilEnv 即 Runtime,它的获取是在 rild.c 代码中的静态代码块中完成的// 其返回值 funcs 即 Functions    funcs = rilInit(&s_rilEnv, argc, rilArgv);    RLOGD("RIL_Init rilInit completed");// 调用 libril.so 的 RIL_register 函数,将 funcs 传递给 libril.so// 即提供其 Functions 给 LibRIL 调用。    RIL_register(funcs);    RLOGD("RIL_Init RIL_register completed");}

上面的 main 函数整体负责启动 RILC。
关键职责便是建立 LibRIL 和 Reference-RIL 的一种相互协调的能力。
LibRIL 中有指向 Reference-RIL 中 funcs 结构体的指针。
Reference-RIL 中有指向 LibRIL 中 s_rilEnv 结构体(Runtime) 的指针。
建立关系后,两者就可以开始 RIL 消息的交互。

2. RILC 运行过程

LibRIL 现在可以和 Reference-RIL 开始 RIL 消息的交互。
根据消息流向分为两种类型:
下行消息:LibRIL 接收到 RILJ 发起的 Solicited 消息后,LibRIL 适用 funcs 调用 Reference-RIL 的 onRequest、onStateRequest 等方法。
上行消息:Modem 中相关通信的状态发生变化或者执行完 Solicited 请求消息后,
Reference-RIL 可以通过 s_rilEnv 结构体指针调用 LibRIL 中的方法,完成上行消息的发送。

3. RILC Runtime LibRIL

3.1 代码架构

hardware/ril/libril
├── Android.mk
├── ril_commands.h // 定义了 LibRIL 接收到 RILJ发出的 Solicited 请求消息所对应的调用函数和返回调用函数
├── ril.cpp // 建立 Runtime 框架
├── ril_event.cpp // 实现基于 ril_event 双向链表操作函数 ,在 5 节中讲解
├── ril_event.h // ril_event 事件的结构定义
├── ril_ex.h
├── RilSapSocket.cpp
├── RilSapSocket.h
├── RilSocket.cpp
├── RilSocket.h
├── rilSocketQueue.h
└── ril_unsol_commands.h // 定义了 UnSolicited 消息返回调用的函数

LibRIL 以 ril.cpp 代码为核心,其他代码协助它完成 LibRIL Runtime 的启动和运行,LibRIL Runtime 的两个作用:
1. 与 RILJ 基于 Socket 交互
2. 与 Reference-RIL 基于函数调用的交互

3.2 结构体 RIL_Env

hardware/ril/include/telephony/ril.h
hardware/ril/reference-ril/ril.h
这两个 ril.h 完全相同。
RIL_Env 在 ril.h 中的定义如下

struct RIL_Env {    /**     * "t" is parameter passed in on previous call to RIL_Notification     * routine.     * If "e" != SUCCESS, then response can be null/is ignored     * "response" is owned by caller, and should not be modified or     * freed by callee     * RIL_onRequestComplete will return as soon as possible     */    //动态库完成一个请求后,通过 OnRequestComplete 通知处理结果,其中第一个参数标明是哪个请求的处理结果    void (*OnRequestComplete)(RIL_Token t, RIL_Errno e,                           void *response, size_t responselen);#if defined(ANDROID_MULTI_SIM)    /**     * "unsolResponse" is one of RIL_UNSOL_RESPONSE_*     * "data" is pointer to data defined for that RIL_UNSOL_RESPONSE_*     * "data" is owned by caller, and should not be modified or freed by callee     */    void (*OnUnsolicitedResponse)(int unsolResponse, const void *data, size_t datalen, RIL_SOCKET_ID socket_id);#else    /**     * "unsolResponse" is one of RIL_UNSOL_RESPONSE_*     * "data" is pointer to data defined for that RIL_UNSOL_RESPONSE_*     * "data" is owned by caller, and should not be modified or freed by callee     */    // 动态库用于进行unsolicited Response通知的函数    void (*OnUnsolicitedResponse)(int unsolResponse, const void *data, size_t datalen);#endif    /**     * Call user-specifed "callback" function on on the same thread that     * RIL_RequestFunc is called. If "relativeTime" is specified, then it specifies     * a relative time value at which the callback is invoked. If relativeTime is     * NULL or points to a 0-filled structure, the callback will be invoked as     * soon as possible     */     // 给Rild提交一个超时任务    void (*RequestTimedCallback) (RIL_TimedCallback callback,                                   void *param, const struct timeval *relativeTime);};

定义了三个指向函数的指针。其功能在上面注释中有说明。

3.3 结构体 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;

当 LibRIL 接收到 RILJ 发起的 Solicited 请求消息后,其他 5 个指向函数的指针会调用 Reference-RIL 提供的 funcs 中对应请求函数。

4. LibRIL Runtime 加载

LibRIL Runtime 的加载体现在 RIL_startEventLoop 和 RIL_register 两个函数。

4.1 RIL_startEventLoop

RILC 启动过程中,先调用 LibRIL 中的 RIL_startEventLoop 函数完成 LibRIL 运行环境的准备,然后开始循环监听 Socket 相关 RIL 事件。

extern "C" void         // 标识后此方法可以让 rild.c 调用RIL_startEventLoop(void) {    /* spin up eventLoop thread and wait for it to get started */    s_started = 0;      // 启动标志    pthread_mutex_lock(&s_startupMutex);    // 增加 pthread 的同步锁    pthread_attr_t attr;    pthread_attr_init(&attr);       // 初始化 pthread 参数    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);        // 创建基于 eventLoop 函数调用的子线程    int result = pthread_create(&s_tid_dispatch, &attr, eventLoop, NULL);    if (result != 0) {        RLOGE("Failed to create dispatch thread: %s", strerror(result));        goto done;    }    while (s_started == 0) { // pthread 启动标志,会在 eventLoop 方法中设置为 1        // 等待 s_startupCond 通知        pthread_cond_wait(&s_startupCond, &s_startupMutex);    }done:    pthread_mutex_unlock(&s_startupMutex); //释放锁}

eventLoop 函数

进入 ril.cpp 代码中的 eventLoop 函数,其处理逻辑如下:

static void *eventLoop(void *param) {    int ret;    int filedes[2];    ril_event_init(); // 初始化 ril_event 双向链表    pthread_mutex_lock(&s_startupMutex); // 增加 pthread 同步锁    s_started = 1; // 修改启动状态为 1    pthread_cond_broadcast(&s_startupCond);    pthread_mutex_unlock(&s_startupMutex); // 释放 thread 同步锁    ret = pipe(filedes); //  创建管道通信    if (ret < 0) { // 管道创建异常处理        RLOGE("Error in pipe() errno:%d", errno);        return NULL; // 直接返回 NULL    }    s_fdWakeupRead = filedes[0];  // 输入文件描述符    s_fdWakeupWrite = filedes[1];  // 输出文件描述符    fcntl(s_fdWakeupRead, F_SETFL, O_NONBLOCK);    // 设置新创建 RIL 事件的 ril_event 参数,关注 s_fdWakeupRead 文件描述符 以及 RIL 事件回调方法 processWakeupCallback    ril_event_set (&s_wakeupfd_event, s_fdWakeupRead, true,                processWakeupCallback, NULL);    // 增加 ril_event 节点,并激活    rilEventAddWakeup (&s_wakeupfd_event);    // Only returns on error 只在异常情况下才返回    // 调用 ril_event.cpp 中 ril_event_init 函数,开始循环监听和处理 ril_event 时间    ril_event_loop();    RLOGE ("error in event_loop_base errno:%d", errno);    // kill self to restart on error 异常时自杀重启    kill(0, SIGKILL);     return NULL;}

s_wakeupfd_event 的事件处理
s_wakeupfd_event 事件处理主要分为三大块,如下:
1. 创建管道获取其输入输出文件描述符 s_fdWakeupRead、s_fdWakeupWrite
2. 使用 s_fdWakeupRead 和 processWakeupCallback 创建 s_wakeupfd_event 事件
3. 增加并激活 s_wakeupfd_event 事件

ril_event 双向链表中此时仅有一个节点,那就是 s_wakeupfd_event。此节点的 fd 文件描述符为 s_fdWakeupRead,RIL 事件回调函数为 processWakeupCallback。

ril_event_loop 函数
ril_event.cpp
在 ril_event_loop 函数中,核心是 for(;;) 循环,只要循环中处理逻辑不变化,ril_event_loop 函数调用是不会返回的。

4.2 RIL_register 函数引入三方 RIL_RadioFunctions

RIL_register 函数
其作用在于引入第三方 Reference-RIL(以封装后 so 动态链接库提供 libreference-ril.so)。
关键代码在于对第三方 Reference-RIL 提供的 callbacks 参数(funcs)作 判空、版本号 等验证,验证过后,拷贝保存此指向 RIL_RadioFunctions 结构体指针。
这样 LibRIL 中就可以调用第三方 Reference-RIL 提供的 RIL 请求相关函数。

5. ril_event 事件处理机制

RIL 事件相关的数据均封装在 ril_event 结构体中,一个 RIL 事件会对应一个 ril_event 结构体。
LibRIL 在启动完成后进入运行状态,将围绕 ril_event 结构体处理 RIL 相关事件。

5.1 ril_event 数据结构

hardware/ril/libril/ril_event.h

// 定义指向 RIL 事件 Callback 回调函数的指针 ril_event_cbtypedef void (*ril_event_cb)(int fd, short events, void *userdata);struct ril_event {    struct ril_event *next;    struct ril_event *prev;    int fd;    int index;  // 当前 RIL 事件的索引    bool persist; // 保留当前 RIL 事件标志    struct timeval timeout; // RIL 事件超时设置    ril_event_cb func; // RIL 事件回调函数指针    void *param;};

5.2 RIL 事件生命周期控制的处理函数

ril_event_init 双向链表初始化
ril_event_set 设置新创建 ril_event 时间参数
ril_event_add 增加 event

5.3 ril_event_loop 处理机制

LibRIL 运行环境加载过程中,最后会调用 ril_event_loop 函数,开始监听 RIL 事件。
分为两步:
1)增加 pending_list 双向链表中的 RIL 事件节点。processTimeouts 和 processReadReadies 两个函数都是将对应 RIL 事件增加到 pending_list 双向链表。
2)调用 firePending 函数遍历 pending_list 双向链表获取 ril_event ,调用其 func 回调函数,完成对应 RIL 事件的回调。

6. LibRIL 运行机制

分为两个部分:
1) RILJ 建立与 RIL 的 Socket 连接
2) RILJ 向 RIL 发起 Solicited 消息的交互流程和处理机制。

原创粉丝点击