Android RIL结构分析与移植

Android RIL结构分析与移植

介绍java

本文档对Android RIL部分的内容进行了介绍,其重点放在了Android RIL的原生代码部分。android

包括四个主题:windows

1.Android RIL框架介绍数组

2.Android RIL与 WindowsMobile RIL网络

3.Android RIL portingapp

4.Android RIL的java框架框架

 

在本文档中将Android代码中的重要模块列出进行分析,并给出了相关的程序执行流程介绍,以加深对模块间交互方式的理解。异步

对于java代码部分,这里仅进行简单的介绍。若是须要深刻了解,能够查看相关参考资料。socket

本文档中还对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)

 

1.1.Android RIL概况:

 

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:

[c-sharp] view plaincopyprint?

  1. struct ril_event {  

  2.     struct ril_event *next;  

  3.     struct ril_event *prev;  

  4.     int fd;  

  5.     int index;  

  6.     bool persist;  

  7.     struct timeval timeout;  

  8.     ril_event_cb func;  

  9.     void *param;  

  10. };  

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:文件扩展

 

3.2.Android RIL的配置与加载

在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

相关文章
相关标签/搜索