本文档对Android RIL部分的内容进行了介绍,其重点放在了Android RIL的原生代码部分。包括四个主题:
1.Android RIL框架介绍
2.Android RIL与 WindowsMobile RIL
3.Android RIL porting
4.Android RIL的java框架
在本文档中将Android代码中的重要模块列出进行分析,并给出了相关的程序执行流程介绍,以加深对模块间交互方式的理解。
对于java代码部分,这里仅进行简单的介绍。如果需要深入了解,可以查看相关参考资料。
本文档中还对Android RIL的Porting部分内容进行了描述和分析。
针对对unix操作系统环境并不熟悉的读者,本文档中所涉及到的相关知识包括:
Unix file system
Unix socket
Unix thread
Unix 下I/O多路转接
以上信息可以在任意一份描述Unix系统调用的文档中找到。
1.Android RIL框架介绍 术语:
fd unix文件描述符
pipe unix管道
cond 一般是condition variable的缩写
tty 通常使用tty来简称各种类型的终端设备
unsolicited response 被动请求命令来自baseband
event loop android的消息队列机制,由unix的系统调用select()实现
init.rc init守护进程启动后被执行的启动脚本。
HAL 硬件抽象层(Hardware Abstraction Layer,HAL)
Android RIL提供了无线硬件设备与电话服务之间的抽象层。
下图展示了RIL在Android体系中的位置。
android的ril位于应用程序框架与内核之间,分成了两个部分,一个部分是rild,它负责socket与应用程序框架进行通信。另外一个部分是Vendor RIL,这个部分负责向下是通过两种方式与radio进行通信,它们是直接与radio通信的AT指令通道和用于传输包数据的通道,数据通道用于手机的上网功能。
对于RIL的java框架部分,也被分成了两个部分,一个是RIL模块,这个模块主要用于与下层的rild进行通信,另外一个是Phone模块,这个模块直接暴露电话功能接口给应用开发用户,供他们调用以进行电话功能的实现。
1.2.Android RIL目录结构:Android的RIL模块位于Android/hardware/ril文件夹,有三个子模块:rild , libril , reference-ril
●include文件夹:
包含RIL头文件,最主要的是ril.h
●rild文件夹:
RIL守护进程,开机时被init守护进程调用启动,里面仅有main函数作为入口点,负责完成RIL初始化工作。
在rild.c文件中,将完成ril的加载过程,它会执行如下操作:
动态加载Vendor RIL的.so文件
执行RIL_startEventLoop()开启消息队列以进行事件监听
通过执行Vendor RIL的rilInit()方法来进行Vendor RIL与libril的关系建立。
在rild文件夹中还包括一个radiooptions.c文件,它的作用是通过串口将一些radio相关的参数直接传给rild来对radio进行配置。
●libril文件夹:
在编译时libril被链入rild,它为rild提供了event处理功能,还提供了在rild与Vendor RIL之间传递请求和响应消息的能力。
Libril提供的主要功能分布在两个主要方法内,一个是RIL_startEventLoop()方法,另一个是RIL_register()方法
RIL_startEventLoop()方法所提供的功能就是启用eventLoop线程,开始执行RIL消息队列。
RIL_register()方法的主要功能是启动名为 rild 的监听端口,等待java 端通过socket进行连接。
●reference-ril文件夹:
Android自带的Vendor RIL的参考实现。被编译成.so文件,由于本部分是厂商定制的重点所在。所以被设计为松散耦合,且可灵活配置的。在rild中通过opendl()的方式加载。
librefrence.so负责直接与radio通信,这包括将来自libril的指令转换为AT指令,并且将AT指令写入radio中。
reference-ril会接收调用者传来的参数,参数内容为与radio的通信方式。如通过串口连接radio,那么参数为这种形式:-d /dev/ttySx
1.3.Android RIL中的消息(event)队列机制:在Android RIL中,为了达到等待多路输入并且不出现阻塞的目的,使用了IO多路复用机制。
如果使用阻塞I/O进行网络的读取写入,这意味着假如需要同时从两个网络文件描述符中读内容,那么如果读取操作在等待网络数据到来,这将可能很长时间阻塞在一个描述符上,另一个网络文件描述符不管有没有数据到来都无法被读取。
一种解决方案是:
如果使用非阻塞I/O进行网络的读取写入,在读取其中一个网络文件描述符如果阻塞将直接返回,再读取另外一个,这种方式的循环被称之为轮询。轮询方式确实能解决进行多路io操作时的阻塞问题,但是这种方法的不足之处是反复的执行读写调用将浪费cpu时钟。
I/O多路转接技术在这里提供了另一种比较好的解决方案:
它会先构造一张有关I/O描述符的列表,然后调用select函数,当IO描述符列表中的一个描述符准备好进行I/O时,该函数返回,并告知可以读或写哪个描述符。
Android RIL中消息队列的核心实现思想就是这种I/O多路转接技术。
消息队列机制的实现在ril_event.cpp中,其中被定义的ril_event结构是消息的主体。
每个ril_event结构,与一个fd句柄绑定(可以是文件,socket,管道等),并且带一个func指针,这个func指针所指的函数是个回调函数,它指定了当所绑定的fd准备好进行读取时所要进行的操作。
消息队列的开始点为RIL_startEventLoop函数。RIL_startEventLoop在ril.cpp中实现,它的主要目的是通过pthread_create(&s_tid_dispatch, &attr, eventLoop, NULL)建立一个dispatch线程,线程入口点在eventLoop. 而在eventLoop中,会调ril_event.cpp中的ril_event_loop()函数,建立起消息队列机制。
ril_event是一个带有链表行为的struct,它最主要的成员一个是fd,一个是func:
struct ril_event {
struct ril_event *next;
struct ril_event *prev;
int fd;
int index;
bool persist;
struct timeval timeout;
ril_event_cb func;
void *param;
};
初始化一个新ril_event的操作是通过ril_event_set()来完成的,并通过ril_event_add()加入到消息队列之中,add会把队列里所有ril_event的fd,放入一个fd集合readFds中。这样 ril_event_loop能通过一个多路复用I/O的机制(select)来等待这些fd。
在进入ril_event_loop()之前,在eventLoop中已经创建和挂入了s_wakeupfd_event,它是通过pipe的机制实现的,这个管道fd的回调函数并没有实现什么功能,它的目的只是为了让select方法能返回一次,这样select()方法就能重新跟踪新加入事件队列的fd和timeout设置。
所以在添加新fd到eventLoop时,往往不是直接调用ril_event_add,实际通常用rilEventAddWakeup来添加,这个方法除了会间接调用ril_event_add外,还会调用triggerEvLoop()函数来向s_fdWakeupWrite中写入一个空字符,这样select()函数会返回并重新执行,新加入的文件描述符便得以被select()加载并跟踪。
如果在ril_event队列中任何一个fd已经准备好,则进入分析流程:
processTimeouts(),processReadReadies(&rfds, n),firePending()
其中firePending()方法执行这个event的func,也就是回调函数。
在Android RIL初始化完成后,将有几个event被挂入到eventLoop中:
1. s_listen_event: 名为rild的socket,主要requeset & response通道
2. s_debug_event: 名为rild-debug的socket,调试用requeset & response通道
3. s_wakeupfd_event: 无名管道,用于队列主动唤醒
这其中最为重要的event就是s_listen_event,它作为request与response的通道实现。
在ril_event.cpp中还持有一个watch_table数组,一个timer_list链表和一个pending_list链表。
watch_table数组的目的很单纯,存放当前被eventLoop等待的ril_event(非timer event),供eventLoop唤醒时使用。
timer_list是存放timer event的链表,在eventLoop唤醒时要对这些timer event单独进行处理
pending_list:待处理(对其回调函数进行调用)的所有ril_event的链表。
1.4.Android RIL中初始化流程分析:
● Rild的初始化流程
初始化流程从rild.c中的main函数开始,它被init守护进行调用执行:
首先在main()函数内会首先通过dlopen()函数加载Vendor RIL(在自带的参考实现中为librefrence_ril.so)。接着调用RIL_startEventLoop()函数来启动消息队列机制。
调用librefrence_ril.so的RIL_Init()函数来进行Vendor RIL的初始化。RIL_Init()函数执行后会返回一个RIL_RadioFunction结构体,这个结构体内最重要的成员就是onRequest()方法。onRequest()方法会被dispatchFunction调用,也就是说dispatchFunction调用是程序流从rild转入Vendor RIL的分界点。
RIL_register()函数将实现两个目地,一个是将RIL_INIT中获得的RIL_RadioFunction进行注册,rild通过此种方式保证自己持有一个RIL_RadioFunction实例,第二个是将s_fdListen加入到消息队列机制中,开启s_fdListen的事件监听。
● Vendor RIL的初始化流程:
RIL_Init被调用后首先通过参数获取硬件接口的设备文件或模拟硬件接口的socket。(参见上文中对reference-ril文件夹的介绍)
接下来是创建mainLoop线程,并跳入到线程内执行。mainLoop会建立起与硬件的通信,然后通过read方法阻塞等待硬件的主动上报或响应。mainLoop还会调用initlizeCallBack()函数来向radio发送一系列的AT命令来进行radio的初始化设置工作。
1.5.Android RIL中request流程分析:
上层应用开始向rild通过socket传输数据时,通过RIL消息队列机制,s_listen_event的回调函数listenCallBack将会被调用,开始进行数据流的分析与处理。
接下来,s_fdCommand = accept(s_fdListen, (sockaddr *) &peeraddr, &socklen),获取传入的socket描述符,也就是上层的java RIL传入的连接。
然后,通过record_stream_new()建立起一个RecordStream, 将这个record_stream与s_fdCommand绑定, RecordStream实际上是一个用于存放数据的结构体,这个结构体提供了一些操作类来保证这个RecordStream所绑定的文件描述符被读取时里面的数据会被完整读取。
一旦s_fdCommand中有数据,它的回调函数processCommandsCallback()将会被调用,processCommandsCallback()通过record_stream_get_next阻塞读取s_fdCommand上发来的 数据, 直到收到一完整的request。然后将其传递进processCommandBuffer()函数,processCommandBuffer()正式进入了命令的解析部分。
每个接收到的命令将以RequestInfo的形式存在。从socket过来的数据流,是Parcel处理过的序列化字节流, 在这里会通过反序列化的方法提取出来。最前面的是request号, 以及token域(request的递增序列号)。request号是一个CommandInfo,它在ril_command.h中定义。
接下来, 这个RequestInfo会被挂入pending的request队列, 执行具体的dispatchFunction(), 进行详细解析。
dispatchFunction方法有着多种实现,如dispatchVoid, dispatchString, 它们的调用取决于Parcel的参数传入形式。比如说在dispatchDial方法中,Parcel对象将被解析为RIL_Dial结构。这是disptachFunction的任务之一,它的另一个任务就是调用onRequest()方法,并将解析的内容传入onRequest()方法。
从onRequest方法开始,程序控制流脱离了RILD,进入到了Vendor RIL中。
onRequest方法会通过传入的请求类型来调用指定的request×××()方法,request×××()方法则负责组装AT指令并下发给at_send_command()方法集合中的一个,这个方法集合提供了针对不同类型AT指令的实现,如单行AT指令at_send_command_singleline(),短信息指令at_send_command_sms()等。
最后,执行at_send_command_full(),再通过一个互斥的at_send_command_full_nolock()调用,完成最终的写出操作,在writeline()中,写出到初始化时打开的设备中。
需要注意的是:at_send_command_full_nolock()在将指令写入radio后并不会直接返回,而是通过条件变量等待响应信息,得到响应信息后会携带这些信息返回。具体流程可以参考下面的response流程分析。
1.6.Android RIL中response流程分析:
AT的response有两种,一种是unsolicited。另一种是普通response,也就是命令的响应。
response信息的获取在readerLoop()中。由readline()函数读取上来。
读取到的line将被传入processLine()函数进行解析,processLine()函数首先会判断当前的响应是主动响应还是普通响应,如果是主动响应,将调用handleUnsolicited()函数,如果为普通响应,那么将调用handleFinalResponse()函数进行处理
对响应串的主要的解析过程,由at_tok.c中的各种解析函数完成,提供字符串分析解析功能。
● 对主动上报的解析
handleUnsolicited ()方法处理主动上报,它会调用onUnsolicited()来进行进一步的解析,这个函数在Vendor-RIL初始化时被传入at_open()函数,onUnsolicited只解析出头部(一般是+XXXX的形式),然后按类型决定下一步操作,操作为 RIL_onUnsolicitedResponse和RIL_requestTimedCallback两种。
在RIL_onUnsolicitedResponse()函数中,通过Parcel传递,将 RESPONSE_UNSOLICITED,unsolResponse(request号)写入Parcel,然后调用对应的responseFunction完成进一步的的解析,将解析的数据写入Parcel中,最后通过sendResponse()→sendResponseRaw()→blockingWrite()→writeLine()将数据写回给与应用层通信的socket。
在RIL_requestTimedCallback()函数中。通过event机制实现的timer机制,回调对应的内部处理函数。通过internalRequestTimedCallback将回调添加到event循环,最终完成callback上挂的函数的回调。比如 pollSIMState,onPDPContextListChanged等回调, 不用返回上层,内部处理就可以。
● 对普通上报的解析
IsFinalResponse()和isFinalResponseError()所处理的是一条AT指令的响应上报,它们将转入handleFinalResponse方法。
handleFinalResponse()函数会将所有响应信息装入sp_response,这是一个ATResponse结构,它的成员包括成功与否(success)以及一个中间结果(p_intermediates)。
handleFinalResponse()在将响应结果保存至sp_response后, 设置s_commandcond这一条件变量,此条件变量由at_send_command_full_nolock等待。
at_send_command_full_nolock获得到了完整的响应信息(在sp_response中),便开始进行响应信息的处理,最后由RIL_onRequestComplete将响应数据序列化并通过sendResponse传递至与应用层通信的socket,这一部分与RIL_onUnsolicitedResponse()函数的功能非常相似,请参考对主动上报的解析部分。
2.Android RIL与 WindowsMobile RIL
Android RIL与WindowsMobile RIL 在设计思路上都是作为一个radio的抽象,为上层提供电话服务,但在实现方式上两者有着一定的差异,这种差异的产生主要是源自操作系统机制的不同。
Android RIL被实现为HAL,相对于windows mobile中被实现为驱动的方式,Android RIL模块的内聚性更为理想,可维护性也将更强,你也可以把Android Ril 看做一个中间件。Android RIL部分的开发工作,只需要拿到相应的radio文件描述符,就可以进行操作,无需关注radio的I/O驱动实现。
2.1两者在与应用通信上的实现对比
WindowsMobile RIL在实现与应用的通信时提供了RIL Proxy,在这个层面中它定义了大量的RIL_***()函数来作为电话服务请求。这一点与Android RIL的实现比较相似,Android RIL中在ril.h内提供了一系列的宏来定义电话服务请求。
在Android中的rild功能类似于windows mobile RIL的RIL proxy。它同样也是起到一个中介的作用,为上层接口向下传递请求,并上传回响应。在windows mobile RIL中要为每一个应用程序客户提供一份Ril Proxy实例。
对于这两种操作系统平台,RIL所定义的所有请求是不可更改的。
2.2两者在线程结构与回调机制上的对比
在windows mobile的设计中,request与response被设计为异步执行的,他们分别使用两个队列来对它们的异步行为进行管理,执行命令下发和上报命令处理的过程也互不影响,下发命令与命令的相应响应之间的依赖关系由应用程序来捏合。
在android ril中的request与response设计与windows mobile不同,它的命令与响应之间是同步的过程。也就是说一条命令被下发后,将等待执行结果,并进行处理,再上向上层发。而不是直接异步的进行处理和向上发送。
3.Android RIL porting 3.1.命名
要实现某个无线模块的RIL,需要创建一个实现了所有请求方法的共享库,保证Android能够响应无线通信请求。所有的请求被定义ril.h中。
不同的Modem使用不同的端口,这个在init.rc中设置。
Android提供了一个参考Vendor RIL,RIL参考源码在reference-ril。
将你自己的Vendor RIL实现编译为共享库形式:
libril-<companyname>-<RIL version>.so
比如:
libril-techfaith-124.so
其中:
libril:所有vendor RIL的开头
<companyname>:公司缩写
<RIL version>:RIL版本number
so:文件扩展
在init.rc文件中,将通过这种方式来进行Android RIL的加载。
service ril-daemon /system/bin/rild -l /system/lib/libreference-ril.so -- -d /dev/ttyS0
也可以手动加载:
/system/bin/rild -l /system/lib/libreference-ril.so -- -d /dev/ttyS0
这两种方式,都将启动rild守护进程,然后通过-l参数将libreference-ril.so共享库链入,libreference-ril.so的参数-d是指加载一个串口设备,/dev/ttyS0则是这个串口设备的具体设备文件,除了参数-d外,还有-s代表加载类型为socket的设备,-p代表回环接口。
3.3.Android RIL的编译结构rild:
被编译成可执行文件,rild以守进程的形式执行。
libril:
将被编译为共享库,并被链入rild。
Vendor RIL:
可以以两种方式来运行,如果定义了RIL_SHLIB宏,那么它将被编译成共享库,如果没定义RIL_SHLIB宏,它将以守护进程程序的方式被调用执行。
4.Android RIL的java框架
Android RIL的Java部分也被分为了两个模块,RIL模块与Phone模块。其中RIL模块负责进行请求以及相应的处理,它将直接与RIL的原声代码进行通信。而Phone模块则向应用程序开发者提供了一系列的电话功能接口。
4.1.RIL模块结构
在RIL.java中实现了几个类来进行与下层rild的通信。
它实现了如下几个类来完成操作:
RILRequest:代表一个命令请求
RIL.RILSender:负责AT指令的发送
RIL.RILReceiver:用于处理主动和普通上报信息
RIL.RILSender与RIL.RILReceiver是两个线程。
RILRequest提供了obtain()方法,用于得到具体的request操作,这些操作被定义在RILConstants.java中(RILConstants.java中定义的request命令与RIL原生代码中ril.h中定义的request命令是相同的),然后通过send()函数发送EVENT_SEND,在RIL_Sender线程中处理这个EVENT_SEND将命令写入到stream(socket)中去。Socket是来自常量SOCKET_NAME_RIL,它与RIL 原生代码部分的s_fdListen所指的socket是同一个。
当有上报信息来到时,系统将通过RILReciver来得到信息,并进行处理。在RILReciver的生命周期里,它一直监视着SOCKET_NAME_RIL这个socket,当有数据到来时,它将通过readRilMessage()方法读取到一个完整的响应,然后通过processResponse来进行处理。
4.2.Phone模块结构
Android通过暴露Phone模块来供上层应用程序用户使用电话功能相关的接口。它为用户提供了诸如电话呼叫,短信息,SIM卡管理之类的接口调用。它的核心部分是类GSMPhone,这个是Gsm的电话实现,需要通过PhoneFactory获取这个GSMPhone。
GSMPhone并不是直接提供接口给上层用户使用,而是通过另外一个管理类TelephonyManager来供应用程序用户使用。
类TelephonyManager实现了android的电话相关操作。它主要使用两个服务来访问telephony功能:
1.ITelephony,提供给上层应用程序用户与telephony进行操作,交互的接口,在packages/apps/Phone中由PhoneInterfaceManager.java实现。
2.ItelephonyRegistry提供了一个通知机制,将底层来的上报通知给框架中需要得到通知的部分,由TelephonyRegistry.java实现。
GSMPhone通过PhoneNotifier的实现者DefaultPhoneNotifier将具体的事件转化为函数调用,通知到TelephonyRegistry。TelephonyRegistry再通过两种方式通知给用户,其一是广播事件,另外一种是通过服务用户在TelephonyRegistry中注册的IphoneStateListener接口,实现回调(回调方式参见android的aidl机制)。
参考资料
相关网站:
http://code.google.com/android/
http://www.ibm.com/developerworks/cn/opensource/theme/android/
http://en.wikipedia.org/wiki/Android_%28operating_system%29
相关书籍:
《UNIX环境高级编程》
《UNIX编程艺术》
《Android系统原理及开发要点详解》
相关推荐
### Android RIL 结构分析与移植 #### 一、Android RIL 框架概述 Android RIL(Radio Interface Layer)是Android系统中一个重要的组件,它为移动设备的无线电硬件提供了一个统一的抽象接口。RIL使得Android系统...
### Android RIL结构分析与移植概述 Android RIL(Radio Interface Layer)是Android系统中一个关键的组件,它充当了应用程序框架与底层无线硬件设备之间的桥梁。RIL的设计旨在提供一个标准化的接口,使得上层应用...
### Android RIL 结构分析与移植 #### 一、Android RIL 与 Windows Mobile RIL 的设计理念及实现差异 在移动通信系统中,RIL(Radio Interface Layer)扮演着核心角色,它负责处理与基带处理器(即无线收发器)的...
### Android RIL结构分析与移植(4):深入探索RIL与Phone模块 #### RIL模块解析 在Android系统中,RIL(Radio Interface Layer)是连接应用层和硬件层的关键桥梁,负责处理所有与无线通信相关的事务。RIL模块在...
### Android RIL结构分析与移植 (2):深入解析RIL初始化及请求流程 #### 一、RIL初始化流程详解 ##### Rild的初始化过程: Rild,即Radio Interface Layer Daemon,是Android系统中用于管理和控制无线通信硬件的...
### Android-RIL结构分析与移植知识点详解 #### 一、Android RIL框架介绍 ##### 1.1 Android RIL概述 - **定义**: Android RIL(Radio Interface Layer)提供了无线硬件设备与服务之间的抽象层,使得上层应用程序...
【标题】:“将Android平台的RIL层移植到基于LINUX的通用平台的研究与实现” 【描述】:该研究探讨了如何将Android系统的Radio Interface Layer(RIL)移植到基于LINUX的通用平台,旨在为非Android环境提供3G网络...
【Android RIL 结构分析与移植】 Android RIL(Radio Interface Layer)是Android操作系统中一个至关重要的组件,它充当了操作系统与无线硬件设备之间的接口,处理电话、数据连接和其他无线电功能。本文档深入剖析...
本文档主要针对RIL的结构、原理以及移植进行深入分析。 1. Android RIL框架介绍 Android RIL架构主要由两部分组成:RILD(RIL Daemon)和Vendor RIL。RILD运行在用户空间,通过Unix套接字与上层应用程序框架通信,...
RIL移植是一项关键的技术工作,对于Android系统尤其重要,因为Android广泛使用RIL来与各种不同类型的硬件进行通信。本文将深入探讨RIL移植的相关知识点。 1. RIL的结构与功能: RIL主要由三部分组成:上层接口...
"Android中RIL层详细分析" Android 中的 RIL(Radio Interface Layer, радио接口层)是负责无线硬件设备与电话服务之间的抽象层,位于应用程序框架与内核之间。RIL 的主要作用是提供了电话功能的实现,包括...
4.4.3 分析sensor源代码看Android api 与硬件平台的衔接 104 4.5 移植总结 116 4.5.1 移植各个Android部件的方式 116 4.5.2 移植技巧之一——不得不说的辅助工作 117 第5章 goldfish下的驱动解析 ...
本文旨在分析Android RIL层的结构和工作原理,实现其在纯LINUX平台上的移植,为基于LINUX的通用平台提供一套成熟的RIL解决方案。 首先,理解Android RIL层的背景和意义至关重要。RIL层作为硬件抽象层的一部分,它的...
【标题】:“华为通用reference-ril”是华为针对Android系统设计的一个特定的Radio Interface Layer(RIL)实现,它主要用于连接手机的射频模块与操作系统之间的通信。RIL是Android系统中的关键组件,负责处理手机的...
4.4.3 分析sensor源代码看Android api 与硬件平台的衔接 104 4.5 移植总结 116 4.5.1 移植各个Android部件的方式 116 4.5.2 移植技巧之一——不得不说的辅助工作 117 第5章 goldfish下的驱动解析 ...
4.4.3 分析sensor源代码看Android api 与硬件平台的衔接 104 4.5 移植总结 116 4.5.1 移植各个Android部件的方式 116 4.5.2 移植技巧之一——不得不说的辅助工作 117 第5章 goldfish下的驱动解析 ...
#### 三、Android RIL的移植与实现 ##### 3.1 Android RIL的移植 移植Android RIL涉及以下几个步骤: - **无线通讯模块驱动加载**:确保无线通讯模块能够正确加载并初始化。 - **配置与加载**:设置必要的配置...
1.1.4 android系统开发(移植)和应用开发 /11 1.2 获取和编译android的源码 /13 1.2.1 环境配置 /13 1.2.2 获取android源码 /14 1.2.3 编译android的源码及其工具包 /16 1.2.4 运行android系统 /21 1.3 开发环境...