Android RIL源码梳理(1) ——rild启动流程

一、RIL的基本架构

Android RIL (Radio Interface Layer)提供了Telephony服务和Radio硬件之间的抽象层。RIL负责数据的可靠传输、AT命令的发送以及response的解析。一般的,应用处理器(AP)通过AT命令集与无线通讯模块(基带/BP)通信。通信的方式又分为主动请求的request(诸如拨号、发短信……),以及Modem主动上报的例如信号强度、基站信息、来电、来短信等,称之为unsolicited response。

Android RIL源码梳理(1) ——rild启动流程_第1张图片

二、ril-daemon的启动:

首先我们来看一下ril-daemon进程的启动:ril-daemon进程是由init进程在系统开机时负责启动的,该进程在我们系统启动之后就一直存在在系统里面了。在init.rc(system/core/rootdir/)中我们可以看到如下代码:

//ril-daemon守护进程指的是system/bin/下面的可执行程序rild (此处的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 sdcard_rw log

从代码中我们可以看到rild可执行程序是由hardware/ril/rild/目录下的rild.c文件编译生成的。

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)

#编译的资源文件

LOCAL_SRC_FILES:= \

rild.c

LOCAL_SHARED_LIBRARIES := \

libcutils \

libril

ifeq ($(TARGET_ARCH),arm)

LOCAL_SHARED_LIBRARIES += libdl

endif # arm

LOCAL_CFLAGS := -DRIL_SHLIB

#编译生成rild可执行程序

LOCAL_MODULE:= rild

include $(BUILD_EXECUTABLE)

在该Android.mk文件中,还将radiooptions.c编译成二进制可执行文件radiooptions(system/bin/)

Android RIL源码梳理(1) ——rild启动流程_第2张图片

我们可以在shell下进入system/bin目录,执行以下radiooptions这个命令,我们就可以得到如下结果:

Android RIL源码梳理(1) ——rild启动流程_第3张图片

(注意:MTK平台对该模块进行了重新封装,因此在MTK手机的system/bin目录下我们找不到radiooptions可执行文件,如果有兴趣的同学想试着执行以下该命令,可以创建一个模拟器,然后在进入模拟器的/system/bin下面就可以看到上面的结果了)。

三、rild启动流程分析。

前面我们讲了,ril-daemon其实质就是rild可执行程序,rild又是由init进程启动的,那么它具体又完成了哪些工作呢? 这就需要我们具体来看rild的入口函数rild.c(hardware/ril/rild/)的实现了。这之前我们先 来了解一下几个动态库文件。

1、rild、libreference-ril.so 、libril.so、radiooptions的作用:

1)、rild(hardware/ril/rild/rild.c):仅实现一main函数作为整个ril层的入口点,负责完成初始化。

