美文网首页
电话笔记 | RIL整体框架启动介绍

电话笔记 | RIL整体框架启动介绍

作者: 力卉编程 | 来源:发表于2020-01-03 10:08 被阅读0次

    一、电话流程整体框架

    Android的电话系统主要分为三个部分,java层的各种电话相关应用,java层的Phone Service,主要为上层提供API,同时与native进行通信,可以看做为电话系统的客户端,native层的电话服务进程RILD,负责为上层提供各种电话功能服务,直接与modem进行交互:


    电话流程框架

    二、Android电话系统设计框架:

    Android电话系统设计框架

    由于Android 开发者使用的Modem 是不一样的,各种指令格式,初始化序列都可能不一样,所以为了消除这些差别,Android 设计者将ril 做了一个抽象,使用一个虚拟电话的概念,不同modem相关的AT指令或者通信协议编译成相应的动态链接库.so文件,Rild 是具体的AT 指令合成者和应答解析者。

    三、Android电话系统代码结构:

    图片.png

    四、RILD框架

    在android的电话系统中,在native层实现了电话服务的服务端,由RILD服务与modem的交互,在java层实现电话的客户端,本文主要介绍电话系统的服务端RILD进程:


    RILD框架

    五、RILD源码介绍

    接下来通过源码对RILD的整个框架进行详细介绍。

    5.1 启动

    在kernel启动完成后,将启动第一个应用进程Init进程,init进程在启动过程中将读取init.rc文件来启动一些重量级的native服务,rild进程就是通过配置在init.rc中来启动的。

    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
    
    5.2 RILD进程入口流程:
    RILD进程启动的时序图
    5.3 RILD入口main
    //hardware\ril\rild\rild.c
    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;  
        char libPath[PROPERTY_VALUE_MAX];  
        unsigned char hasLibArgs = 0;  
        int i;  
      umask(S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH);  
      //rild启动无参数  
        for (i = 1; i < argc ;) {  
            if (0 == strcmp(argv[i], "-l") && (argc - i > 1)) {  
                rilLibPath = argv[i + 1];  
                i += 2;  
            } else if (0 == strcmp(argv[i], "--")) {  
                i++;  
                hasLibArgs = 1;  
                break;  
            } else {  
                usage(argv[0]);  
            }  
        }  
      if (rilLibPath == NULL) {  
          //通过Android属性系统读取属性"rild.libpath"的值,即lib库的存放路径  
            if ( 0 == property_get(LIB_PATH_PROPERTY, libPath, NULL)) {  
                goto done;  
            } else {  
                rilLibPath = libPath;  
            }  
      }  
    ##################################################################################  
                                判断是否为模拟器  
    ##################################################################################  
    #if 1  
        {  
            static char*  arg_overrides[3];  
            static char   arg_device[32];  
            int           done = 0;  
    #define  REFERENCE_RIL_PATH  "/system/lib/libreference-ril.so"  
            /* first, read /proc/cmdline into memory */  
            char          buffer[1024], *p, *q;  
            int           len;  
            int           fd = open("/proc/cmdline",O_RDONLY);  
            if (fd < 0) {  
                LOGD("could not open /proc/cmdline:%s", strerror(errno));  
                goto OpenLib;  
            }  
            //读取/proc/cmdline文件中的内容  
            do {  
                len = read(fd,buffer,sizeof(buffer)); }  
            while (len == -1 && errno == EINTR);  
            if (len < 0) {  
                LOGD("could not read /proc/cmdline:%s", strerror(errno));  
                close(fd);  
                goto OpenLib;  
            }  
            close(fd);  
            //判断是否为模拟器,对于真机,此处条件为false  
            if (strstr(buffer, "android.qemud=") != NULL)  
            {  
                int  tries = 5;  
    #define  QEMUD_SOCKET_NAME    "qemud"  
                while (1) {  
                    int  fd;  
                    sleep(1);  
                    fd = socket_local_client(QEMUD_SOCKET_NAME,  
                                ANDROID_SOCKET_NAMESPACE_RESERVED,  
                                SOCK_STREAM );  
                    if (fd >= 0) {  
                        close(fd);  
                        snprintf( arg_device, sizeof(arg_device), "%s/%s",  
                                    ANDROID_SOCKET_DIR, QEMUD_SOCKET_NAME );  
                        arg_overrides[1] = "-s";  
                        arg_overrides[2] = arg_device;  
                        done = 1;  
                        break;  
                    }  
                    LOGD("could not connect to %s socket: %s",QEMUD_SOCKET_NAME, strerror(errno));  
                    if (--tries == 0)  
                        break;  
                }  
                if (!done) {  
                    LOGE("could not connect to %s socket (giving up): %s",  
                        QEMUD_SOCKET_NAME, strerror(errno));  
                    while(1)  
                        sleep(0x00ffffff);  
                }  
            }  
      
            /* otherwise, try to see if we passed a device name from the kernel */  
            if (!done) do { //true  
    #define  KERNEL_OPTION  "android.ril="  
    #define  DEV_PREFIX     "/dev/"  
                //判断/proc/cmdline中的内容是否包含"android.ril="  
                p = strstr( buffer, KERNEL_OPTION );  
                if (p == NULL)  
                    break;  
                p += sizeof(KERNEL_OPTION)-1;  
                q  = strpbrk( p, " \t\n\r" );  
                if (q != NULL)  
                    *q = 0;  
                snprintf( arg_device, sizeof(arg_device), DEV_PREFIX "%s", p );  
                arg_device[sizeof(arg_device)-1] = 0;  
                arg_overrides[1] = "-d";  
                arg_overrides[2] = arg_device;  
                done = 1;  
            } while (0);  
              
            if (done) { //false  
                argv = arg_overrides;  
                argc = 3;  
                i    = 1;  
                hasLibArgs = 1;  
                rilLibPath = REFERENCE_RIL_PATH;  
                LOGD("overriding with %s %s", arg_overrides[1], arg_overrides[2]);  
            }  
        }  
    OpenLib:  
    #endif  
    ##################################################################################  
                                动态库装载  
    ##################################################################################  
      
      switchUser();//设置Rild进程的组用户为radio  
      //加载厂商自定义的库  ①
        dlHandle = dlopen(rilLibPath, RTLD_NOW);  
        if (dlHandle == NULL) {  
            fprintf(stderr, "dlopen failed: %s\n", dlerror());  
            exit(-1);  
      }  
      //创建客户端事件监听线程  ②
      RIL_startEventLoop();  
      //通过dlsym定位到RIL_Init函数的地址,并且强制转换为RIL_RadioFunctions的函数指针  ③
        rilInit = (const RIL_RadioFunctions *(*)(const struct RIL_Env *, int, char **))dlsym(dlHandle, "RIL_Init");  
        if (rilInit == NULL) {  
            fprintf(stderr, "RIL_Init not defined or exported in %s\n", rilLibPath);  
            exit(-1);  
        }  
        if (hasLibArgs) { //false  
            rilArgv = argv + i - 1;  
            argc = argc -i + 1;  
        } else {  
            static char * newArgv[MAX_LIB_ARGS];  
            static char args[PROPERTY_VALUE_MAX];  
            rilArgv = newArgv;  
            property_get(LIB_ARGS_PROPERTY, args, "");//通过属性系统读取"rild.libargs"属性值  
            argc = make_argv(args, rilArgv);  
        }  
        // Make sure there's a reasonable argv[0]  
      rilArgv[0] = argv[0];  
      //调用RIL_Init函数来初始化rild,传入参数s_rilEnv,返回RIL_RadioFunctions地址  ④
      funcs = rilInit(&s_rilEnv, argc, rilArgv);  
      //注册客户端事件处理接口RIL_RadioFunctions,并创建socket监听事件  ⑤
        RIL_register(funcs);  
    done:  
        while(1) {  
            // sleep(UINT32_MAX) seems to return immediately on bionic  
            sleep(0x00ffffff);  
        }  
    }
    

    在main函数中主要完成以下工作:

    • 解析命令行参数,通过判断是否为模拟器采取不同的方式来读取libreference-ril.so库的存放路径;
    • 使用dlopen手动装载libreference-ril.so库;
    • 启动事件循环处理 RIL_startEventLoop();
    • 从libreference-ril.so库中取得RIL_Init函数地址,建立起libril.so库与libreference-ril.so库通信桥梁;
      ------ 使用该函数将libril.so库中的RIL_Env接口注册到libreference-ril.so库,同时将libreference-ril.so库中的RIL_RadioFunctions接口注册到到libril.so库中,
    5.4 启动事件循环处理eventLoop工作线程

    建立多路I/O驱动机制的消息队列,用来接收上层发出的命令以及往Modem发送AT指令的工作,时整个RIL系统的核心部分。创建一个事件分发线程s_tid_dispatch,线程执行体为eventLoop。

    图:

    eventLoop工作线程时序图
    代码:RIL_startEventLoop
    //hardware\ril\libril\Ril.cpp 
    extern "C" void RIL_startEventLoop(void) {  
        int ret;  
        pthread_attr_t attr;  
        /* spin up eventLoop thread and wait for it to get started */  
        s_started = 0;  
        pthread_mutex_lock(&s_startupMutex);  
        pthread_attr_init (&attr);  
      pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);  
      //创建一个工作线程eventLoop  
      ret = pthread_create(&s_tid_dispatch, &attr, eventLoop, NULL);  
      //确保函数返回前eventLoop线程启动运行  
        while (s_started == 0) {  
            pthread_cond_wait(&s_startupCond, &s_startupMutex);  
        }  
        pthread_mutex_unlock(&s_startupMutex);  
        if (ret < 0) {  
            LOGE("Failed to create dispatch thread errno:%d", errno);  
            return;  
        }  
    } 
    

    代码:eventLoop

    static void * eventLoop(void *param) {  
        int ret;  
        int filedes[2];  
        ril_event_init(); //初始化请求队列  
        pthread_mutex_lock(&s_startupMutex);  
        s_started = 1; //eventLoop线程运行标志位  
        pthread_cond_broadcast(&s_startupCond);  
      pthread_mutex_unlock(&s_startupMutex);  
      //创建匿名管道  
        ret = pipe(filedes);  
        if (ret < 0) {  
            LOGE("Error in pipe() errno:%d", errno);  
            return NULL;  
      }  
      //s_fdWakeupRead为管道读端  
      s_fdWakeupRead = filedes[0];  
      //s_fdWakeupWrite为管道写端  
      s_fdWakeupWrite = filedes[1];  
      //设置管道读端为O_NONBLOCK非阻塞  
      fcntl(s_fdWakeupRead, F_SETFL, O_NONBLOCK);  
      //初始化s_wakeupfd_event结构体的内容,句柄为s_fdWakeupRead,回调函数为   processWakeupCallback  
        ril_event_set (&s_wakeupfd_event, s_fdWakeupRead, true,processWakeupCallback, NULL);  
        ①rilEventAddWakeup (&s_wakeupfd_event);  
        // Only returns on error  
        ②ril_event_loop();  
        LOGE ("error in event_loop_base errno:%d", errno);  
        return NULL;  
    }  
    
    5.4.1 rild两类事件

    在rild中定义了event的概念,Rild支持两种类型的事件:

    • 定时事件:根据事件的执行时间来启动执行,通过ril_timer_add添加到time_list队列中
    • Wakeup事件:这些事件的句柄fd将加入的select IO多路复用的句柄池readFDs中,当对应的fd可读时将触发这些事件。对于处于listen端的socket,fd可读表示有个客户端连接,此时需要调用accept接受连接。
    struct ril_event {  
        struct ril_event *next;  
        struct ril_event *prev;  
        int fd;  //文件句柄  
        int index; //该事件在监控表中的索引   
        bool persist; //如果是保持的,则不从watch_list 中删除  
        struct timeval timeout; //任务执行时间  
        ril_event_cb func; //回调事件处理函数  
        void *param; //回调时参数  
    };  
    
    static struct ril_event s_commands_event;  
    ril_event_set (&s_commands_event, s_fdCommand, 1,processCommandsCallback, p_rs)  
      
    static struct ril_event s_wakeupfd_event;  
    ril_event_set (&s_wakeupfd_event, s_fdWakeupRead, true,processWakeupCallback, NULL)  
      
    static struct ril_event s_listen_event;  
    ril_event_set (&s_listen_event, s_fdListen, false,listenCallback, NULL)  
      
    static struct ril_event s_wake_timeout_event;  
    ril_timer_add(&(p_info->event), &myRelativeTime);  
    
    static struct ril_event s_debug_event;  
    ril_event_set (&s_debug_event, s_fdDebug, true,debugCallback, NULL)  
    
    5.4.2 三个事件队列
    //事件监控队列
    static struct ril_event * watch_table[MAX_FD_EVENTS];
    //定时事件队列
    static struct ril_event timer_list;
    //处理事件队列
    static struct ril_event pending_list; //待处理事件队列,事件已经触发,需要所回调处理的事件
    
    5.4.3 事件流程概述:

    首先通过Linux中的select多路I/O复用对句柄池中的所有句柄进行监控,当有事件到来时select返回,否则阻塞。当select返回时,表示有事件的到来,通过调用processTimeouts函数来处理超时事件,处理方式是遍历time_list链表以查询超时事件,并将超时事件移入到pending_list链表中,接着调用processReadReadies函数来处理触发的事件,处理方式为遍历watch_table列表以查询触发的事件,并将触发的事件移入到pending_list链表中,如果该事件不是持久事件,还需要从watch_table列表中移除,当查询完两种待处理的事件并放入到pending_list链表中后,调用firePending函数对待处理的事件进行集中处理,处理方式为遍历链表,调用每一个事件的回调函数。

    RIL事件处理详见:电话笔记 | RIL整体框架事件处理介绍

    完~~

    整理: 力卉编程

    相关文章

      网友评论

          本文标题:电话笔记 | RIL整体框架启动介绍

          本文链接:https://www.haomeiwen.com/subject/dtaaoctx.html