收藏 分享(赏)

Android-RIL结构分析与移植.docx

上传人:HR专家 文档编号:6549987 上传时间:2019-04-16 格式:DOCX 页数:14 大小:58.23KB
下载 相关 举报
Android-RIL结构分析与移植.docx_第1页
第1页 / 共14页
Android-RIL结构分析与移植.docx_第2页
第2页 / 共14页
Android-RIL结构分析与移植.docx_第3页
第3页 / 共14页
Android-RIL结构分析与移植.docx_第4页
第4页 / 共14页
Android-RIL结构分析与移植.docx_第5页
第5页 / 共14页
点击查看更多>>
资源描述

1、Android RIL 结构分析与移植介绍本文档对 Android RIL 部分的内容进行了介绍,其重点放在了 Android RIL 的原生代码部分。包括四个主题:1.Android RIL 框架介绍2.Android RIL 与 WindowsMobile RIL3.Android RIL porting4.Android RIL 的 java 框架在本文档中将 Android 代码中的重要模块列出进行分析,并给出了相关的程序执行流程介绍,以加深对模块间交互方式的理解。对于 java 代码部分,这里仅进行简单的介绍。如果需要深入了解,可以查看相关参考资料。本文档中还对 Android RI

2、L 的 Porting 部分内容进行了描述和分析。针对对 unix 操作系统环境并不熟悉的读者,本文档中所涉及到的相关知识包括:Unix file systemUnix socketUnix threadUnix 下 I/O 多路转接以上信息可以在任意一份描述 Unix 系统调用的文档中找到。1.Android RIL框架介绍术语:fd unix 文件描述符pipe unix 管道cond 一般是 condition variable 的缩写tty 通常使用 tty 来简称各种类型的终端设备unsolicited response 被动请求命令来自 basebandevent loop and

3、roid 的消息队列机制,由 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 进行通

4、信,它们是直接与 radio 通信的 AT 指令通道和用于传输包数据的通道,数据通道用于手机的上网功能。 对于 RIL 的 java 框架部分,也被分成了两个部分,一个是 RIL 模块,这个模块主要用于与下层的 rild 进行通信,另外一个是 Phone 模块,这个模块直接暴露电话功能接口给应用开发用户,供他们调用以进行电话功能的实现。1.2.Android RIL 目录结构:Android 的 RIL 模块位于 Android/hardware/ril 文件夹,有三个子模块:rild , libril , reference-rilinclude 文件夹:包含 RIL 头文件,最主要的是 r

5、il.hrild 文件夹:RIL 守护进程,开机时被 init 守护进程调用启动,里面仅有 main 函数作为入口点,负责完成 RIL 初始化工作。在 rild.c 文件中,将完成 ril 的加载过程,它会执行如下操作:动态加载 Vendor RIL 的.so 文件执行 RIL_startEventLoop()开启消息队列以进行事件监听通过执行 Vendor RIL 的 rilInit()方法来进行 Vendor RIL 与 libril 的关系建立。在 rild 文件夹中还包括一个 radiooptions.c 文件, 它的作用是通过串口将一些 radio 相关的参数直接传给 rild 来对

6、 radio 进行配置。libril 文件夹:在编译时 libril 被链入 rild,它为 rild 提供了 event 处理功能,还提供了在 rild 与 Vendor RIL 之间传递请求和响应消息的能力。Libril 提供的主要功能分布在两个主要方法内,一个是 RIL_startEventLoop()方法,另一个是 RIL_register()方法RIL_startEventLoop()方法所提供的功能就是启用 eventLoop 线程,开始执行 RIL 消息队列。RIL_register()方法的主要功能是启动名为 rild 的监听端口,等待 java 端通过 socket进行连接。

