Android中RIL层详细分析报告.docx
- 文档编号:9125865
- 上传时间:2023-02-03
- 格式:DOCX
- 页数:88
- 大小:231.60KB
Android中RIL层详细分析报告.docx
《Android中RIL层详细分析报告.docx》由会员分享,可在线阅读,更多相关《Android中RIL层详细分析报告.docx(88页珍藏版)》请在冰豆网上搜索。
Android中RIL层详细分析报告
Android的功能介绍——整个RIL文件夹的分析
介绍
本文档对AndroidRIL局部的内容进展了介绍,其重点放在了AndroidRIL的原生代码局部。
包括四个主题:
1.AndroidRIL框架介绍
2.AndroidRIL与WindowsMobileRIL
3.AndroidRILporting
4.AndroidRIL的java框架
在本文档中将Android代码中的重要模块列出进展分析,并给出了相关的程序执行流程介绍,以加深对模块间交互方式的理解。
对于java代码局部,这里仅进展简单的介绍。
如果需要深入了解,可以查看相关参考资料。
本文档中还对AndroidRIL的Porting局部内容进展了描述和分析。
针对对unix操作系统环境并不熟悉的读者,本文档中所涉与到的相关知识包括:
Unixfilesystem
Unixsocket
Unixthread
Unix 下I/O多路转接
以上信息可以在任意一份描述Unix系统调用的文档中找到。
1.AndroidRIL框架介绍
术语:
fd Linux文件描述符
pipe Linux管道
cond 一般是conditionvariable的缩写
tty 通常使用tty来简称各种类型的终端设备
unsolicitedresponse 被动请求命令来自baseband
eventloop android的消息队列机制,由Linux的系统调用select()实现
init.rc init守护进程启动后被执行的启动脚本。
HAL 硬件抽象层〔HardwareAbstractionLayer,HAL〕
1.1AndroidRIL概况
AndroidRIL提供了无线硬件设备与服务之间的抽象层。
如下图展示了RIL在Android体系中的位置。
android的ril位于应用程序框架与内核之间,分成了两个局部,一个局部是rild,它负责socket与应用程序框架进展通信。
另外一个局部是VendorRIL,这个局部负责向下是通过两种方式与radio进展通信,它们是直接与radio通信的AT指令通道和用于传输包数据的通道,数据通道用于手机的上网功能。
对于RIL的java框架局部,也被分成了两个局部,一个是RIL模块,这个模块主要用于与下层的rild进展通信,另外一个是Phone模块,这个模块直接暴露功能接口给应用开发用户,供他们调用以进展功能的实现。
1.2AndroidRIL目录结构
Android的RIL模块位于Android/hardware/ril文件夹,有三个子模块:
rild,libril,reference-ril
所在目录结构:
/hardware/ril/
|--ril 〔无线电抽象层〕| |--include 〔头文件〕| |--libril 〔库〕| |--reference-cdma-sms〔cdma短信参考〕| |--reference-ril 〔ril参考〕| |--rild 〔ril后台服务程序〕
hardware/ril$ls
include libril reference-cdma-sms reference-ril rild
1.hardware/ril/rild$ls
Android.mk MODULE_LICENSE_APACHE2 NOTICE
2.hardware/ril/include/telephony$ls
ril_cdma_sms.h
3.hardware/ril/libril$ls
Android.mk NOTICE ril_event.h
MODULE_LICENSE_APACHE2
4.hardware/ril/reference-cdma-sms$ls
5.hardware/ril/reference-ril$ls
Android.mk atchannel.h at_tok.h misc.h NOTICE misc.c MODULE_LICENSE_APACHE2
●include文件夹:
●rild文件夹:
RIL守护进程,开机时被init守护进程调用启动,里面仅有main函数作为入口点,负责完成RIL初始化工作。
在rild.c文件中,将完成ril的加载过程,它会执行如下操作:
执行RIL_startEventLoop()开启消息队列以进展事件监听
通过执行VendorRIL的rilInit()方法来进展VendorRIL与libril的关系建立。
在rild文件夹中还包括一个radiooptions.c文件,它的作用是通过串口将一些radio相关的参数直接传给rild来对radio进展配置。
●libril文件夹:
在编译时libril被链入rild,它为rild提供了event处理功能,还提供了在rild与VendorRIL之间传递请求和响应消息的能力。
Libril提供的主要功能分布在两个主要方法内,一个是RIL_startEventLoop()方法,另一个是RIL_register()方法
RIL_startEventLoop()方法所提供的功能就是启用eventLoop线程,开始执行RIL消息队列。
RIL_register()方法的主要功能是启动名为rild的监听端口,等待java端通过socket进展连接。
●reference-ril文件夹:
Android自带的VendorRIL的参考实现。
被编译成.so文件,由于本局部是厂商定制的重点所在。
所以被设计为松散耦合,且可灵活配置的。
在rild中通过opendl()的方式加载。
librefrence.so负责直接与radio通信,这包括将来自libril的指令转换为AT指令,并且将AT指令写入radio中。
reference-ril会接收调用者传来的参数,参数内容为与radio的通信方式。
如通过串口连接radio,那么参数为这种形式:
-d/dev/ttySx
1.3.AndroidRIL中的消息〔event〕队列机制
在AndroidRIL中,为了达到等待多路输入并且不出现阻塞的目的,使用了IO多路复用机制。
如果使用阻塞I/O进展网络的读取写入,这意味着假设需要同时从两个网络文件描述符中读内容,那么如果读取操作在等待网络数据到来,这将可能很长时间阻塞在一个描述符上,另一个网络文件描述符不管有没有数据到来都无法被读取。
一种解决方案是:
如果使用非阻塞I/O进展网络的读取写入,在读取其中一个网络文件描述符如果阻塞将直接返回,再读取另外一个,这种方式的循环被称之为轮询。
轮询方式确实能解决进展多路io操作时的阻塞问题,但是这种方法的不足之处是反复的执行读写调用将浪费cpu时钟。
I/O多路转接技术在这里提供了另一种比拟好的解决方案:
它会先构造一X有关I/O描述符的列表,然后调用select函数,当IO描述符列表中的一个描述符准备好进展I/O时,该函数返回,并告知可以读或写哪个描述符。
AndroidRIL中消息队列的核心实现思想就是这种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:
structril_event{
structril_event*next;
structril_event*prev;
intfd;
intindex;
boolpersist;
structtimevaltimeout;
ril_event_cbfunc;
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,也就是回调函数。
在AndroidRIL初始化完成后,将有几个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〔非timerevent〕,供eventLoop唤醒时使用。
timer_list是存放timerevent的链表,在eventLoop唤醒时要对这些timerevent单独进展处理pending_list:
待处理(对其回调函数进展调用)的所有ril_event的链表。
1.4.AndroidRIL中初始化流程分析
● Rild的初始化流程
初始化流程从rild.c中的main函数开始,它被init守护进展调用执行:
首先在main()函数内会首先通过dlopen()函数加载VendorRIL〔在自带的参考实现中为librefrence_ril.so〕。
接着调用RIL_startEventLoop〔〕函数来启动消息队列机制。
调用librefrence_ril.so的RIL_Init()函数来进展VendorRIL的初始化。
RIL_Init()函数执行后会返回一个RIL_RadioFunction结构体,这个结构体内最重要的成员就是onRequest()方法。
onRequest()方法会被dispatchFunction调用,也就是说dispatchFunction调用是程序流从rild转入VendorRIL的分界点。
RIL_register()函数将实现两个目地,一个是将RIL_INIT中获得的RIL_RadioFunction进展注册,rild通过此种方式保证自己持有一个RIL_RadioFunction实例,第二个是将s_fdListen参加到消息队列机制中,开启s_fdListen的事件监听。
● VendorRIL的初始化流程:
RIL_Init被调用后首先通过参数获取硬件接口的设备文件或模拟硬件接口的socket。
〔参见上文中对reference-ril文件夹的介绍〕
接下来是创建mainLoop线程,并跳入到线程内执行。
mainLoop会建立起与硬件的通信,然后通过read方法阻塞等待硬件的主动上报或响应。
mainLoop还会调用initlizeCallBack()函数来向radio发送一系列的AT命令来进展radio的初始化设置工作。
1.5.AndroidRIL中request流程分析
上层应用开始向rild通过socket传输数据时,通过RIL消息队列机制,s_listen_event的回调函数listenCallBack将会被调用,开始进展数据流的分析与处理。
接下来s_fdmand=accept(s_fdListen,(sockaddr*)&peeraddr,&socklen),获取传入的socket描述符,也就是上层的javaRIL传入的连接。
然后,通过record_stream_new()建立起一个RecordStream,将这个record_stream与s_fdmand绑定,RecordStream实际上是一个用于存放数据的结构体,这个结构体提供了一些操作类来保证这个RecordStream所绑定的文件描述符被读取时里面的数据会被完整读取。
一旦s_fdmand中有数据,它的回调函数processmandsCallback()将会被调用,processmandsCallback()通过record_stream_get_next阻塞读取s_fdmand上发来的数据,直到收到一完整的request。
然后将其传递进processmandBuffer()函数,processmandBuffer()正式进入了命令的解析局部。
每个接收到的命令将以RequestInfo的形式存在。
从socket过来的数据流,是Parcel处理过的序列化字节流,在这里会通过反序列化的方法提取出来。
最前面的是request号,以与token域(request的递增序列号)。
request号是一个mandInfo,它在ril_mand.h中定义。
接下来,这个RequestInfo会被挂入pending的request队列,执行具体的dispatchFunction(),进展详细解析。
dispatchFunction方法有着多种实现,如dispatchVoid,dispatchString,它们的调用取决于Parcel的参数传入形式。
比如说在dispatchDial方法中,Parcel对象将被解析为RIL_Dial结构。
这是disptachFunction的任务之一,它的另一个任务就是调用onRequest()方法,并将解析的内容传入onRequest()方法。
从onRequest方法开始,程序控制流脱离了RILD,进入到了VendorRIL中。
onRequest方法会通过传入的请求类型来调用指定的request×××()方法,request×××()方法如此负责组装AT指令并下发给at_send_mand()方法集合中的一个,这个方法集合提供了针对不同类型AT指令的实现,如单行AT指令at_send_mand_singleline(),短信息指令at_send_mand_sms()等。
最后,执行at_send_mand_full(),再通过一个互斥的at_send_mand_full_nolock()调用,完成最终的写出操作,在writeline()中,写出到初始化时打开的设备中。
需要注意的是:
at_send_mand_full_nolock()在将指令写入radio后并不会直接返回,而是通过条件变量等待响应信息,得到响应信息后会携带这些信息返回。
具体流程可以参考下面的response流程分析。
1.6.AndroidRIL中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_mandcond这一条件变量,此条件变量由at_send_mand_full_nolock等待。
at_send_mand_full_nolock获得到了完整的响应信息〔在sp_response中〕,便开始进展响应信息的处理,最后由RIL_onRequestplete将响应数据序列化并通过sendResponse传递至与应用层通信的socket,这一局部与RIL_onUnsolicitedResponse()函数的功能非常相似,请参考对主动上报的解析局部。
2.AndroidRIL与 WindowsMobileRIL
AndroidRIL与WindowsMobileRIL在设计思路上都是作为一个radio的抽象,为上层提供服务,但在实现方式上两者有着一定的差异,这种差异的产生主要是源自操作系统机制的不同。
AndroidRIL被实现为HAL,相对于windowsmobile中被实现为驱动的方式,AndroidRIL模块的内聚性更为理想,可维护性也将更强,你也可以把AndroidRil看做一个中间件。
AndroidRIL局部的开发工作,只需要拿到相应的radio文件描述符,就可以进展操作,无需关注radio的I/O驱动实现。
WindowsMobileRIL在实现与应用的通信时提供了RILProxy,在这个层面中它定义了大量的RIL_***()函数来作为服务请求。
这一点与AndroidRIL的实现比拟相似,AndroidRIL中在ril.h内提供了一系列的宏来定义服务请求。
在Android中的rild功能类似于windowsmobileRIL的RILproxy。
它同样也是起到一个中介的作用,为上层接口向下传递请求,并上传回响应。
在windowsmobileRIL中要为每一个应用程序客户提供一份RilProxy实例。
对于这两种操作系统平台,RIL所定义的所有请求是不可更改的。
在windowsmobile的设计中,request与response被设计为异步执行的,他们分别使用两个队列来对它们的异步行为进展管理,执行命令下发和上报命令处理的过程也互不影响,下发命令与命令的相应响应之间的依赖关系由应用程序来捏合。
在androidril中的request与response设计与windowsmobile不同,它的命令与响应之间是同步的过程。
也就是说一条命令被下发后,将等待执行结果,并进展处理,再上向上层发。
而不是直接异步的进展处理和向上发送。
3.AndroidRILporting
要实现某个无线模块的RIL,需要创建一个实现了所有请求方法的共享库,保证Android能够响应无线通信请求。
所有的请求被定义ril.h中。
不同的Modem使用不同的端口,这个在init.rc中设置。
Android提供了一个参考VendorRIL,RIL参考源码在reference-ril。
将你自己的VendorRIL实现编译为共享库形式:
libril-
比如:
libril-techfaith-124.so 其中:
libril:
所有vendorRIL的开头
公司缩写
RIL版本number so:
文件扩
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Android RIL 详细 分析 报告