• Android Ril 分析


     引言:

    这段时间手中的工作,正好好调试一款3g modem,于是乎就分析了一下Android Ril的代码,做了些总结归纳,阅读时可以先看前后两段以及流程图,这样可能更容易把握;

    知识在于分享,文档中可能有些地方写的不对或是不完善,希望各位朋友留言指正,大家相互学习;

    转载时请说明出处;

    欢迎大家留言讨论,大家共同进步。

    RIL 架构分析:

          

    上图清楚的标识了ril在整个Android系统各层的表现形式,我们这里主要分析Ril(RIDL、librefrenece_ril.so、libril.so);

    …/Hardware/ril/rild      RILD的代码实现,有main函数,作为ril层的入口点,常驻系统进程,负责与上下层交互

    …/Hardware/ril/libril    负责与守护进程交互???

    …/Hardware/ril/reference-ril/    Ril库的实现,主要负责与modem进行交互 

     

    实现详细分析:

     

    从init.rc中service ril-daemon /system/bin/rild -l /system/lib/libreference-ril.so -- -d /dev/ttyUSB1 -u /dev/ttyUSB2

    可以知道,Android启动时,系统会启动一个与ril相关的service (ril-daemon),其入口命令为/system/bin/rild

     

    (一)那么首先看看rild(/hardware/ril/rild/*);该目录下有两文件radiooptions.c、rild.c

    • Radiooptions.c 看Makefile知道最终会被编译成radiooptions二进制工具,放在/system/bin/下面,具体用法我在这里就不说了,我到终点里面执行一下,把他的help信息打出了,再详细的就自己看吧,源码不长,也不复杂。

    # radiooptions

    Usage: radiooptions [option] [extra_socket_args]

               0 - RADIO_RESET,

               1 - RADIO_OFF,

               2 - UNSOL_NETWORK_STATE_CHANGE,

               3 - QXDM_ENABLE,

               4 - QXDM_DISABLE,

               5 - RADIO_ON,

               6 apn- SETUP_PDP apn,

               7 - DEACTIVE_PDP,

               8 number - DIAL_CALL number,

               9 - ANSWER_CALL,

               10 - END_CALL

    • Rild.c分析

    1)有main函数入口,带参数输入,其输入参数就是最前面提到的init.rc启动ril-daemon是带的参数;当然如果仔细分析main函数代码,会发现其有多种获取参数的方法。

    2)dlHandle = dlopen(rilLibPath, RTLD_NOW),打开动态库(system/lib/libreference-ril.so)。

    3)RIL_startEventLoop(),启动监听事件队列(自己的理解),具体作用我们后面分析

    4)dlsym(dlHandle, "RIL_Init"),获取动态库的RIL_Init

    5)调用RIL_Init方法,并获取到RIL_RadioFunctions类型的返回值,RIL_RadioFunctions结构体包含了ril需要用的的几个重要函数的函数指针   

    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;

    注释:这些函数的具体实现,见/system/ril/reference-ril/*中的具体实现(这部分也是ril中变化最大的一部分,模块或是厂家不一样,其内容也会有较大的不同)

     6)RIL_register(funcs),funcs就是5)中提到的RIL_RadioFunctions类型的返回值(具体实现见/system/ril/libril);

      • 传递几大重要函数的函数指针
      •  RIL_startEventLoop,注意这里会有判断语句,如果监听事件队列启动,则会监听事件队列机制
      • 获取Socket(rild)和Socket(rild-debug)的listen句柄,并分别将其加入监听事件队列。
    • RIL_startEventLoop、ril_event_loop、watch_table、time_table、pending_list、readFds;这些都是监听事件队列很重要的函数,这里分析起来有点拗口

    1)ril_event_loop是监听事件队列的主循环,当调用RIL_startEventLoop成功以后,其就一直在循环了;

    2)现在来看一下事件描述的结构体,理解了这个才能继续后面的;      

    struct ril_event {

        struct ril_event *next;

        struct ril_event *prev; 

        int fd;  //事件相关的句柄;比如现在这个事件是正当socket的,那么这个句柄就应该是一个socket句柄;还可以使串口等其他设备

        int index;//在list中的index值

        bool persist;//如果保持,则其回常驻watch_table,不会被从list中删除

        struct timeval timeout;//select等待超时的时间

        ril_event_cb func; //回调处理事件函数,通俗说就是当监听的fd有数据来或是select被处罚,则会在将此函数扔到pending_list中待执行

        void *param;  //回调带的参数

    };

    3)说明几个重要的队列

              watch_table :监听事件队列,土话就是一个ril_event数据类型的链表或者数组;

              time_table:超时事件队列,也就是说本来某某事件是在time_table里面的,表示正在被监听,过了些时间,超时了还没被促发,那么这个某某事件就被扔  pending_list里面, 待执行;

              pending_list:待执行事件队列;

              readFds:所以监听事件相关句柄的集合,土话就是一个数组,里面放着正在被监听的事件的相关的fd;

    4)监听事件队列的相关函数(其实就和链表操作差不多,什么插入链表操作,删除链表操作啦)

          ril_event_init:初始化个事件队列

          ril_event_set:初始化某某事件

          ril_event_add:添加事件到watch_table中

          ril_timer_add:添加事件到time_table中

          ril_event_del:从watch_table中删除某某事件,对了time_table不需要删除函数,其事件到了,事件会自动被清除掉

    5)event部分流程图     

        (二)看看reference-ril(/hardware/ril/reference-ril/*),这里就是ril定制的部分,也是差异化最大的部分。

    • Ril_Init  (注:在Rild main函数中被调用,见上一节rild.c分析)---------在这里我们将其描述为:用户自定义RIL初始化部分

    1)首先我们关注一下结构图RIL_RadioFunctions(也称为回调函数),这里面包含了一系列的ril操作相关的函数指针,最终提供给RILD(通过RIL_register)调用,个函数的字面意思已经比较清楚这里就不多做解释。

    static const RIL_RadioFunctions s_callbacks = {

        RIL_VERSION, 

        onRequest,

        currentState,

        onSupports,

        onCancel,

        getVersion

    };

    2)start_uevent_monitor(),现在大部分modem支持串口和usb口通讯,这里我们这要针对USB口,其底层驱动通过USB转串口来实现modem和系统进行交互,而这里是新建一个USB监控线程,监控USB插入、拔出等消息,主要应用与外置modem的热插拔功能。

    3)这里还有一些输入参数的处理,这些输入参数主要来自Rild的模块main函数的输入参数

    4)新建线程mainLoop,下面我们进入mainLoop分析,这里才是干实事的地方

      • open (s_device_path, O_RDWR); 打开AT通讯文件接口,例如/dev/ttyUSB*  
      • at_open(fd, onUnsolicited);这个函数很重要,其建立了两个线程一个是readerLoop,一个是pppcheckLoop

                 readerLoop:用来监听接收来自modem AT口发送过来的命令回事数据

                 readerLoop:用来处理ppp网络拨号上网

                 另外注意这里onUnsolicited是个函数指针,其主要用来处理当readerLoop接收到数据时,会调用其做一些优先的判断处理,主要是进行一些    事件的主动上报,例如时间更新、短消息、ril状体变化等。

    5)RIL_requestTimedCallback(initializeCallback, NULL, &TIMEVAL_DELAYINIT);前开了好些个线程,应该也差不多了,这里应该需要对modem进行一下初始化设置了(通过AT命令),详见initializeCallback,很多模块初始化系列AT 命令都放在这里处理。

     

    • readerLoop

          这里我把这个家伙拎出来再说说,主要这里是返回response给上层的驱动器;AT命令都是以\r\n或\n\r为分隔符,readloop是line驱动的,当读到一行完整的相应其才会有返回,才会上报。

    (三)请求流程(Java层是如何调用到ril里面来的,其实这也是reference-ril的一部分)    

    1)还记得在(一)中描述的RIL_startEventLoop-------->ril_event_loop------>监听着一系列的nfds,其中这里就监听了socket rild(见(一)中6)的描述),其  向ril_event_loop中就加入了RILD socket的listen event,当java层向此套接字发送连接请求时,其select就会监听到socket的连接请求,则会调用event对应的处理函数listenCallback(),具体如何被调用请参看图4 。

           2)接下来看listenCallback()

                 ……

                 s_fdCommand = accept(s_fdListen, (sockaddr *) &peeraddr, &socklen);  //等待接收socket的连接请求,获取socket连接符s_fdCommand

                 ……

                  p_rs = record_stream_new(s_fdCommand, MAX_COMMAND_BYTES);  //建立一个record stream 与s_fdCommand绑定

             ril_event_set (&s_commands_event, s_fdCommand, 1,processCommandsCallback, p_rs);//建立一个event,当s_fdCommand有数据,则调用回调  函数processCommandsCallback

                   rilEventAddWakeup (&s_commands_event); //向watch_table中插入event

         3)接着分析processCommandsCallback()

             其取到上层发送过来的数据后,会调用processCommandBuffer(p_record, recordlen) ,其中p_record包含上层发送过来的消息和数据

         4) 接下来分析processCommandBuffer()

    •  从java层传递下来的命令格式为:parcel+Request号+令牌+内容,这里Request号很重要,它们被定义在RILConstants.java和ril_command.h中,其中它们的定义是一致的;根据Request号我们可以找到对应的dispatchFunction函数和responseFunction函数,详见ril_commands.h以及CommandInfo结构体:

    static CommandInfo s_commands[] = {

    #include "ril_commands.h"

    }; 

     

    typedef struct {

        int requestNumber;

        void (*dispatchFunction) (Parcel &p, struct RequestInfo *pRI);

        int(*responseFunction) (Parcel &p, void *response, size_t responselen);

    } CommandInfo;

    • processCommandBuffer()中我们可以得知,在从java层传递的数据中提取出Request号找到对应的dispatchFunction函数,并调用对应的pRI->pCI->dispatchFunction(p, pRI)函数;
    • 对应的dispatchFunction函数最终会调用s_callbacks.onRequest(pRI->pCI->requestNumber, xxx,sizeof(char *), pRI),这里的s_callbacks就是(二)----1)中所提到的回调函数指针,这些函数的最终实现是在reference-ril.cpp中,这里调用的是reference-ril中的onRequest()函数

    5)所以总结下来请求流程最终都会调用reference-ril中的onRequest()函数,所以如果你不想了解那么多,就直接把onRequest函数当成请求流程的处理函数即可,当然如果你要增减一些对应Java层数据处理功能,那么这套流程你还是需要清楚的。 

     

         (四)Response流程(Ril接收到的数据是如何返回给上层的,这也是reference-ril的一部分)

         1)记得前面提过readerLoop,其实在mainloop中开启的一个线程,主要最用是监听modem AT 通讯口,并且接受其发过来的数据,并进行分类处理,

            这里的分类只的是普通AT 命令和短信/自动上报处理;

    2)先分析短信处理,在readerloop()中通过readline()函数接收响应,然后经过isSMSUnsolicited检测是否是短信/自动上报,如果是则通过   s_unsolHander(也就是函数onUnsolicited)来处理;如果不是短信/自动上报,则执行processLine来处理;

      • onUnsolicited函数(短信/自动上报处理)最终会命令信息以及上报信息内容,可分成两种操作:RIL_onUnsolicitedResponse和RIL_requestTimedCallback;
      • RIL_onUnsolicitedResponse会将unsolicited信息直接返回给上层,调用流程为 RIL_onUnsolicitedResponse ---->sendResponse---->sendResponseRaw---->blockingWrite;
      • blockingWrite(fd, (void *)&header, sizeof(header))表示想fd写入数据,这里fd就是s_fdCommand即前面提到的和java层建立起来的socket,这里表示通过socke(RILD)将response信息传递给上层框架;
      • RIL_requestTimedCallback通过event机制实现timer机制,回调对应的内部处理函数;

    3)接着我们来看一下非短信/自动上报的处理过程,这里我们之间分析processLine函数,这个东东会把冲modem过来的response信息存储到一个临时变量sp_response中,具体怎么存储的劳烦您还是去看看代码吧,都是泪啊!

      • 接下来咋们得分析at_send_command_full_nolock函数,这个函数就充分利用了sp_response这个变量的值,它最终会将sp_response返回给调用at_send_command_full_nolock的函数(时间就是存在于Ril_commands.h中以“request*”开头的函数,其实这些函数最终都会调用到onRequest函数,给详细的理解请回顾(三));
      • 当取得sp_response(即response信息)后,则response信息可以通过RIL_onRequestComplete()函数返回给上层,最后这些信息也是通过soket(RILD)返回给应用的,和前面介绍的基本类似

    总结归纳: 

    • 首先是RIL整体架构,从宏观上了解一下下RIL

               (一)主要是从init.rc、main函数这样的程序员思维,先了解ril在Android中时怎么起来的,以及一部分的具体实现。

               (二)(三)(四)才是RIl的核心部分,前面只是打基础,他们分别说明了RIL如何初始化、如何接收消息和数据、如何返回消息和数据

    • 接下来我们用图表的形式来归纳一下ril初始化 以及"接收"和"返回"数据的过程:

          

                

     

  • 相关阅读:
    10个最好的游戏开发在线资源
    程序员什么时候该考虑辞职
    程序员常去的14个顶级开发社区
    如何成为10倍速的程序员
    (通用)深度学习环境搭建:tensorflow安装教程及常见错误解决
    20行JS代码实现贪吃蛇
    程序员必备工具目录
    发布 Google Chrome插件教程
    高并发思路
    30分钟入门Java
  • 原文地址:https://www.cnblogs.com/AdiSky/p/Android_Ril_Process.html
Copyright © 2020-2023  润新知