前言

在上一篇文章里(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. Java实现交换两个String

    在Java中我们所使用的实例变量其实都是一个引用,所以如果要求实现一个swap(String A, String B)这种函数时无法实现的,因为在类方法的定义中是先对行参进行地址传递,然后对形参修改, ...

  2. Maven私服Nexus详解

    maven的仓库只有两大类:1.本地仓库 2.远程仓库,在远程仓库中又分成了3种:2.1 中央仓库 2.2 私服 2.3 其它公共库. 私服是一种特殊的远程仓库,它是架设在局域网内的仓库服务,私服代理 ...

  3. OC基础:内存(进阶):retain.copy.assign的实现原理 分类: ios学习 OC 2015-06-26 17:36 58人阅读 评论(0) 收藏

    遍历构造器的内存管理 a.遍历构造器方法内部使用autorelease释放对象 b.通过遍历构造器生成的对象.不用释放. 内存的管理总结 1.想占用某个对象的时候,要让它的引用计数器+1(retain ...

  4. 一个两年Java的面试总结

    前言 16年毕业到现在也近两年了,最近面试了阿里集团(菜鸟网络,蚂蚁金服),网易,滴滴,点我达,最终收到点我达,网易offer,蚂蚁金服二面挂掉,菜鸟网络一个月了还在流程中...最终有幸去了网易.但是 ...

  5. iOS开发中,如何恢复到某一个版本(Cornerstone)

    Mac上的svn代码管理工具:Cornerstone 如何付恢复某个版本 第一:定位到你的工程,右上角边栏“Working Copy” ---->"Revert" 第二:选择 ...

  6. 大龄码农那些事——也谈996.ICU

    1.背景 近期Github突然有一个开源项目火了,叫“996.icu”,开源地址:https://github.com/996icu/996.ICU ,目前star的人数截止我写这篇博文时已经高达17 ...

  7. int类型转string类型c++

    前言 使用VS的过程中,经常会用到需要将int类型数据转换为字符串类型,便于显示信息等. 实现方法 c++11标准中的to_string函数,在VS安装文件的include文件中生成的只读文件,使用起 ...

  8. ThinkPHP3.2.3整合smarty模板(三)

    在smarty模板中使用thinkphp框架的U方法时要主要的问题: 1.不能直接使用{:U('Index/index')}: 2.正确的使用方法为:<!--{U("Login/log ...

  9. 压力测试命令行工具SuperBenchmarker

    压力测试命令行工具SuperBenchmarker SuperBenchmarker 是ㄧ个开源的类似于Apache ab的压力测试命令行工具.可以在 .NET 4.52+ 或者 .NET Core ...

  10. CTF-练习平台-Misc之 Linux??????

    八.Linux?????? 下载文件,解压后只得到一个没有后缀名的文件,添加后缀名为txt,打开搜索,关键词为“flag”,没有找到:改关键词为“key”得到答案