2)、libril.so(hardware/ril/libril/*):与rild结合相当紧密,是其共享库,编译时就已经建立了这一关系(感兴趣的同学可以看看hardware/ril/libril/Android.mk文件中动态库的调

用关系)。组成部分为ril.cpp,ril_event.cpp。libril.so驻留在rild这一守护进程中,主要完成同上层通信的工作,接受ril请求并传递给librefrence_ril.so,同时把librefrence_ril.so的

反馈回传给调用进程。

3)、librefrence_ril.so(hardware/ril/librefrence_ril/*):rild通过手动的dlopen方式加载,结合稍微松散,这也是因为librefrence.so主要负责跟Modem硬件通信的缘故。这样

做更方便替换或修改以适配更多的Modem种类。它转换来自libril.so的请求为AT命令,同时监控Modem的反馈信息,并传递回libril.so。在初始化时, rild通过符号RIL_Init获取一组函数指针并

以此与之建立联系。

4)、radiooptions(hardware/ril/rild/radiooptions.c):radiooptiongs通过获取启动参数,利用socket与rild通信,可供调试时配置Modem参数。

2、下面我们就来介绍一下rild的启动的流程,首先先来看看rild.c的main方法。在rild.c的main方法中,其实主要就完成了3件事,下面我们来一一解析这3件事。

Android RIL源码梳理(1) ——rild启动流程_第4张图片

1)、RIL_startEventLoop()

Android RIL源码梳理(1) ——rild启动流程_第5张图片Android RIL源码梳理(1) ——rild启动流程_第6张图片

Android RIL源码梳理(1) ——rild启动流程_第7张图片

Ril_event_loop就是一个for的无限循环,在这个for内部看到select函数了,其实select只监测read的fd_set,所要监听的fd都存放在全局变量readFds中,ptv决定select block的形态,

要么设定时间block直到到期,要么无限block直到有监听fd上数据可读,当select返回后就会查找是哪个事件的fd的触发的,然后通过firePending()呼叫该事件的callback。注意这是循环的内部,也

就是说每当select返回并执行其他动作之后,又会重新把readFds加到select中。熟悉Linux的同学应该很清楚这种IO多路复用的select机制(我们也可以在命令提示行下执行man select来

查看select函数的详细介绍)。这样就完成了我们rild启动的第一步就完成了,接下来我们再来看看第二步。

2)、RIL_Init

在rild.c的main方法是通过如下方式来调用RIL_Iint方法的:

dlHandle = dlopen(rilLibPath, RTLD_NOW); //加载libreference-ril.so动态库文件

rilInit = (const RIL_RadioFunctions *(*)(const struct RIL_Env *, int, char **))dlsym(dlHandle, "RIL_Init");

此处的rilLibPath是通过property属性值的方式来获取的。它的值为:“/system/lib/libreference-ril.so “动态库文件的属性值。下面就会通过调用动态库的方式来调用

reference-ril.c中RIL_Init()方法。

Android RIL源码梳理(1) ——rild启动流程_第8张图片

RIL_Init首先通过参数获取硬件接口的设备文件或模拟硬件接口的socket. 接下来便新开一个线程继续初始化,mainLoopmainLoop的主要任务是建立起与硬件的通信,然后通过read方法阻塞

等待硬件的主动上报或响应在注册一些基础回调(timeout,readerclose)后,mainLoop首先打开硬件设备文件,建立起与硬件的通信,s_device_paths_port是前面获取的设备路径参数,将其打开。

Android RIL源码梳理(1) ——rild启动流程_第9张图片

上面我们就完成了RIL_Init的整个过程了。

3)、RIL_register()

在RIL_init结束时的返回值为rilInit(RIL_RadioFunctions结构体类型),先来看看RIL_RadioFunctions结构体的构成,其中最重要的是onRequest,上层来的请求都由这个函数进行映射

后转换成对应的AT命令发给硬件。

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;

}

funcs = rilInit(&s_rilEnv, argc, rilArgv);

RIL_register(funcs); //rild通过RIL_register注册这一指针

RIL_register的另外一个重要的作用是:打开和上层通信的socket管道。有了这样一个通道,上层App就可以同过这个通道来和Modem端通信,Modem端也可以通过这个通道向上层App发送

响应消息了

Android RIL源码梳理(1) ——rild启动流程_第10张图片

我们也可以看到“rild“ socket的通信处理,是在它的回调函数listenCallback中进行处理的,有兴趣的同学可以自行学习一下。到此,rild的启动的流程我们就已经分析完了。

以上的分析都是基于google 4.0的源码的基础上进行的,和我们目前开发的MTK平台有一些差异,MTK自己重新实现了rildlibreference-ril.so、libril.so……,但是它基本的流程任然是一样的,

下面我们再简单看看彼此的差异点。

四、MTK与Google原生的一些差异

1、首先我们先来看一下init.rc的差异

Android RIL源码梳理(1) ——rild启动流程_第11张图片Android RIL源码梳理(1) ——rild启动流程_第12张图片

上边是MTK4.0平台的ril-daemon,下边是google4.0的源代码的。ril-daemon都是通过rild可执行程序来实现的;MTK平台实现了双卡功能,因此它在初始化时创建了两个Socket管道

“rild“和rild2,这两个socket就是用来上层app和Modem通信的通道。同时他还创建了rild-mtk-ut、rild-mtk-ut-2、rild-mtk-modem、rild-atci等socket通道,这些socket通道具体的作

用,希望有了解的同学共享一下。

2 在MTK4.1的代码中,我们在hardware/ril/rild/目录下也可以看到一个rild.c文件编译生成的rild可执行文件,但是前面我们又讲到MTK是有自己的实现的,那么这又是怎么回事了?

在ril目录下有一个CleanSpec.mk文件,里面只有如下一句:$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/rild)

也就是说,hardware/ril/*生成的rild可执行程序会被删除掉,也就是说init.rc中定义的ril-daemon的可执行文件并不是此处的rild。

那么那个init.rc中启动的那个rild在哪里呢?在vendor下,我们找到了MTK的rild,这个是以可执行程序的形式Release给我们的。因此我们也看不到它具体的实现的。

3、libreference-ril.so和libril.so动态库文件

前面我们讲到,在rild.c的main方法中会同过dlopen()的方式来加载动态库文件,而动态库文件的路径又是通过property属性文件的方式获取的,既然这样,那么我们就可以尝

试在adb shell下执行getprop方法来查看这个属性

得到的结果是

同样我们在vendor下找到了mtk-ril.so以及librilmtk.so库文件。

你可能感兴趣的:(android)