前言

在上一篇文章里(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在启动时,负责将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 root radio
socket rild-debug stream 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的代码:

  int main(int argc, char **argv)
{
...
// Vendor RIL接口函数
const RIL_RadioFunctions *(*rilInit)(const struct RIL_Env *, int, char **);
const RIL_RadioFunctions *funcs;
... OpenLib: //从指定路径加载RILD可执行文件
dlHandle = dlopen(rilLibPath, RTLD_NOW); if (dlHandle == NULL) {
RLOGE("dlopen failed: %s", dlerror());
exit(EXIT_FAILURE);
} // 启动LIBRIL的事件处理线程
RIL_startEventLoop();
// Vendor RIL初始化函数,返回一个RIL_RadioFunctions
rilInit = (const RIL_RadioFunctions *(*)(const struct RIL_Env *, int, char **))dlsym(dlHandle, "RIL_Init");
...
funcs = rilInit(&s_rilEnv, argc, rilArgv);
RLOGD("RIL_Init rilInit completed");
// 将 RIL_RadioFunctions注册到LIBRIL中
RIL_register(funcs); RLOGD("RIL_Init RIL_register completed");
}

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

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

  • RIL_startEventLoop()启动RIL事件处理线程:
   extern "C" void RIL_startEventLoop(void) {
/* spin up eventLoop thread and wait for it to get started */
s_started = ;
pthread_mutex_lock(&s_startupMutex);
...
// eventLoop函数才是真正开始启动事件处理线程的地方
int result = pthread_create(&s_tid_dispatch, &attr, eventLoop, NULL);
if (result != ) {
RLOGE("Failed to create dispatch thread: %s", strerror(result));
goto done;
} while (s_started == ) {
pthread_cond_wait(&s_startupCond, &s_startupMutex);
} done:
pthread_mutex_unlock(&s_startupMutex);
} // evetLoop static void *eventLoop(void *param) {
int ret;
int filedes[];
//初始化事件队列
ril_event_init(); pthread_mutex_lock(&s_startupMutex); s_started = ;
pthread_cond_broadcast(&s_startupCond); pthread_mutex_unlock(&s_startupMutex); ret = pipe(filedes);
// 用于监听wakeup事件的pipe端口
s_fdWakeupRead = filedes[];
s_fdWakeupWrite = filedes[];
//设置线程唤醒事件,唤醒时,回调processWakeupCallback函数
ril_event_set (&s_wakeupfd_event, s_fdWakeupRead, true,
processWakeupCallback, NULL); rilEventAddWakeup (&s_wakeupfd_event); // 真正干活的函数
ril_event_loop();
// kill self to restart on error
kill(, SIGKILL); return NULL;
}
  • RILD初始化vendor RIL之后,将返回的RIL_RadioFunctions返回给RILD,RILD接着将其注册到LIBRIL中:
     extern "C" void RIL_register (const RIL_RadioFunctions *callbacks) {
...
/* Initialize socket1 parameters */
s_ril_param_socket = {
RIL_SOCKET_1, /* socket_id */
-, /* fdListen */
-, /* fdCommand */
PHONE_PROCESS, /* processName */
&s_commands_event, /* commands_event */
&s_listen_event, /* listen_event */
processCommandsCallback, /* processCommandsCallback */
NULL /* p_rs */
};
....
// back compatibility
if (s_started == ) {
RIL_startEventLoop();
} // start listen socket1
startListen(RIL_SOCKET_1, &s_ril_param_socket);
} // startListen
static void startListen(RIL_SOCKET_ID socket_id, SocketListenParam* socket_listen_p) {
int fdListen = -;
int ret;
char socket_name[]; memset(socket_name, , sizeof(char)*); switch(socket_id) {
case RIL_SOCKET_1:
strncpy(socket_name, RIL_getRilSocketName(), );
break;
....
// 获取 Unix domain socket对应的FD
fdListen = android_get_control_socket(socket_name);
// 监听端口
ret = listen(fdListen, ); socket_listen_p->fdListen = fdListen;
// 设置监听回调事件 listenCallback,RILJ主动连接RILD时,处理该回调
/* note: non-persistent so we can accept only one connection at a time */
ril_event_set (socket_listen_p->listen_event, fdListen, false,
listenCallback, socket_listen_p);
//添加到事件队列中,并唤醒事件处理线程
rilEventAddWakeup (socket_listen_p->listen_event);
}

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

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

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

  •  RIL_RequestFunc是最主要的一个,所有从RILJ发送过来的请求均由该接口发送给Vendor RIL;
    RIL_RadioStateRequest从LIBRIL获取modem的即时状态;
    RIL_Supports判断Vendor RIL是否支持某个请求命令;
    RIL_Cancel取消某个请求命令;
    RIL_GetVersion获取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; // 将从RILJ发送过来的请求发送给Vendor RIL
    typedef void (*RIL_RequestFunc) (int request, void *data,
    size_t datalen, RIL_Token t, RIL_SOCKET_ID socket_id);
    // 获取 modem 状态
    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
    struct RIL_Env {
// 请求完成,返回给LIBRIL
void (*OnRequestComplete)(RIL_Token t, RIL_Errno e,
void *response, size_t responselen); // Vendor RIL接收到从CP主动上报的消息后,传给LIBRIL
#if defined(ANDROID_MULTI_SIM)
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_*
*/
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
*/
// 指定时间内LIBRIL调用回调函数RIL_TimedCallback
void (*RequestTimedCallback) (RIL_TimedCallback callback,
void *param, const struct timeval *relativeTime);
};

代码路径: /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事件:

  void ril_event_loop()
{
int n;
fd_set rfds;
struct timeval tv;
struct timeval * ptv; for (;;) { // make local copy of read fd_set
memcpy(&rfds, &readFds, sizeof(fd_set));
....
// 从FD集合中选择可用的端口
n = select(nfds, &rfds, NULL, NULL, ptv);
....
// 处理timer队列中超时的事件
processTimeouts();
// 处理监测队列中的事件: listenCallback,
processReadReadies(&rfds, n);
// OK,fire pending list
firePending();
}
}

该线程,一直监听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, , 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, " ", );
} while (ret < && errno == EINTR);
}
}

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

   static void listenCallback (int fd, short flags, void *param) {
....
// 接受RILJ的链接请求
fdCommand = accept(fd, (sockaddr *) &peeraddr, &socklen); /* check the credential of the other side and only accept socket from
* phone process
*/
is_phone_socket = ; err = getsockopt(fdCommand, SOL_SOCKET, SO_PEERCRED, &creds, &szCreds); .... ret = fcntl(fdCommand, F_SETFL, O_NONBLOCK);
.... p_info->fdCommand = fdCommand; p_rs = record_stream_new(p_info->fdCommand, MAX_COMMAND_BYTES); p_info->p_rs = p_rs;
ril_event_set (p_info->commands_event, p_info->fdCommand, ,
p_info->processCommandsCallback, p_info);
// 添加指令事件`s_commands_event`
rilEventAddWakeup (p_info->commands_event);
// 建立新的连接,告知RILJ链接成功,并上报radio状态
onNewCommandConnect(p_info->socket_id);
}

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

   static void processCommandsCallback(int fd, short flags, void *param) {
// 循环读 RILD socket接口数据流
for (;;) {
/* loop until EAGAIN/EINTR, end of stream, or other error */
// 读取 socket数据流
ret = record_stream_get_next(p_rs, &p_record, &recordlen); if (ret == && p_record == NULL) {
/* end-of-stream */
break;
} else if (ret < ) {
break;
} else if (ret == ) { /* && p_record != NULL */
processCommandBuffer(p_record, recordlen, p_info->socket_id);
}
}
....
   // 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返回时,就可以调用对应的响应函数来处理返回的结果了:

    static CommandInfo s_commands[] = {
#include "ril_commands.h"
}; {, NULL, NULL}, //none
{RIL_REQUEST_GET_SIM_STATUS, dispatchVoid, responseSimStatus},
{RIL_REQUEST_ENTER_SIM_PIN, dispatchStrings, responseInts},
{RIL_REQUEST_ENTER_SIM_PUK, dispatchStrings, responseInts},
{RIL_REQUEST_ENTER_SIM_PIN2, dispatchStrings, responseInts},
{RIL_REQUEST_ENTER_SIM_PUK2, dispatchStrings, responseInts},
{RIL_REQUEST_CHANGE_SIM_PIN, dispatchStrings, responseInts},
{RIL_REQUEST_CHANGE_SIM_PIN2, dispatchStrings, responseInts},
....

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

参考文献

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wang2119/article/details/53392526

Android RILD运行机制详解的更多相关文章

  1. Android事件传递机制详解及最新源码分析——ViewGroup篇

    版权声明:本文出自汪磊的博客,转载请务必注明出处. 在上一篇<Android事件传递机制详解及最新源码分析--View篇>中,详细讲解了View事件的传递机制,没掌握或者掌握不扎实的小伙伴 ...

  2. JavaScript运行机制详解

    JavaScript运行机制详解   var test = function(){ alert("test"); } var test2 = function(){ alert(& ...

  3. Android事件分发机制详解

    事件分发机制详解 一.基础知识介绍 1.经常用的事件有:MotionEvent.ACTION_DOWN,MotionEvent.ACTION_MOVE,MotionEvent.ACTION_UP等 2 ...

  4. PULL解析XML的运行机制详解

    PULL解析简单易上手,基本上看一遍,基本上就会解析啦,但总是感觉对PULL解析的运行机制不是很了解,就总结了以下事件驱动到底是怎么执行的.. PULL: Android内置了PULL解析器.PULL ...

  5. Android开发——Android的消息机制详解

    )子线程默认是没有Looper的,Handler创建前,必须手动创建,否则会报错.通过Looper.prepare()即可为当前线程创建一个Looper,并通过Looper.loop()来开启消息循环 ...

  6. Linux find运行机制详解

    本文目录: 1.1 find基本用法示例 1.2 find理论部分 1.2.1 expression-operators 1.2.2 expression-options 1.2.3 expressi ...

  7. 【转载】Android异步消息处理机制详解及源码分析

    PS一句:最终还是选择CSDN来整理发表这几年的知识点,该文章平行迁移到CSDN.因为CSDN也支持MarkDown语法了,牛逼啊! [工匠若水 http://blog.csdn.net/yanbob ...

  8. JavaScript 运行机制详解:再谈Event Loop

    原文地址:http://www.ruanyifeng.com/blog/2014/10/event-loop.html 一年前,我写了一篇<什么是 Event Loop?>,谈了我对Eve ...

  9. Android的事件处理机制详解(二)-----基于监听的事件处理机制

    基于监听的事件处理机制 前言: 我们开发的app更多的时候是需要与用户的交互----即对用户的操作进行响应 这就涉及到了android的事件处理机制; android给我们提供了两套功能强大的处理机制 ...

随机推荐

  1. 2019.1.11 EDVT

    Processing Gain and Occupied Bandwidth ESA Basic Setup (11b)Span 110MHzRBW 100kHzVBW 100kHzSweep Tim ...

  2. 2019.1.7 Russia temperature control demo

    1layout 2导出Gerber 做钢网 3刷锡膏 4.1调SMT程序: a摆元件,写P/N位置 b定位检测点 4.2手贴元件 手别抖! 5过炉 温度270 6插件PCBA 做载板最方便,手插焊接也 ...

  3. mysql left join 查询

    inner join(等值连接) 只返回两个表中联结字段相等的行 left join(左联接) 返回包括左表中的所有记录和右表中联结字段相等的记录 左连接实例: SELECT t. NAME, t1. ...

  4. Python mode_a

    f = open("葫芦小金刚", mode="a", encoding="utf-8") # a, append 追加, 在文件的末尾写入 ...

  5. Redis学习第八课:Redis高级实用特性(二)

    Redis高级实用特性 4.持久化机制 Redis是一个支持持久化的内存数据库,也就是说Redis需要经常将内存中的数据同步到硬盘来保证持久化.Redis支持两种持久化方式:(1).snapshott ...

  6. python3:cmd运行python脚本,提示 No module named 'xxx'

    问题:cmd窗口运行python脚本,报错 C:\Users\xxx\Documents\GitHub\python3\main>python run_test.pyTraceback (mos ...

  7. ListBox 控件单击事件

    XAML: <ListBox x:Name="ItemBox" Grid.Column="0" Tap="ItemBox_Tap"&g ...

  8. PowerShell添加和部署WSP

    SharePoint PowerShell在SharePoint Product列表里边,然后以管理员权限启动. 1. 添加Solution 到 SharePoint Farm. Add-SPSolu ...

  9. how to get keyboard key with non blocking in terminal

    /************************************************************************** * how to get keyboard ke ...

  10. 利用git向github中推送文件

    /*游戏或者运动才能让我短暂的忘记心痛,现如今感觉学习比游戏和运动还重要——曾少锋*/ 如果对git不够熟悉的学者,可以参考:http://www.cnblogs.com/zengsf/p/75062 ...