7、reference-ril 文件夹:Android 自带的 Vendor RIL 的参考实现。被编译成.so 文件,由于本部分是厂商定制的重点所在。所以被设计为松散耦合,且可灵活配置的。在 rild 中通过 opendl()的方式加载。librefrence.so 负责直接与 radio 通信,这包括将来自 libril 的指令转换为 AT 指令,并且将 AT 指令写入 radio 中。reference-ril 会接收调用者传来的参数,参数内容为与 radio 的通信方式。如通过串口连接 radio,那么参数为这种形式:-d /dev/ttySx1.3.Android RIL 中的消息(ev

8、ent)队列机制 :在 Android RIL 中,为了达到等待多路输入并且不出现阻塞的目的,使用了 IO 多路复用机制。如果使用阻塞 I/O 进行网络的读取写入 ,这意味着假如需要同时从两个网络文件描述符中读内容,那么如果读取操作在等待网络数据到来,这将可能很长时间阻塞在一个描述符上,另一个网络文件描述符不管有没有数据到来都无法被读取。一种解决方案是:如果使用非阻塞 I/O 进行网络的读取写入,在读取其中一个网络文件描述符如果阻塞将直接返回,再读取另外一个,这种方式的循环被称之为轮询。轮询方式确实能解决进行多路 io 操作时的阻塞问题,但是这种方法的不足之处是反复的执行读写调用将浪费 cpu

9、 时钟。I/O 多路转接技术在这里提供了另一种比较好的解决方案:它会先构造一张有关 I/O 描述符的列表,然后调用 select 函数,当 IO 描述符列表中的一个描述符准备好进行 I/O 时,该函数返回,并告知可以读或写哪个描述符。Android RIL 中消息队列的核心实现思想就是这种 I/O 多路转接技术。消息队列机制的实现在 ril_event.cpp 中,其中被定义的 ril_event 结构是消息的主体。每个 ril_event 结构,与一个 fd 句柄绑定(可以是文件,socket,管道等),并且带一个func 指针, 这个 func 指针所指的函数是个回调函数,它指定了当所绑定

