zoukankan      html  css  js  c++  java
  • Android RILD运行机制详解

    前言

    在上一篇文章里(http://blog.csdn.net/jason_wzn/article/details/53232022),简要介绍了Android RIL的架构。这一篇文章,就来看一看RILD(RIL Daemon)相关的内容。Android RIL在HAL(Hardware Abstract Layer)层(C++层)由三个部分组成:

    • RILD是系统的守护进程,主要用于初始化LIBRIL以及启动厂商自定义的Vendor RIL;
    • LIBRIL被RILD初始化完成后,用于与Vendor RIL之间进行交互,负责接收、发送指令;
    • Vendor RIL是第三方厂商自定义的一个库,用于向Modem发送指令或者接收来自LIBRIL或者Modem的指令。

    三者之间的关系图如下所示:

    RILD and LIBRIL

    从这里可以看到,RILD在启动时,负责将LibRil以及Vendor RIL进行初始化,将相应的回调函数以及调用接口进行注册,LibRIL向vendor RIL提供了接口RIL_Env,当Vendor有消息时,利用该回调返回;而Vendor RIL 同样提供了接口RIL_RadioFunctions,给LibRIl调用。这里涉及到3个主要问题:

    1. RILD是如何启动?
    2. RILD是如何进行初始化操作的?
    3. 初始完成后,LIBRIL是如何进行消息的接收与发送的?

    RILD是如何启动的

    RILD(RIL Daemon)是系统的守护进程,系统已启动,就会一直运行。手机开机时,kernel完成初始化后,Android启动一个初始化进程Init用于加载系统基础服务,如文件系统,zygote进程,服务管家ServiceManager,以及RILD:

    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 log

    这里,init进程从手机文件系统目录system/bin/rild中读取RILD的可执行文件,加载到内存运行;同时,创建两个socket端口:rild和rild-debug,其中rild用于RILJ与RILD之间的数据通信,而rild-debug则用于RILJ与RILD的调试。

    RILD是如何进行初始化的

    RILD启动后,一方面会去初始化Vendor RIL,将LIBRIL的回调接口RIL_Env传递给Vendor RIL;同时将Vendor RIL的接口RIL_RadioFunctions注册到LIBRIL中,这样一旦初始化完成,LIBRIL与Vendor RIL就可以进行数据的交换了。

    来看一看RILD的代码:

     1  int main(int argc, char **argv)
     2     {
     3         ...
     4        // Vendor RIL接口函数
     5         const RIL_RadioFunctions *(*rilInit)(const struct RIL_Env *, int, char **);
     6         const RIL_RadioFunctions *funcs;
     7         ...
     8 
     9     OpenLib:
    10 
    11         //从指定路径加载RILD可执行文件
    12         dlHandle = dlopen(rilLibPath, RTLD_NOW);
    13 
    14         if (dlHandle == NULL) {
    15             RLOGE("dlopen failed: %s", dlerror());
    16             exit(EXIT_FAILURE);
    17         }
    18 
    19         // 启动LIBRIL的事件处理线程
    20         RIL_startEventLoop();
    21         // Vendor RIL初始化函数,返回一个RIL_RadioFunctions
    22         rilInit = (const RIL_RadioFunctions *(*)(const struct RIL_Env *, int, char **))dlsym(dlHandle, "RIL_Init");
    23         ...
    24         funcs = rilInit(&s_rilEnv, argc, rilArgv);
    25         RLOGD("RIL_Init rilInit completed");
    26         // 将 RIL_RadioFunctions注册到LIBRIL中
    27         RIL_register(funcs);
    28 
    29         RLOGD("RIL_Init RIL_register completed");
    30     }

    RILD初始化主要完成两件事:(1) 加载Vendor RIL的代码,并对其进行初始化操作,将LIBRIL的接口RIL_Env传递给Vendor RIL,用于回调;(2)开始RIL事件处理线程;(3)将Vendor RIL的接口注册到LIBRIL中,这样LIBRIL就可以将消息发送给Vendor RIL了。

    下图是RILD初始化LIBRIL以及Vendor RIL的一个简化流程:

    RILD init process

    • RIL_startEventLoop()启动RIL事件处理线程:
     1   extern "C" void RIL_startEventLoop(void) {
     2         /* spin up eventLoop thread and wait for it to get started */
     3         s_started = 0;
     4         pthread_mutex_lock(&s_startupMutex);
     5         ...
     6         // eventLoop函数才是真正开始启动事件处理线程的地方
     7         int result = pthread_create(&s_tid_dispatch, &attr, eventLoop, NULL);
     8         if (result != 0) {
     9             RLOGE("Failed to create dispatch thread: %s", strerror(result));
    10             goto done;
    11         }
    12 
    13         while (s_started == 0) {
    14             pthread_cond_wait(&s_startupCond, &s_startupMutex);
    15         }
    16 
    17     done:
    18         pthread_mutex_unlock(&s_startupMutex);
    19     }
    20 
    21     // evetLoop 
    22 
    23     static void *eventLoop(void *param) {
    24         int ret;
    25         int filedes[2];
    26         //初始化事件队列
    27         ril_event_init();
    28 
    29         pthread_mutex_lock(&s_startupMutex);
    30 
    31         s_started = 1;
    32         pthread_cond_broadcast(&s_startupCond);
    33 
    34         pthread_mutex_unlock(&s_startupMutex);
    35 
    36         ret = pipe(filedes);
    37         // 用于监听wakeup事件的pipe端口
    38         s_fdWakeupRead = filedes[0];
    39         s_fdWakeupWrite = filedes[1];
    40         //设置线程唤醒事件,唤醒时,回调processWakeupCallback函数
    41         ril_event_set (&s_wakeupfd_event, s_fdWakeupRead, true,
    42                     processWakeupCallback, NULL);
    43 
    44         rilEventAddWakeup (&s_wakeupfd_event);
    45 
    46         // 真正干活的函数
    47         ril_event_loop();
    48         // kill self to restart on error
    49         kill(0, SIGKILL);
    50 
    51         return NULL;
    52     }
    • RILD初始化vendor RIL之后,将返回的RIL_RadioFunctions返回给RILD,RILD接着将其注册到LIBRIL中:
     1     extern "C" void RIL_register (const RIL_RadioFunctions *callbacks) {
     2         ...
     3         /* Initialize socket1 parameters */
     4         s_ril_param_socket = {
     5                             RIL_SOCKET_1,             /* socket_id */
     6                             -1,                       /* fdListen */
     7                             -1,                       /* fdCommand */
     8                             PHONE_PROCESS,            /* processName */
     9                             &s_commands_event,        /* commands_event */
    10                             &s_listen_event,          /* listen_event */
    11                             processCommandsCallback,  /* processCommandsCallback */
    12                             NULL                      /* p_rs */
    13                             };
    14         ....
    15         // back compatibility
    16         if (s_started == 0) {
    17             RIL_startEventLoop();
    18         }
    19 
    20         // start listen socket1
    21         startListen(RIL_SOCKET_1, &s_ril_param_socket);
    22     }
    23 
    24     // startListen
    25     static void startListen(RIL_SOCKET_ID socket_id, SocketListenParam* socket_listen_p) {
    26         int fdListen = -1;
    27         int ret;
    28         char socket_name[10];
    29 
    30         memset(socket_name, 0, sizeof(char)*10);
    31 
    32         switch(socket_id) {
    33             case RIL_SOCKET_1:
    34                 strncpy(socket_name, RIL_getRilSocketName(), 9);
    35                 break;
    36         ....
    37         // 获取 Unix domain socket对应的FD
    38         fdListen = android_get_control_socket(socket_name);
    39         // 监听端口 
    40         ret = listen(fdListen, 4);
    41 
    42         socket_listen_p->fdListen = fdListen;
    43         // 设置监听回调事件 listenCallback,RILJ主动连接RILD时,处理该回调
    44         /* note: non-persistent so we can accept only one connection at a time */
    45         ril_event_set (socket_listen_p->listen_event, fdListen, false,
    46                     listenCallback, socket_listen_p);
    47         //添加到事件队列中,并唤醒事件处理线程
    48         rilEventAddWakeup (socket_listen_p->listen_event);
    49     }

    源代码: /hardware/ril/libril/ril.cpp

    接下来,我们就来看一看LIBRIL与Vendor RIL各自提供的接口函数。 这两个接口都在/hardware/ril/include/telephony/ril.h中进行了声明。

    Vendor RIL主要提供了5个接口,供LIBRIL调用:

    •  1 RIL_RequestFunc是最主要的一个,所有从RILJ发送过来的请求均由该接口发送给Vendor RIL;
       2 RIL_RadioStateRequest从LIBRIL获取modem的即时状态;
       3 RIL_Supports判断Vendor RIL是否支持某个请求命令;
       4 RIL_Cancel取消某个请求命令;
       5 RIL_GetVersion获取RIL的版本号;
       6 
       7     typedef struct {
       8         int version;        /* set to RIL_VERSION */
       9         RIL_RequestFunc onRequest;
      10         RIL_RadioStateRequest onStateRequest;
      11         RIL_Supports supports;
      12         RIL_Cancel onCancel;
      13         RIL_GetVersion getVersion;
      14     } RIL_RadioFunctions;
      15 
      16 
      17     // 将从RILJ发送过来的请求发送给Vendor RIL
      18     typedef void (*RIL_RequestFunc) (int request, void *data,
      19                                         size_t datalen, RIL_Token t, RIL_SOCKET_ID socket_id);
      20     // 获取 modem 状态
      21     typedef RIL_RadioState (*RIL_RadioStateRequest)(RIL_SOCKET_ID socket_id);

    LIBRIL则向Vendor RIL提供了3个接口:

    • OnRequestComplete:RIL请求完成后,通过该接口将数据返回给LIBRIL,由LIBRIL将数据写入socket RILD;
    • OnUnsolicitedResponse:CP主动上报消息给Vendor RIL后,通过该接口将消息传给LIBRIL;
    • RequestTimedCallback:在指定时间内,LIBRIL调用回调函数RequestTimedCallback
     1    struct RIL_Env {
     2         // 请求完成,返回给LIBRIL
     3         void (*OnRequestComplete)(RIL_Token t, RIL_Errno e,
     4                                void *response, size_t responselen);
     5 
     6     // Vendor RIL接收到从CP主动上报的消息后,传给LIBRIL
     7     #if defined(ANDROID_MULTI_SIM)
     8         void (*OnUnsolicitedResponse)(int unsolResponse, const void *data, size_t datalen, RIL_SOCKET_ID socket_id);
     9     #else
    10         /**
    11          * "unsolResponse" is one of RIL_UNSOL_RESPONSE_*
    12          * "data" is pointer to data defined for that RIL_UNSOL_RESPONSE_*
    13          */
    14         void (*OnUnsolicitedResponse)(int unsolResponse, const void *data, size_t datalen);
    15     #endif
    16         /**
    17          * Call user-specifed "callback" function on on the same thread that
    18          * RIL_RequestFunc is called. If "relativeTime" is specified, then it specifies
    19          * a relative time value at which the callback is invoked. If relativeTime is
    20          * NULL or points to a 0-filled structure, the callback will be invoked as
    21          * soon as possible
    22          */
    23         // 指定时间内LIBRIL调用回调函数RIL_TimedCallback
    24         void (*RequestTimedCallback) (RIL_TimedCallback callback,
    25                                        void *param, const struct timeval *relativeTime);
    26     };

    代码路径: /hardware/ril/rild/rild.c

    初始化完成了 ,那么RIL事件处理线程是从何时开始处理事件的了?RIL事件处理线程是怎么又是同时处理来自RILJ以及Vendor RIL的消息的?下面就来看一看LIBRIL如何处理RIL事件的。

    LIBRIL如何处理RIL事件

    为处理RIL事件,LIBRIL提供了3个事件队列(由双向列表组成):

        static struct ril_event * watch_table[MAX_FD_EVENTS];
        static struct ril_event timer_list;
        static struct ril_event pending_list;

    其中,watch_table用于事件的监测,timer_list保存定时事件,而pending_list用于保存即将被处理的事件列表。对LIBRIL来讲,有3种类型的RIL事件需要处理:

     // RILJ请求事件
        static struct ril_event s_commands_event;
        // 事件处理线程唤醒事件
        static struct ril_event s_wakeupfd_event;
        // RILD socket端口监听事件
        static struct ril_event s_listen_event;

    上一节我们了解到,在RIL事件处理线程开始时,LIBRIL会添加一个s_wakeupfd_event的唤醒事件,必要时对线程进行唤醒操作;在注册Vendor RIL的接口时,注册一个监听事件s_listen_event,当RILJ尝试通过socket连接RILD时,处理该事件;当RILJ与RILD连接成功后,处理回调函数listenCallback时,会添加一个 s_commands_event事件,用于处理RILD socket的数据。

    那么,LIBRIL是从何时开始处理这些事件的?上一节我们了解到,初始化时,LIBRIL启动了一个专门的线程来处理RIL事件:

     1  void ril_event_loop()
     2     {
     3         int n;
     4         fd_set rfds;
     5         struct timeval tv;
     6         struct timeval * ptv;
     7 
     8         for (;;) {
     9 
    10             // make local copy of read fd_set
    11             memcpy(&rfds, &readFds, sizeof(fd_set));
    12             ....
    13             // 从FD集合中选择可用的端口
    14             n = select(nfds, &rfds, NULL, NULL, ptv);
    15             ....
    16             // 处理timer队列中超时的事件
    17             processTimeouts();
    18             // 处理监测队列中的事件: listenCallback,
    19             processReadReadies(&rfds, n);
    20             // OK,fire pending list
    21             firePending();
    22         }
    23     }

    该线程,一直监听FD(File Descriptor)集合readFds,如果有数据时,就会立即返回,进而开始执行事件的处理:首先处理定时事件队列中的event,如果发现有超时的事件,就将其加入pending队列中;接着,查看监测表(保存了最多8个事件)中是否有readFds对应的RIL事件,如果存在,则也将其放入到pending队列。最后,就要开始处理pending队列了:

      static void firePending()
        {
            dlog("~~~~ +firePending ~~~~");
            struct ril_event * ev = pending_list.next;
            while (ev != &pending_list) {
                struct ril_event * next = ev->next;
                removeFromList(ev);
                // 执行回调函数: processWakeupCallback,listenCallback,processCommandsCallback...
                ev->func(ev->fd, 0, ev->param);
                ev = next;
            }
            dlog("~~~~ -firePending ~~~~");
        }

    源码:/android/hardware/ril/libril/samsung/ril_event.cpp

    LIBRIL事件处理线程开始时,只有两个事件:s_wakeupfd_events_listen_events_wakeupfd_event事件在添加s_listen_event事件,需要唤醒RIL事件处理线程被执行:

     static void triggerEvLoop() {
            int ret;
            if (!pthread_equal(pthread_self(), s_tid_dispatch)) {
                /* trigger event loop to wakeup. No reason to do this,
                 * if we're in the event loop thread */
                 do {
                    ret = write (s_fdWakeupWrite, " ", 1);
                 } while (ret < 0 && errno == EINTR);
            }
        }

    接着,开始执行s_listen_event事件,调用回调函数listenCallback:

     1   static void listenCallback (int fd, short flags, void *param) {
     2         ....
     3         // 接受RILJ的链接请求
     4         fdCommand = accept(fd, (sockaddr *) &peeraddr, &socklen);
     5 
     6         /* check the credential of the other side and only accept socket from
     7          * phone process
     8          */
     9         is_phone_socket = 0;
    10 
    11         err = getsockopt(fdCommand, SOL_SOCKET, SO_PEERCRED, &creds, &szCreds);
    12 
    13         ....
    14 
    15         ret = fcntl(fdCommand, F_SETFL, O_NONBLOCK);
    16         ....
    17 
    18         p_info->fdCommand = fdCommand;
    19 
    20         p_rs = record_stream_new(p_info->fdCommand, MAX_COMMAND_BYTES);
    21 
    22         p_info->p_rs = p_rs;
    23         ril_event_set (p_info->commands_event, p_info->fdCommand, 1,
    24             p_info->processCommandsCallback, p_info);
    25         // 添加指令事件`s_commands_event`
    26         rilEventAddWakeup (p_info->commands_event);
    27         // 建立新的连接,告知RILJ链接成功,并上报radio状态
    28         onNewCommandConnect(p_info->socket_id);
    29     }

    下次处理pending事件队列时,处理s_commands_event,调用回调函数processCommandsCallback

     1   static void processCommandsCallback(int fd, short flags, void *param) {
     2         // 循环读 RILD socket接口数据流
     3         for (;;) {
     4             /* loop until EAGAIN/EINTR, end of stream, or other error */
     5             // 读取 socket数据流
     6             ret = record_stream_get_next(p_rs, &p_record, &recordlen);
     7 
     8             if (ret == 0 && p_record == NULL) {
     9                 /* end-of-stream */
    10                 break;
    11             } else if (ret < 0) {
    12                 break;
    13             } else if (ret == 0) { /* && p_record != NULL */
    14                 processCommandBuffer(p_record, recordlen, p_info->socket_id);
    15             }
    16         }
    17         ....

       // processCommandBuffer
        static int processCommandBuffer(void *buffer, size_t buflen, RIL_SOCKET_ID socket_id) {
    
            RequestInfo *pRI;
            ...
            pRI = (RequestInfo *)calloc(1, sizeof(RequestInfo));
    
            pRI->token = token;
            // 根据 RILJ的REQUEST类型来获取CommandInfo
            pRI->pCI = &(s_commands[request]);
            pRI->socket_id = socket_id;
            ...
            // 将请求分配给对应的函数处理
            pRI->pCI->dispatchFunction(p, pRI);
    
            return 0;
        }
    
    }

    上述代码中,s_commands将所有RILJ的请求命令,对应的请求函数以及响应处理函数组成一个类型为commandInfo的结构体数组,等请求从CP返回时,就可以调用对应的响应函数来处理返回的结果了:

     1    static CommandInfo s_commands[] = {
     2         #include "ril_commands.h"
     3     };
     4 
     5     {0, NULL, NULL},                   //none
     6     {RIL_REQUEST_GET_SIM_STATUS, dispatchVoid, responseSimStatus},
     7     {RIL_REQUEST_ENTER_SIM_PIN, dispatchStrings, responseInts},
     8     {RIL_REQUEST_ENTER_SIM_PUK, dispatchStrings, responseInts},
     9     {RIL_REQUEST_ENTER_SIM_PIN2, dispatchStrings, responseInts},
    10     {RIL_REQUEST_ENTER_SIM_PUK2, dispatchStrings, responseInts},
    11     {RIL_REQUEST_CHANGE_SIM_PIN, dispatchStrings, responseInts},
    12     {RIL_REQUEST_CHANGE_SIM_PIN2, dispatchStrings, responseInts},
    13     ....

    源码: /android/hardware/ril/libril/samsung/ril_commands.h

    参考文献

    版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wang2119/article/details/53392526
  • 相关阅读:
    关于32位操作系统和64位操作系统对InstallShield打包的影响
    NEWS: Symantec宣布Wise Package Studio将终止
    InstallShield 2012新功能试用(2) 调用MsiGetProperty等MSI API发生变化
    Basic INFO 在命令行Build InstallShield安装包工程获得压缩安装包
    NEWS InstallShield 2012 Service Pack 1发布
    Basic INFO InstallShield Basic MSI工程中如何在SetupCompleteSuccess界面中启动Readme
    Basic INFO InstallShield的脚本编辑器中如何显示代码行号
    Basic INFO 关于在InstallShield制作的安装包界面中删除InstallShield文字的厂商回复
    Basic INFO InstallShield工程中如何让产品的快捷方式名称始终与产品名保持一致
    Basic INFO: 创建隐藏文件夹
  • 原文地址:https://www.cnblogs.com/ricks/p/9497426.html
Copyright © 2011-2022 走看看