RILD及qcril初始化流程

分析以下问题:RILD和qcril如何初始化?Phone进程又如何与RILD关联?
摘要由CSDN通过智能技术生成

之前看了许多其他同行写的这方面的好文章了,最近重温这一块,觉得应该自己写一篇,以后再看也好上手。

Rild是Android提供的框架,位置处于AP侧,对接Phone进程和BP侧Modem。

架构

双卡手机在phone进程会有2个Phone的实例对应各个卡槽,相应的Rild进程也会有2个,Phone和Rild之间通过Socket保持连接,各卡槽之间的操作互相独立。

但是由于基带需要由第三方的芯片厂商来提供,因此Google只提供了Rild的框架和接口,剩下的事情需要由芯片供应商自行来实现,这里说的就是vendor RIL。
关于Vendor RIL的实现方法各有不同,如高通用qcril + qmi机制实现,而MTK则直接使用AT指令与Modem通讯,在RIL层不做过多的处理。
具体的实现以后有时间再来整理。接下来的内容以一个高通的工程为范本来分析。


1. RILD进程的启动

用手上的一台双卡手机验证了rild进程确实存在2个:

root@s2:/ # ps |grep rild
radio     464   1     107284 14088 hrtimer_na 7fa7f60604 S /system/bin/rild
radio     759   1     106252 13920 hrtimer_na 7f95db4604 S /system/bin/rild

Rild为系统服务,很明显会在init.rc中能找到启动的描述(Android7.0被定义在hardware\ril\rild\Rild.rc):

service ril-daemon /system/bin/rild
    class main
    socket rild stream 660 root radio
    socket sap_uim_socket1 stream 660 bluetooth bluetooth
    socket rild-debug stream 660 radio system
    user root

这里启动了一个rild,那么第二个rild在哪里呢?通过OpenGrok的帮忙,在工程中搜索到device/qcom/common/rootdir/etc/init.qcom.rc有相关定义:

service ril-daemon2 /system/bin/rild -c 2
    class main
    socket rild2 stream 660 root radio
    socket rild-debug2 stream 660 radio system
    user root
    disabled
    group radio cache inet misc audio sdcard_r sdcard_rw qcom_diag diag log

service ril-daemon3 /system/bin/rild -c 3
    class main
    socket rild3 stream 660 root radio
    socket rild-debug3 stream 660 radio system
    user root
    disabled
    group radio cache inet misc audio sdcard_r sdcard_rw qcom_diag diag log

这里有rild2和rild3的描述,猜想在工程编译期间脚本会通过编译参数判断工程支持的SIM卡数,然后对应地将这些启动描述整合到最终的init.rc中。(以后再来验证。)


2. RILD main函数

根据init.rc中描述可知,rild的入口函数名字为main。
下面过滤出main函数主要的处理,先大致看一下:

int main(int argc, char **argv) {
    const char * rilLibPath = NULL;
    char **rilArgv;
    void *dlHandle;
    const RIL_RadioFunctions *(*rilInit)(const struct RIL_Env *, int, char **);
    const RIL_RadioFunctions *funcs;
    unsigned char hasLibArgs = 0;

    int i;
    const char *clientId = NULL;

    ...
    //1.从启动参数中获取clientId,代表是第几个RILD
    for (i = 1; i < argc ;) {
        ...
    if (0 == strcmp(argv[i], "-c") &&  (argc - i > 1)) {
            clientId = argv[i+1];
            i += 2;
        } ...
    }

    //2.使用clientId设置RILD的socket名字
    if (strncmp(clientId, "0", MAX_CLIENT_ID_LENGTH)) {
        strlcat(rild, clientId, MAX_SOCKET_NAME_LENGTH);
        RIL_setRilSocketName(rild);
    }

    //3.获取vendor ril库的路径
    //#define LIB_PATH_PROPERTY   "rild.libpath"
    if (rilLibPath == NULL) {
        if ( 0 == property_get(LIB_PATH_PROPERTY, libPath, NULL)) {
            // No lib sepcified on the command line, and nothing set in props.
            // Assume "no-ril" case.
            goto done;
        } else {
            rilLibPath = libPath;
        }
    }
    ...
    //4.打开vendor ril库
    dlHandle = dlopen(rilLibPath, RTLD_NOW);
    //5.启动消息循环
    RIL_startEventLoop();
    ...
    //6.获取vendor RIL的初始化入口方法
    rilInit =
        (const RIL_RadioFunctions *(*)(const struct RIL_Env *, int, char **))
        dlsym(dlHandle, "RIL_Init");
        ...
    //7.调用vendor RIL的RIL_Init方法,此方法会返回RIL_RadioFunctions
    rilArgv[argc++] = "-c";
    rilArgv[argc++] = clientId;
    rilArgv[0] = argv[0];
    funcs = rilInit(&s_rilEnv, argc, rilArgv);

    //8.注册vendor RIL的处理方法
    RIL_register(funcs);
    ...
}

我们看到,main函数主要的流程如下:
一)获取vendor ril并打开。(3和4步)
二)启动消息循环。(第5步)
三)获得vendor ril入口方法,并取得vendor ril的消息处理函数列表。(6和7)
四)注册vendor ril回调方法。(第8步)

用本文接下来的篇幅来分析这上面列出的4个步骤。


3. 深入分析main函数

3.1 获取Vendor RIL库文件路径

main函数中通过getprop获取了vendor ril文件的路径,property名为:”rild.libpath”

#define LIB_PATH_PROPERTY   "rild.libpath"
property_get(LIB_PATH_PROPERTY, libPath, NULL)

在adb shell中输入getprop命令,找到库路径:/vendor/lib64/libril-qc-qmi-1.so

root@s2:/ # getprop rild.libpath
/vendor/lib64/libril-qc-qmi-1.so

这个so文件由qcril源码生成,mk文件中有描述:
/vendor/qcom/proprietary/qcril/qcril_qmi/qcril_qmi.mk

ifndef QCRIL_DSDA_INSTANCE
    LOCAL_MODULE:= libril-qc-qmi-1

在获得了vendor ril库文件路径之后,打开库并取得文件句柄,流程似乎即将进入vendor RIL。但在此之前,Rild会先启动一个子线程消息循环,然后再开始qcril的流程。


3.2 RIL_startEventLoop 启动监听消息循环

RIL_startEventLoop创建了一个线程,用于执行eventLoop这个函数。

Ril.cpp
extern "C" void
RIL_startEventLoop(void) {
    /* spin up eventLoop thread and wait for it to get started */
    s_started = 0;
    ...
    //启动线程,执行eventLoop函数
    int result = pthread_create(&s_tid_dispatch, &attr, eventLoop, NULL);
    ...
    //s_started会在eventLoop被执行后置成1,保证了线程启动后才离开这个函数。
    while (s_started == 0) {
        pthread_cond_wait(&s_startupCond, &s_startupMutex);
    }
    ...
}

在RIL_startEventLoop退出之前,还进入了一个while循环去,这里意图是通过判断s_started 的值,保证线程已经启动且eventLoop已经开始被执行,因为s_started在eventLoop开始时会被赋值成1。

eventLoop可以说是Rild中消息获取和分发的中心,其实现的关键字如下:
无限循环、select 、fd_set、ril_event、callback。

  • 4
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值