10、的 fd 准备好进行读取时所要进行的操作。消息队列的开始点为 RIL_startEventLoop 函数。RIL_startEventLoop 在 ril.cpp 中实现,它的主要目的是通过 pthread_create(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 会把队列里所有

11、 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_even

12、t_add,实际通常用rilEventAddWakeup 来添加,这个方法除了会间接调用 ril_event_add 外,还会调用triggerEvLoop()函数来向 s_fdWakeupWrite 中写入一个空字符,这样 select()函数会返回并重新执行,新加入的文件描述符便得以被 select()加载并跟踪。如果在 ril_event 队列中任何一个 fd 已经准备好,则进入分析流程:processTimeouts(),processReadReadies(&rfds, n), firePending()其中 firePending()方法执行这个 event 的 func,也就是回

13、调函数。在 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 中还持有一个

14、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

15、中的 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

16、 调用,也就是说dispatchFunction 调用是程序流从 rild 转入 Vendor RIL 的分界点。RIL_register()函数将实现两个目地,一个是将 RIL_INIT 中获得的 RIL_RadioFunction进行注册,rild 通过此种方式保证自己持有一个 RIL_RadioFunction 实例,第二个是将s_fdListen 加入到消息队列机制中,开启 s_fdListen 的事件监听。 Vendor RIL 的初始化流程:RIL_Init 被调用后首先通过参数获取硬件接口的设备文件或模拟硬件接口的socket。(参见上文中对 reference-ril 文件夹的

17、介绍)接下来是创建 mainLoop 线程,并跳入到线程内执行。mainLoop 会建立起与硬件的通信,然后通过 read 方法阻塞等待硬件的主动上报或响应。mainLoop 还会调用initlizeCallBack()函数来向 radio 发送一系列的 AT 命令来进行 radio 的初始化设置工作。1.5.Android RIL 中 request 流程分析:上层应用开始向 rild 通过 socket 传输数据时,通过 RIL 消息队列机制,s_listen_event的回调函数 listenCallBack 将会被调用,开始进行数据流的分析与处理。接下来,s_fdCommand = a

18、ccept(s_fdListen, (sockaddr *) &peeraddr, &socklen),获取传入的 socket 描述符,也就是上层的 java RIL 传入的连接。然后,通过 record_stream_new()建立起一个 RecordStream, 将这个 record_stream 与s_fdCommand 绑定, RecordStream 实际上是一个用于存放数据的结构体,这个结构体提供了一些操作类来保证这个 RecordStream 所绑定的文件描述符被读取时里面的数据会被完整读取。一旦 s_fdCommand 中有数据,它的回调函数 processCommands

19、Callback()将会被调用,processCommandsCallback()通过 record_stream_get_next 阻塞读取 s_fdCommand 上发来的 数据, 直到收到一完整的 request。然后将其传递进 processCommandBuffer()函数,processCommandBuffer()正式进入了命令的解析部分。每个接收到的命令将以 RequestInfo 的形式存在。从 socket 过来的数据流, 是 Parcel 处理过的序列化字节流, 在这里会通过反序列化的方法提取出来。最前面的是 request 号,以及 token 域 (request 的

20、递增序列号 )。request 号是一个 CommandInfo,它在ril_command.h 中定义。接下来, 这个 RequestInfo 会被挂入 pending 的 request 队列, 执行具体的dispatchFunction(), 进行详细解析。dispatchFunction 方法有着多种实现,如 dispatchVoid, dispatchString, 它们的调用取决于 Parcel 的参数传入形式。比如说在 dispatchDial 方法中,Parcel 对象将被解析为RIL_Dial 结构。这是 disptachFunction 的任务之一,它的另一个任务就是调用o

21、nRequest()方法,并将解析的内容传入 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()

22、,再通过一个互斥的at_send_command_full_nolock()调用,完成最终的写出操作 ,在 writeline()中, 写出到初始化时打开的设备中。需要注意的是:at_send_command_full_nolock()在将指令写入 radio 后并不会直接返回,而是通过条件变量等待响应信息,得到响应信息后会携带这些信息返回。具体流程可以参考下面的 response 流程分析。1.6.Android RIL 中 response 流程分析:AT 的 response 有两种,一种是 unsolicited。另一种是普通 response,也就是命令的响应。 response 信

23、息的获取在 readerLoop()中。由 readline()函数读取上来。读取到的 line 将被传入 processLine()函数进行解析,processLine()函数首先会判断当前的响应是主动响应还是普通响应,如果是主动响应,将调用 handleUnsolicited()函数,如果为普通响应,那么将调用 handleFinalResponse()函数进行处理对响应串的主要的解析过程,由 at_tok.c 中的各种解析函数完成,提供字符串分析解析功能。 对主动上报的解析handleUnsolicited ()方法处理主动上报,它会调用 onUnsolicited()来进行进一步的解析

24、,这个函数在 Vendor-RIL 初始化时被传入 at_open()函数,onUnsolicited 只解析出头部(一般是+XXXX 的形式),然后按类型决定下一步操作,操作为RIL_onUnsolicitedResponse 和 RIL_requestTimedCallback 两种。在 RIL_onUnsolicitedResponse()函数中,通过 Parcel 传递,将RESPONSE_UNSOLICITED,unsolResponse(request 号)写入 Parcel,然后调用对应的responseFunction 完成进一步的的解析,将解析的数据写入 Parcel 中,最

25、后通过sendResponse()sendResponseRaw()blockingWrite()writeLine() 将数据写回给与应用层通信的 socket。在 RIL_requestTimedCallback()函数中。通过 event 机制实现的 timer 机制,回调对应的内部处理函数。通过 internalRequestTimedCallback 将回调添加到 event 循环,最终完成 callback 上挂的函数的回调。比如 pollSIMState,onPDPContextListChanged 等回调,不用返回上层,内部处理就可以。 对普通上报的解析IsFinalResp

26、onse()和 isFinalResponseError()所处理的是一条 AT 指令的响应上报,它们将转入 handleFinalResponse 方法。handleFinalResponse()函数会将所有响应信息装入 sp_response,这是一个 ATResponse结构,它的成员包括成功与否(success )以及一个中间结果( p_intermediates)。handleFinalResponse()在将响应结果保存至 sp_response 后, 设置 s_commandcond这一条件变量,此条件变量由 at_send_command_full_nolock 等待。at_s

27、end_command_full_nolock 获得到了完整的响应信息(在 sp_response 中),便开始进行响应信息的处理,最后由 RIL_onRequestComplete 将响应数据序列化并通过sendResponse 传递至与应用层通信的 socket,这一部分与RIL_onUnsolicitedResponse()函数的功能非常相似,请参考对主动上报的解析部分。2.Android RIL与 WindowsMobile RILAndroid RIL 与 WindowsMobile RIL 在设计思路上都是作为一个 radio 的抽象,为上层提供电话服务,但在实现方式上两者有着一定

28、的差异,这种差异的产生主要是源自操作系统机制的不同。 Android RIL 被实现为 HAL,相对于 windows mobile 中被实现为驱动的方式,Android RIL 模块的内聚性更为理想,可维护性也将更强,你也可以把 Android Ril 看做一个中间件。Android RIL 部分的开发工作,只需要拿到相应的 radio 文件描述符,就可以进行操作,无需关注 radio 的 I/O 驱动实现。2.1 两者在与应用通信上的实现对比WindowsMobile RIL 在实现与应用的通信时提供了 RIL Proxy,在这个层面中它定义了大量的 RIL_*()函数来作为电话服务请求。

29、这一点与 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 被设计

30、为异步执行的,他们分别使用两个队列来对它们的异步行为进行管理,执行命令下发和上报命令处理的过程也互不影响,下发命令与命令的相应响应之间的依赖关系由应用程序来捏合。在 android ril 中的 request 与 response 设计与 windows mobile 不同,它的命令与响应之间是同步的过程。也就是说一条命令被下发后,将等待执行结果,并进行处理,再上向上层发。而不是直接异步的进行处理和向上发送。3.Android RIL porting3.1.命名要实现某个无线模块的 RIL,需要创建一个实现了所有请求方法的共享库,保证Android 能够响应无线通信请求。所有的请求被定义 r

31、il.h 中。不同的 Modem 使用不同的端口,这个在 init.rc 中设置。Android 提供了一个参考 Vendor RIL,RIL 参考源码在 reference-ril。将你自己的 Vendor RIL 实现编译为共享库形式:libril-.so比如:libril-techfaith-124.so其中:libril:所有 vendor RIL 的开头:公司缩写:RIL 版本 numberso:文件扩展3.2.Android RIL 的配置与加载在 init.rc 文件中,将通过这种方式来进行 Android RIL 的加载。service ril-daemon /system/b

32、in/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

33、.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 模块则向应用程序开发者提供了一

34、系列的电话功能接口。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 原生代码中

35、 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()

36、方法读取到一个完整的响应,然后通过processResponse 来进行处理。4.2.Phone 模块结构Android 通过暴露 Phone 模块来供上层应用程序用户使用电话功能相关的接口。它为用户提供了诸如电话呼叫,短信息,SIM 卡管理之类的接口调用。它的核心部分是类 GSMPhone,这个是 Gsm 的电话实现,需要通过 PhoneFactory 获取这个GSMPhone。GSMPhone 并不是直接提供接口给上层用户使用,而是通过另外一个管理类TelephonyManager 来供应用程序用户使用。类 TelephonyManager 实现了 android 的电话相关操作。它主要使

37、用两个服务来访问 telephony 功能:1.ITelephony,提供给上层应用程序用户与 telephony 进行操作,交互的接口,在packages/apps/Phone 中由 PhoneInterfaceManager.java 实现。2.ItelephonyRegistry 提供了一个通知机制,将底层来的上报通知给框架中需要得到通知的部分,由 TelephonyRegistry.java 实现。GSMPhone 通过 PhoneNotifier 的实现者 DefaultPhoneNotifier 将具体的事件转化为函数调用,通知到 TelephonyRegistry。TelephonyRegistry 再通过两种方式通知给用户,其一是广播事件,另外一种是通过服务用户在 TelephonyRegistry 中注册的 IphoneStateListener 接口,实现回调(回调方式参见 android 的 aidl 机制)。

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 企业管理 > 管理学资料

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报