Android中RIL层详细分析
更新时间:2024-04-25 06:13:01 阅读量: 综合文库 文档下载
- Android ril推荐度:
- 相关推荐
Android的电话功能介绍——整个RIL文件夹的分析
介绍
本文档对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 Linux文件描述符 pipe Linux管道
cond 一般是condition variable的缩写
tty 通常使用tty来简称各种类型的终端设备 unsolicited response 被动请求命令来自baseband
event loop android的消息队列机制,由Linux的系统调用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
所在目录结构:/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 radiooptions.c rild.c 2.hardware/ril/include/telephony$ ls
ril_cdma_sms.h ril.h
3.hardware/ril/libril$ ls
Android.mk NOTICE ril_event.h ril.cpp ril_event.cpp ril_commands.h ril_unsol_commands.h MODULE_LICENSE_APACHE2
4.hardware/ril/reference-cdma-sms$ ls
Android.mk reference-cdma-sms.c reference-cdma-sms.h
5.hardware/ril/reference-ril$ ls
Android.mk atchannel.h at_tok.h misc.h NOTICE atchannel.c at_tok.c ril_event.h reference-ril.c misc.c MODULE_LICENSE_APACHE2
●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-
比如:
libril-techfaith-124.so 其中:
libril:所有vendor RIL的开头
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接口,实现回调(回调方式参见android的aidl机制)。 参考资料
相关网站:
http://code.google.com/android/ http://android-dls.com
http://www.ibm.com/developerworks/cn/opensource/theme/android/ http://en.wikipedia.org/wiki/Android_(operating_system)
5 电话功能概述
Android的Radio Interface Layer (RIL)提供了电话服务和的radio硬件之间的抽象层。 Radio Interface Layer RIL(Radio Interface Layer)负责数据的可靠传输、AT命令的发送以及response的解析。应用处理器通过AT命令集与带GPRS功能的无线通讯模块通信。
AT command由Hayes公司发明,是一个调制解调器制造商采用的一个调制解调器命令语言,每条命令以字母\开头。 JAVA Framework
代码的路径为:
frameworks/base/telephony/java/android/telephony android.telephony以及android.telephony.gsm
Core native:
在hardware/ril目录中,提供了对RIL支持的本地代码,包括4个文件夹: hardware/ril/include hardware/ril/libril hardware/ril/reference-ril hardware/ril/rild
kernel Driver
在Linux内核的驱动中,提供了相关的驱动程序的支持,可以建立在UART或者SDIO,USB等高速的串行总线上。
电话功能各个部分:
hardware/ril/include/telephony/目录中的ril.h文件是ril部分的基础头文件。 其中定义的结构体RIL_RadioFunctions如下所示: typedef struct { int version;
RIL_RequestFunc onRequest;
RIL_RadioStateRequest onStateRequest; RIL_Supports supports; RIL_Cancel onCancel;
RIL_GetVersion getVersion; } RIL_RadioFunctions;
RIL_RadioFunctions中包含了几个函数指针的结构体,这实际上是一个移植层的接口,下层的库实现后,由rild守护进程得到这些函数指针,执行对应的函数。 几个函数指针的原型为:
typedef void (*RIL_RequestFunc) (int request, void *data, size_t datalen, RIL_Token t); typedef RIL_RadioState (*RIL_RadioStateRequest)(); typedef int (*RIL_Supports)(int requestCode); typedef void (*RIL_Cancel)(RIL_Token t);
typedef const char * (*RIL_GetVersion) (void);
其中最为重要的函数是onRequest(),它是一个请求执行的函数。
5.1 rild守护进程
rild 守护进程的文件包含在hardware/ril/rild目录中,其中包含了rild.c和radiooptions.c两个文件,这个目录中的文件经过编译后生成一个可执行程序,这个程序在系统的安装路径在:
/system/bin/rild
rild.c是这个守护进程的入口,它具有一个主函数的入口main,执行的过程是将上层来的请求都由这个函数RIL_RadioFunctions{onReques()}的方法进行映射后转换成对应的AT命令的字符串,发给下层的硬件执行。在运行过程中,使用dlopen 打开路径为/system/lib/中名称为libreference-ril.so的动态库,然后从中取出 RIL_Init符号来运行。
ril_register()注册:RIL_Init符号是一个函数指针,执行这个函数后,返回的是一个RIL_RadioFunctions类型的指针。得到这个指针后,调用RIL_register()函数,将这个指针注册到libril库之中,然后进入循环ril_event_loop()。
事实上,这个守护进程提供了一个申请处理的框架,而具体的功能都是在libril.so和libreference-ril.so中完成的。 附:
RIL守护进程,开机时被init守护进程调用启动,里面仅有main函数作为入口点,负责完成RIL初始化工作。
在rild.c文件中,将完成ril的加载过程,它会执行如下操作: 动态加载Vendor RIL的.so文件
执行RIL_startEventLoop()开启消息队列以进行事件监听
通过执行Vendor RIL的rilInit()方法来进行Vendor RIL与libril的关系建立。
在rild文件夹中还包括一个radiooptions.c文件,它的作用是通过串口将一些radio相关的参数直接传给rild来对radio进行配置。
5.2 libreference-ril.so动态库
libreference-ril.so动态库的路径是: hardware/ril/reference-ril
其中主要的文件是reference-ril.c和atchannel.c。这个库必须实现的是一个名称为RIL_Init的函数,这个函数执行的结果是返回一个RIL_RadioFunctions结构体的指针,指针指向函数指针。
这个库在执行的过程中需要创建一个线程来执行实际的功能。在执行的过程中,这个库将打开一个/dev/ttySXXX的终端(终端的名字是从上层传入的),然后利用这个终端控制硬件执行。 附:
在编译时libril被链入rild,它为rild提供了event处理功能,还提供了在rild与Vendor RIL之间传递请求和响应消息的能力。
Libril提供的主要功能分布在两个主要方法内,一个是RIL_startEventLoop()方法,另一个是RIL_register()方法
RIL_startEventLoop()方法所提供的功能就是启用eventLoop线程,开始执行RIL消息队列。 RIL_register()方法的主要功能是启动名为rild 的监听端口,等待java 端通过socket进行连接。
5.3 libril.so动态库
libril.so库的目录是: hardware/ril/libril
其中主要的文件为ril.cpp,这个库主要需要实现的以下几个接口为: RIL_startEventLoop(void);
void RIL_setcallbacks (const RIL_RadioFunctions *callbacks); RIL_register (const RIL_RadioFunctions *callbacks);
RIL_onRequestComplete(RIL_Token t, RIL_Errno e, void *response, size_t responselen); void RIL_onUnsolicitedResponse(int unsolResponse, void *data, size_t datalen);
RIL_requestTimedCallback (RIL_TimedCallback callback, void *param, const struct timeval *relativeTime);
这些函数也是被rild守护进程调用的,不同的vendor可以通过自己的方式实现这几个接口,这样可以保证RIL可以在不同系统的移植。其中 RIL_register()函数把外部的RIL_RadioFunctions结构体注册到这个库之中,在恰当的时候调用相应的函数。在执行的过程中,这个库处理了一些将请求转换成字符串的功能。 附:
Android自带的VendorRIL的参考实现。被编译成.so文件,由于本部分是厂商定制的重点所在。所以被设计为松散耦合,且可灵活配置的。在rild中通过opendl()的方式加载。
librefrence.so负责直接与radio通信,这包括将来自libril的指令转换为AT指令,并且将AT指令写入radio中。
reference-ril会接收调用者传来的参数,参数内容为与radio的通信方式。如通过串口连接radio,那么参数为这种形式:-d /dev/ttySx
5.4 ril层的所有代码分析
5.4.1 ril/rild下的文件 rild.c->mian()为函数入口
int main(int argc, char **argv) { // .... //
OpenLib: #endif
switchUser();
//打开dlopen()加载vendor RIL 获取由RIL_register(funcs);注册进来的参数,并解析 dlHandle = dlopen(rilLibPath, RTLD_NOW); if (dlHandle == NULL) {
fprintf(stderr, \ exit(-1); }
//1:消息队列的入口1.到select阻塞//每当看到打印信息,不按顺序打下来说明阻塞 RIL_startEventLoop();
//通过dlsym函数得到rilInit函数指针的引用
rilInit = (const RIL_RadioFunctions *(*)(const struct RIL_Env *, int, char **))dlsym(dlHandle, \
if (rilInit == NULL) {
fprintf(stderr, \ exit(-1); }
if (hasLibArgs) {
rilArgv = argv + i - 1; argc = argc -i + 1; } else {
static char * newArgv[MAX_LIB_ARGS]; static char args[PROPERTY_VALUE_MAX]; rilArgv = newArgv;
property_get(LIB_ARGS_PROPERTY, args, \ argc = make_argv(args, rilArgv); }
// Make sure there's a reasonable argv[0] rilArgv[0] = argv[0];
//2:利用得到的rilInit函数指针,调用真正的RIL_Init funcs = rilInit(&s_rilEnv, argc, rilArgv);
RIL_register(funcs); done:
while(1) {
// sleep(UINT32_MAX) seems to return immediately on bionic sleep(0x00ffffff); }
5.4.2 ril/libril->ril.cpp中RIL_startEventLoop()函数分析 RIL_startEventLoop(void) {
int ret;
pthread_attr_t attr;
LOGD(\
/* spin up eventLoop thread and wait for it to get started */ s_started = 0;
pthread_mutex_lock(&s_startupMutex); pthread_attr_init (&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
LOGD(\ ret = pthread_create(&s_tid_dispatch, &attr,eventLoop, NULL);//创建线程,为入口函数 LOGD(\ while (s_started == 0) {
pthread_cond_wait(&s_startupCond, &s_startupMutex); }
pthread_mutex_unlock(&s_startupMutex); if (ret < 0) {
LOGE(\ return; } }
---------------------------------------------------------
eventLoop(void *param) {
1.---ril_event_init();//初始化 void ril_event_init() {
MUTEX_INIT();
LOGD(\ FD_ZERO(&readFds); init_list(&timer_list); init_list(&pending_list);
memset(watch_table, 0, sizeof(watch_table)); }
2.--- ret = pipe(filedes);
3.---ril_event_set (&s_wakeupfd_event, s_fdWakeupRead, true,processWakeupCallback, NULL);
在ril/libril/ril_event.cpp实现
void ril_event_set(struct ril_event * ev, int fd, bool persist, ril_event_cb func, void * param) {
LOGD(\ril_event_set(struct ril_event * ev, int fd, bool persist, ril_event_cb func, void * param)-\
dlog(\ memset(ev, 0, sizeof(struct ril_event));
ev->fd = fd; ev->index = -1;
ev->persist = persist; ev->func = func; ev->param = param;
fcntl(fd, F_SETFL, O_NONBLOCK); }
3.1processWakeupCallback
static void processWakeupCallback(int fd, short flags, void *param) { char buff[16]; int ret;
LOGD(\read(s_fdWakeupRead, sizeof(buff));---\
LOGV(\/* empty our wakeup socket out */ do {
ret = read(s_fdWakeupRead, &buff, sizeof(buff)); } while (ret > 0 || (ret < 0 && errno == EINTR)); }
4.---rilEventAddWakeup (&s_wakeupfd_event);
static void rilEventAddWakeup(struct ril_event *ev) {
LOGD(\ril_event_add(ev);---\ ril_event_add(ev);
LOGD(\ triggerEvLoop(); }
&buff,
4.1---void ril_event_add(struct ril_event * ev) 在ril/libril/ril_event.cpp实现 {
LOGD(\ dlog(\ MUTEX_ACQUIRE();
for (int i = 0; i < MAX_FD_EVLL) { //1:
watch_table[i] = ev;//把上面ril_event_add函数添加的事件_wakeupfd_event结构体添加到这个数组来 ev->index = i;
dlog(\ dump_event(ev); //2:
FD_SET(ev->fd, &readFds);
if (ev->fd >= nfds) nfds = ev->fd+1; dlog(\ break;ENTS; i++) { if (watch_table[i] == NU } }
MUTEX_RELEASE();
dlog(\}
4.2---static void triggerEvLoop() { int ret;
LOGD(\ if (!pthread_equal(pthread_self(), s_tid_dispatch)) { /* trigger event loop to wakeup. No reason to do this, * if we're in the event loop thread */ do {
ret = write (s_fdWakeupWrite, \ } while (ret < 0 && errno == EINTR); } }
5.ril_event_loop(); }
<一>--RIL层代码分析-RIL_RadioFunctions *RIL_Init(funcs =rilInit()->mainloop()
ril/rild/rild.c->main()为函数入口
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1.消息队列select为非阻塞的去轮询事件 2.read的阻塞的去读取上层发下来的命令,并响应
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
int main(int argc, char **argv) {
const char * rilLibPath = NULL; char **rilArgv; void *dlHandle;
const RIL_RadioFunctions *(*rilInit)(const struct RIL_Env *, int, char **);
const RIL_RadioFunctions *funcs; char libPath[PROPERTY_VALUE_MAX]; unsigned char hasLibArgs = 0; ........ OpenLib: #endif
switchUser();
/*打开dlopen()函数,就会动态去加载动态库vendor RIL 获取由RIL_register(funcs);注册进来的参数,并解析*/ dlHandle = dlopen(rilLibPath, RTLD_NOW); if (dlHandle == NULL) {
fprintf(stderr, \ exit(-1); }
/*消息队列的入口,添加到select,用阻塞方式去读取那些ril_event_set()的数据##每当看到打印信息,不按顺序打下来说明阻塞##*/
RIL_startEventLoop();
/*通过dlsym函数得到rilInit函数指针的引用*/
rilInit = (const RIL_RadioFunctions *(*)(const struct RIL_Env *, int, char **))dlsym(dlHandle, \ if (rilInit == NULL) {
fprintf(stderr, \ exit(-1); }
if (hasLibArgs) { rilArgv = argv + i - 1; argc = argc -i + 1; } else {
static char * newArgv[MAX_LIB_ARGS]; static char args[PROPERTY_VALUE_MAX]; rilArgv = newArgv;
property_get(LIB_ARGS_PROPERTY, args, \ argc = make_argv(args, rilArgv); }
// Make sure there's a reasonable argv[0] rilArgv[0] = argv[0];
/*利用得到的rilInit函数指针,调用真正的RIL_Init ,实际是动态加载动态库去链接reference-ril.c ,由dlopen()函数加载*/
funcs = rilInit(&s_rilEnv, argc, rilArgv);
/*RIL_register作用一:把vendor RIL(即RIL_init) 注册到reference-ril库去等待,dopen()函数加载链接
附:RIL_init通过是onRequest()方法,将上层来的请求进行映射后转换成对应的AT命令发给硬件,rild通过RIL_register注册这一指针。
RIL_register作用二:创建rild socket主要是等待java层得数据通过,传到下一层,还创建debug socket*/ RIL_register(funcs); done:
while(1) {
// sleep(UINT32_MAX) seems to return immediately on bionic sleep(0x00ffffff); }
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 所有文件:
hardware/ril/reference-ril$ ls
Android.mk atchannel.h at_tok.h misc.h NOTICE atchannel.c at_tok.c ril_event.h reference-ril.c misc.c MODULE_LICENSE_APACHE2
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
在编译时libril被链入rild,它为rild提供了event处理功能,还提供了在rild与Vendor RIL之间传递请求和响应消息的能力。
libril.so驻留在rild这一守护进程中,主要完成同上层通信的工作,接受ril请求并传递给librefrence_ril.so, 同时把来自librefrence_ril.so的反馈回传给调用进程。
librefrence.so负责直接与modem通信,这包括将来自libril的指令转换为AT指令,并且将AT指令写入modem中。
reference-ril会接收调用者传来的参数,参数内容为与radio的通信方式。如通过串口连接modem,那么参数为这种形式:-d /dev/ttySx
funcs =rilInit(&s_rilEnv, argc, rilArgv);//实际是通过动态加载动态库的方式执行
reference-ril.c中的RIL_Init
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
reference-ril/reference-ril.c ->RIL_RadioFunctions *RIL_Init()函数
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
const RIL_RadioFunctions *RIL_Init(const struct RIL_Env *env, int argc, char **argv)
{ int ret; int fd = -1; int opt;
pthread_attr_t attr; s_rilenv = env;
LOGD(\ while ( -1 != (opt = getopt(argc, argv, \ switch (opt) { case 'p':
s_port = atoi(optarg); if (s_port == 0) { usage(argv[0]); return NULL; }
LOGI(\
break;
//获取dev/ttyUSB,只是打开而已,是否获取,由线程mainLoop执行完才知道 case 'd':
s_device_path = optarg;
LOGI(\
break; case 's':
s_device_path = optarg; s_device_socket = 1;
LOGI(\ break; default:
usage(argv[0]); return NULL; } }
if (s_port < 0 && s_device_path == NULL) { usage(argv[0]); return NULL; }
//上面获取了设备节点和socket pthread_attr_init (&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
LOGD(\ret = pthread_create(&s_tid_mainloop, &attr, mainLoop, NULL);-\ //创建一个线程,单独去读取串口数据
ret = pthread_create(&s_tid_mainloop, &attr,mainLoop, NULL);//创建一个线程mainLoop
return &s_callbacks;//返回一个ril_init,给 RIL_register(&s_callbacks);回调,进行初始化 }
#########################################################################################################################
这个任务的入口是RIL_Init, RIL_Init首先通过参数获取硬件接口的设备文件或模拟硬件接口的socket. 接下来便新开一个线程继续初始化, 即mainLoop。mainLoop的主要任务是建立起与硬件的通信,然后通过read方法阻塞等待硬件的主动上报或响应。
在注册一些基础回调(timeout,readerclose)后,mainLoop首先打开硬件设备文件,建立起与硬件的通信,s_device_path和s_port是前面获取的设备路径参数,将其打开(两者可以同时打开并拥有各自的reader,这里
也很容易添加双卡双待等支持)。接下来通过at_open函数建立起这一设备文件上的reader等待循环,这也是通
过新建一个线程完成, ret = pthread_create(&s_tid_reader, &attr, readerLoop, &attr),入口点readerLoop。 AT命令都是以/r/n或/n/r的换行符来作为分隔符的,所以readerLoop是line驱动的,除非出错,超时等,否则会读到一行完整的响应或主动上报,才会返回。这个循环跑起来以后,我们基本的AT响应机制已经建立了起来。它的具体分析,包括at_open中挂接的ATUnsolHandler, 我们都放到后面分析response的连载文章里去。
有了响应的机制(当然,能与硬件通信也已经可以发请求了),通过RIL_requestTimedCallback(initializeCallback, NULL, &TIMEVAL_0),跑到initializeCallback中,执行一些Modem的初始化命令,主要都是AT命令的方式。发AT命令的流程,我们放到后面分析request的连载文章里。这里可以看到,主要是一些参数配置,以及网络状态的检查等
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
mainloop的作用:主要任务是建立起与硬件的通信,然后通过at_open()->readloop()->line()方法阻塞等待是否又数据过来,并向硬件主动上报或响应(OK)。
发命令的是由initializeCallback()发过来的ATE0Q0V1等
1.打开rild socket(init.rc传过来的)是用于等待read()后java层传来的数据(再去调nitializeCallback发at命令) 2.就是调用RIL_requestTimedCallback(initializeCallback, NULL, &TIMEVAL_0);函数,用来向modem发生AT命令 --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- static void *
mainLoop(void *param)
{
LOGD(\LOGI(\ int fd; int ret;
static struct timeval TIMEVAL_DELAYINIT = {0,0};//延时函数 AT_DUMP(\ at_set_on_reader_closed(onATReaderClosed); at_set_on_timeout(onATTimeout); for (;;) { fd = -1; while (fd < 0) { if (s_port > 0) {
fd = socket_loopback_client(s_port, SOCK_STREAM); } else if (s_device_socket) {
if (!strcmp(s_device_path, \ /* Qemu-specific control socket */ //1:
LOGD(\
fd = socket_local_client( \ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM ); if (fd >= 0 ) { char answer[2];
if ( write(fd, \ read(fd, answer, 2) != 2 || memcmp(answer, \ {
close(fd); fd = -1; } } } else
//1:
LOGD(\ fd =
socket_local_client( s_device_path, ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM );
} else if (s_device_path != NULL) {
fd = open (s_device_path, O_RDWR); if ( fd >= 0 ) {
/* disable echo on serial ports */ struct termios ios; tcgetattr( fd, &ios );
ios.c_lflag = 0; /* disable ECHO, ICANON, etc... */ tcsetattr( fd, TCSANOW, &ios ); LOGI(\ }else{
LOGI(\ } }
if (fd < 0) {
perror (\ sleep(10); /* never returns */ } }
s_closed = 0; //2:
LOGD(\
ret = at_open(fd, onUnsolicited); if (ret < 0) {
LOGE (\ return 0; }
TIMEVAL_DELAYINIT.tv_sec = 15; LOGD(\
LOGD (\ //3:
LOGD(\ &TIMEVAL_0);---\
RIL_requestTimedCallback(initializeCallback, NULL, &TIMEVAL_0);//打印AT> ATE0Q0V1 // Give initializeCallback a chance to dispatched, since // we don't presently have a cancellation mechanism sleep(1); waitForClose();
LOGI(\
} }
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
at_open()的作用:1.可以添加自己想要的modem
2.函数建立起这一设备文件上的reader等待循环
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- /**
* Starts AT handler on stream \ * returns 0 on success, -1 on error */
int at_open(int fd, ATUnsolHandler h)
{ int ret; pthread_t tid; pthread_attr_t attr;
LOGD(\ s_fd = fd;
s_unsolHandler = h; s_readerClosed = 0; s_responsePrefix = NULL; s_smsPDU = NULL; sp_response = NULL; /* Android power control ioctl */ #ifdef HAVE_ANDROID_OS
#ifdef OMAP_CSMI_POWER_CONTROL
ret = ioctl(fd, OMAP_CSMI_TTY_ENABLE_ACK); if(ret == 0) { int ack_count; int read_count; int old_flags; char sync_buf[256];
old_flags = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, old_flags | O_NONBLOCK); do {
ioctl(fd, OMAP_CSMI_TTY_READ_UNACKED, &ack_count); read_count = 0; do {
ret = read(fd, sync_buf, sizeof(sync_buf)); if(ret > 0)
read_count += ret;
} while(ret > 0 || (ret < 0 && errno == EINTR)); ioctl(fd, OMAP_CSMI_TTY_ACK, &ack_count);
} while(ack_count > 0 || read_count > 0); fcntl(fd, F_SETFL, old_flags); s_readCount = 0; s_ackPowerIoctl = 1; } else
s_ackPowerIoctl = 0;
#else // OMAP_CSMI_POWER_CONTROL s_ackPowerIoctl = 0;
#endif // OMAP_CSMI_POWER_CONTROL #endif /*HAVE_ANDROID_OS*/ pthread_attr_init (&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); #ifdef HUAWEI_EM770W //新增的modem,在创建一个线程去实现它的功能 int fd2 = -1; while(fd2 < 0) {
fd2 = open (\ if (fd2 < 0) {
perror (\ sleep(10); } }
if(fd2 > 0) { urc_fd = fd2; struct termios ios; tcgetattr( fd2, &ios ); ios.c_lflag = 0;
tcsetattr( fd2, TCSANOW, &ios ); }
ret = pthread_create(&s_tid_reader_urc, &attr, urc_readerLoop, &attr); if (ret < 0) {
perror (\ return -1; } #else
LOGD(\ ret = pthread_create(&s_tid_reader, &attr,readerLoop, &attr); if (ret < 0) {
perror (\ return -1; } #endif return 0; }
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
readerLoop()作用:阻塞地去读取,modem发过来的AT命令,并回应(OK)和上报上层,后再去阻塞的形式读取等待下一个AT命令
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
static void *readerLoop(void *arg)
{
LOGD(\ for (;;) {
const char * line;
line = readline();
if (line == NULL) { break; }
if((isSMSUnsolicited(line)) { char *line1; const char *line2;
LOGD(\ // The scope of string returned by 'readline()' is valid only // till next call to 'readline()' hence making a copy of line // before calling readline again. line1 = strdup(line); line2 = readline(); if (line2 == NULL) { break; }
if (s_unsolHandler != NULL) { s_unsolHandler (line1, line2); }
free(line1); } else {
processLine(line); }
#ifdef HAVE_ANDROID_OS if (s_ackPowerIoctl > 0) {
/* acknowledge that bytes have been read and processed */ ioctl(s_fd, OMAP_CSMI_TTY_ACK, &s_readCount); s_readCount = 0; }
#endif /*HAVE_ANDROID_OS*/ }
onReaderClosed();
return NULL; }
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ /**
* Reads a line from the AT channel, returns NULL on timeout. * Assumes it has exclusive read access to the FD *
* This line is valid only until the next call to readline *
* This function exists because as of writing, android libc does not * have buffered stdio. */
static const char *readline()
{
ssize_t count; char *p_read = NULL; char *p_eol = NULL; char *ret;
LOGD(\
/* this is a little odd. I use *s_ATBufferCur == 0 to
* mean \ * the buffer continues until a \\0 */
if (*s_ATBufferCur == '\\0') { /* empty buffer */
s_ATBufferCur = s_ATBuffer; *s_ATBufferCur = '\\0'; p_read = s_ATBuffer;
} else { /* *s_ATBufferCur != '\\0' */
/* there's data in the buffer from the last read */ // skip over leading newlines
while (*s_ATBufferCur == '\\r' || *s_ATBufferCur == '\\n') s_ATBufferCur++;
p_eol = findNextEOL(s_ATBufferCur); if (p_eol == NULL) {
/* a partial line. move it up and prepare to read more */ size_t len;
len = strlen(s_ATBufferCur);
memmove(s_ATBuffer, s_ATBufferCur, len + 1); p_read = s_ATBuffer + len; s_ATBufferCur = s_ATBuffer; }
/* Otherwise, (p_eol !- NULL) there is a complete line */ /* that will be returned the while () loop below */ }
while (p_eol == NULL) {
if (0 == MAX_AT_RESPONSE - (p_read - s_ATBuffer)) { LOGE(\ /* ditch buffer and start over again */ s_ATBufferCur = s_ATBuffer; *s_ATBufferCur = '\\0'; p_read = s_ATBuffer; } do {
count = read(s_fd, p_read,
MAX_AT_RESPONSE - (p_read - s_ATBuffer)); } while (count < 0 && errno == EINTR); if (count > 0) {
AT_DUMP( \ s_readCount += count; p_read[count] = '\\0'; // skip over leading newlines
while (*s_ATBufferCur == '\\r' || *s_ATBufferCur == '\\n') s_ATBufferCur++;
p_eol = findNextEOL(s_ATBufferCur); p_read += count; } else if (count <= 0) {
/* read error encountered or EOF reached */ if(count == 0) {
LOGD(\ } else {
LOGD(\ }
return NULL; } }
/* a full line in the buffer. Place a \\0 over the \\r and return */ ret = s_ATBufferCur; *p_eol = '\\0';
s_ATBufferCur = p_eol + 1; /* this will always be <= p_read, */ /* and there will be a \\0 at *p_read */ //在这打印Modem回应的OK信息 LOGD(\
return ret;//返回,读取好的上面信息 }
------------------
<二>---RIL层代码分析
---RIL_startEventLoop()->eventLoop()->ril_event_ loop()
ril/rild/rild.c->main()为函数入口
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1.消息队列select为非阻塞的去轮询事件 2.read的阻塞的去读取上层发下来的命令,并响应
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- int main(int argc, char **argv) {
const char * rilLibPath = NULL; char **rilArgv; void *dlHandle;
const RIL_RadioFunctions *(*rilInit)(const struct RIL_Env *, int, char **); const RIL_RadioFunctions *funcs; char libPath[PROPERTY_VALUE_MAX]; unsigned char hasLibArgs = 0; ........ OpenLib: #endif
switchUser();
/*打开dlopen()函数,就会动态去加载动态库vendor RIL 获取由RIL_register(funcs);注册进来的参数,并解析*/ dlHandle = dlopen(rilLibPath, RTLD_NOW); if (dlHandle == NULL) {
fprintf(stderr, \ exit(-1); }
/*消息队列的入口,添加到select,用阻塞方式去读取那些ril_event_set()的数据##每当看到打印信息,不按顺序打下来说明阻塞##*/
RIL_startEventLoop();
/*通过dlsym函数得到rilInit函数指针的引用*/
rilInit = (const RIL_RadioFunctions *(*)(const struct RIL_Env *, int, char **))dlsym(dlHandle, \ if (rilInit == NULL) {
fprintf(stderr, \ exit(-1); }
if (hasLibArgs) { rilArgv = argv + i - 1;
argc = argc -i + 1; } else {
static char * newArgv[MAX_LIB_ARGS]; static char args[PROPERTY_VALUE_MAX]; rilArgv = newArgv;
property_get(LIB_ARGS_PROPERTY, args, \ argc = make_argv(args, rilArgv); }
// Make sure there's a reasonable argv[0] rilArgv[0] = argv[0];
/*利用得到的rilInit函数指针,调用真正的RIL_Init ,实际是动态加载动态库去链接reference-ril.c ,由dlopen()函数加载*/
funcs = rilInit(&s_rilEnv, argc, rilArgv);
/*RIL_register作用一:把vendor RIL(即RIL_init) 注册到reference-ril库去等待,dopen()函数加载链接
附:RIL_init通过是onRequest()方法,将上层来的请求进行映射后转换成对应的AT命令发给硬件,rild通过RIL_register注册这一指针。
RIL_register作用二:创建rild socket主要是等待java层得数据通过,传到下一层,还创建debug socket*/ RIL_register(funcs); done: while(1) {
// sleep(UINT32_MAX) seems to return immediately on bionic sleep(0x00ffffff); }
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 所有文件:
hardware/ril/libril$ ls
Android.mk NOTICE ril_event.h ril.cpp ril_event.cpp ril_commands.h ril_unsol_commands.h MODULE_LICENSE_APACHE2
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
与rild结合相当紧密,是其共享库,编译时就已经建立了这一关系。组成部分为ril.cpp,ril_event.cpp。libril.so驻留在rild这一守护进程中,主要完成同上层通信的工作,接受ril请求并传递给librefrence_ril.so, 同时把来自librefrence_ril.so的反馈回传给调用进程
在编译时libril被链入rild,它为rild提供了event处理功能,还提供了在rild与Vendor RIL之间传递请求和响应消息的能力。
Libril提供的主要功能分布在两个主要方法内,一个是RIL_startEventLoop()方法,另一个是RIL_register()方法
RIL_startEventLoop()方法所提供的功能就是启用eventLoop线程,开始执行RIL消息队列。
RIL_register()方法的主要功能是启动名为rild 的监听端口,等待java 端通过socket进行连接。
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
RIL_startEventLoop函数 libril/ril.cpp->RIL_startEventLoop()
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- extern \
RIL_startEventLoop(void) {
int ret;
pthread_attr_t attr;
LOGD(\
/* spin up eventLoop thread and wait for it to get started */ s_started = 0;
pthread_mutex_lock(&s_startupMutex); pthread_attr_init (&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
LOGD(\
ret = pthread_create(&s_tid_dispatch, &attr, eventLoop, NULL);//创建线程,为入口函数 LOGD(\ while (s_started == 0) {
pthread_cond_wait(&s_startupCond, &s_startupMutex); }
pthread_mutex_unlock(&s_startupMutex); if (ret < 0) {
LOGE(\ return; } }
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
开启libril.so中的event机制, 在RIL_startEventLoop中,是最核心的由多路I/O驱动的消息循环
RIL_startEventLoop函数。RIL_startEventLoop在ril.cpp中实现, 它的主要目的是通过pthread_create(&s_tid_dispatch, &attr, eventLoop, NULL)建立一个dispatch线程,入口点在eventLoop. 而eventLoop中,会调ril_event.cpp中的ril_event_loop()函数,建立起消息(event)队列机制。
我们来仔细看看这一消息队列的机制,这些代码都在ril_event.cpp中。 void ril_event_init();
void ril_event_set(struct ril_event * ev, int fd, bool persist, ril_event_cb func, void * param); void ril_event_add(struct ril_event * ev);
void ril_timer_add(struct ril_event * ev, struct timeval * tv); void ril_event_del(struct ril_event * ev); void ril_event_loop();
----------------------------------------------- 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结构,与一个fd句柄绑定(可以是文件,socket,管道等),并且带一个func指针去执行指定的操作。
具体流程是: ril_event_init完成后,通过ril_event_set来配置一新ril_event,并通过ril_event_add加入队列之中(实际通常用rilEventAddWakeup来添加),add会把队列里所有ril_event的fd,放入一个fd集合readFds中。这样ril_event_loop能通过一个多路复用I/O的机制(select)来等待这些fd, 如果任何一个fd有数据写入,则进入分析流程processTimeouts(),processReadReadies(&rfds, n),firePending()。 后文会详细分析这些流程。另外我们可以看到, 在进入ril_event_loop之前, 已经挂入了一s_wakeupfd_event, 通过pipe的机制实现的, 这个event的目的是可以在一些情况下,能内部唤醒ril_event_loop的多路复用阻塞,比如一些带timeout的命令timeout到期的时候。 至此第一个任务分析完毕,这样便建立起了基于event队列的消息循环,稍后便可以接受上层发来的的请求了(上层请求的event对象建立,在第三个任务中)。
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- static void *
eventLoop(void *param) { int ret; int filedes[2];
LOGD(\LOGD(\ ril_event_init();//初始化
LOGD(\ pthread_mutex_lock(&s_startupMutex); s_started = 1;
pthread_cond_broadcast(&s_startupCond); pthread_mutex_unlock(&s_startupMutex); ret = pipe(filedes); if (ret < 0) {
LOGE(\ return NULL; }
s_fdWakeupRead = filedes[0];// 消息循环中侦听
s_fdWakeupWrite = filedes[1];// 用于通知消息循环定义消息已发送
LOGD(\ processWakeupCallback, NULL);--\ fcntl(s_fdWakeupRead, F_SETFL, O_NONBLOCK);
/*先执行 processWakeupCallback中的read函数,再把参数,传给ril_event_set进行设置*/
ril_event_set(&s_wakeupfd_event, s_fdWakeupRead, true, processWakeupCallback, NULL); /*在ilEventAddWakeup进行ril_evnet_add事件添加和triggerEvLoop()实现管道写端*/
LOGD(\ rilEventAddWakeup (&s_wakeupfd_event); LOGD(\ // Only returns on error
LOGD(\ ril_event_loop();
LOGD(\ LOGE (\ return NULL; }
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
ril_event_init()主要工作是清零和处理三种函数(定时,监听,挂起);
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Initialize internal data structs
void ril_event_init()
{
MUTEX_INIT();
LOGD(\ FD_ZERO(&readFds); init_list(&timer_list); init_list(&pending_list);
memset(watch_table, 0, sizeof(watch_table)); }
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
ril_event_set()的作用是:通过管道,对读到的数据进行初始化 ---------注册进程唤醒事件回调
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
void ril_event_set(struct ril_event * ev, int fd, bool persist, ril_event_cb func, void * param)
{
LOGD(\* param)-\
dlog(\ memset(ev, 0, sizeof(struct ril_event)); ev->fd = fd; ev->index = -1; ev->persist = persist; ev->func = func; ev->param = param;
fcntl(fd, F_SETFL, O_NONBLOCK); }
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
用read()得方法去唤醒fd,把可执行得弹出来,ril_event 去复位定时器,把缓冲区清空
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- /**
* A write on the wakeup fd is done just to pop us out of select()
* We empty the buffer here and then ril_event will reset the timers on the * way back down */
static void processWakeupCallback(int fd, short flags, void *param) {
char buff[16]; int ret;
LOGD(\ LOGV(\ /* empty our wakeup socket out */ do {
ret = read(s_fdWakeupRead, &buff, sizeof(buff)); } while (ret > 0 || (ret < 0 && errno == EINTR)); }
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
rilEventAddWakeup的作用是:作为ril_event_add(ev);和triggerEvLoop();入口函数,真正的把数据数组 watch_table[i] = ev上去,等待selcet轮询,最后写入
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
static void rilEventAddWakeup(struct ril_event *ev) {
ril_event_add(ev); triggerEvLoop(); }
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Add event to watch list
void ril_event_add(struct ril_event * ev)
{
LOGD(\ dlog(\ MUTEX_ACQUIRE();
for (int i = 0; i < MAX_FD_EVLL) { //1:
watch_table[i] = ev;//把上面ril_event_add函数添加的事件_wakeupfd_event结构体添加到这个数组来 ev->index = i;
dlog(\ dump_event(ev);
//2:
FD_SET(ev->fd, &readFds);//把上面数组,放到readFds来 if (ev->fd >= nfds) nfds = ev->fd+1; dlog(\ break;ENTS; i++) { if (watch_table[i] == NU } }
MUTEX_RELEASE();
dlog(\}
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
就是上面管道读完,收到数据后,再通过写端,写到对应的数组去(readFs)
static void triggerEvLoop() {
int ret;
LOGD(\ if (!pthread_equal(pthread_self(), s_tid_dispatch)) { /* trigger event loop to wakeup. No reason to do this, * if we're in the event loop thread */ do {
ret = write (s_fdWakeupWrite, \ } while (ret < 0 && errno == EINTR); } }
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
上面工作一做完,说明通过pipe机制已经把数据读到,并放入数组,而数组且放在readFs队列中,等待ril_event_loop()函数t轮询
下面为ril_event_loop()开始轮询的整个过程:
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
void ril_event_loop()
{ int n; fd_set rfds; struct timeval tv; struct timeval * ptv;
LOGD(\ for (;;) {//死循环的轮询
// make local copy of read fd_set
LOGD(\
memcpy(&rfds, &readFds, sizeof(fd_set));把上面通过管道机制读到得数据,换个名字,放到rfds
去
if (-1 == calcNextTimeout(&tv)) {// 获取超时参数,若失败则返回-1。 // no pending timers; block indefinitely
dlog(\ ptv = NULL;
} else {// 获取成功,则使用该值。
dlog(\ ptv = &tv; }
printReadies(&rfds);
LOGD(\ LOGI(\ //初始化到这里结束。
n = select(nfds, &rfds, NULL, NULL, ptv);//等待
fd唤醒,通过selcet多路I/o复用
机制去唤醒管道来的数据
LOGD(\ LOGI(\ printReadies(&rfds);
dlog(\ if (n < 0) {
if (errno == EINTR) continue;
LOGE(\ // bail? return; }
LOGD(\processTimeouts();--\ // Check for timeouts
processTimeouts();// 处理定时消息 ,处理ril_event_init ->pending_list // Check for read-ready
LOGD(\
processReadReadies(&rfds, n); // 处理侦听消息 ,处理ril_event_init ->watch_list // Fire away
LOGD(\ firePending(); // 处理挂起消息
LOGD(\ } }
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
static void processTimeouts()
{
LOGD(\LOGI(\
dlog(\ MUTEX_ACQUIRE();
struct timeval now;
struct ril_event * tev = timer_list.next; struct ril_event * next; getNow(&now);//获取当前时间
// walk list, see if now >= ev->timeout for any events
dlog(\
while ((tev != &timer_list) && (timercmp(&now, &tev->timeout, >))) {// 查找所有当前时间点以前的消息 // Timer expired
dlog(\ next = tev->next;
removeFromList(tev);//从定时消息队列中移除,就是已经做完的内容 addToList(tev, &pending_list); // 添加至挂起列表(pending_list)中 tev = next; }
MUTEX_RELEASE();
dlog(\}
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
static void addToList(struct ril_event * ev, struct ril_event * list) {
LOGD(\ ev->next = list; ev->prev = list->prev; ev->prev->next = ev; list->prev = ev; dump_event(ev); }
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
static void processReadReadies(fd_set * rfds, int n) {
LOGD(\LOGI(\
dlog(\ MUTEX_ACQUIRE();
for (int i = 0; (i < MAX_FD_EVENTS) && (n > 0); i++) { // 此处判断描述符是否在当前循环的等待列表中。 struct ril_event * rev = watch_table[i]; if (rev != NULL && FD_ISSET(rev->fd, rfds)) {
addToList(rev, &pending_list);// 添加至挂起列表(pending_list)中
if (rev->persist == false) { // 会判断ril_event的persist参数,以决定是否从watch_list移除该消息。 removeWatch(rev, i); } n--;
} }
MUTEX_RELEASE();
dlog(\}
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
static void firePending() {
LOGD(\LOGI(\
dlog(\ struct ril_event * ev = pending_list.next; while (ev != &pending_list) { struct ril_event * next = ev->next; removeFromList(ev);
ev->func(ev->fd, 0, ev->param); ev = next; }
dlog(\}
<三>--RIL层代码分析---RIL_register()
ril/rild/rild.c->main()为函数入口
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1.消息队列select为非阻塞的去轮询事件 2.read的阻塞的去读取上层发下来的命令,并响应
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
int main(int argc, char **argv) {
const char * rilLibPath = NULL; char **rilArgv; void *dlHandle;
const RIL_RadioFunctions *(*rilInit)(const struct RIL_Env *, int, char **); const RIL_RadioFunctions *funcs;
char libPath[PROPERTY_VALUE_MAX]; unsigned char hasLibArgs = 0; ........ OpenLib: #endif
switchUser();
/*打开dlopen()函数,就会动态去加载动态库vendor RIL 获取由RIL_register(funcs);注册进来的参数,并解析*/ dlHandle = dlopen(rilLibPath, RTLD_NOW); if (dlHandle == NULL) {
fprintf(stderr, \ exit(-1); }
/*消息队列的入口,添加到select,用阻塞方式去读取那些ril_event_set()的数据##每当看到打印信息,不按顺序打下来说明阻塞##*/
RIL_startEventLoop();
/*通过dlsym函数得到rilInit函数指针的引用*/
rilInit = (const RIL_RadioFunctions *(*)(const struct RIL_Env *, int, char **))dlsym(dlHandle, \ if (rilInit == NULL) {
fprintf(stderr, \ exit(-1); }
if (hasLibArgs) { rilArgv = argv + i - 1; argc = argc -i + 1; } else {
static char * newArgv[MAX_LIB_ARGS]; static char args[PROPERTY_VALUE_MAX]; rilArgv = newArgv;
property_get(LIB_ARGS_PROPERTY, args, \ argc = make_argv(args, rilArgv); }
// Make sure there's a reasonable argv[0] rilArgv[0] = argv[0];
/*利用得到的rilInit函数指针,调用真正的RIL_Init ,实际是动态加载动态库去链接reference-ril.c ,由dlopen()函数加载*/
funcs = rilInit(&s_rilEnv, argc, rilArgv);
/*RIL_register作用一:把vendor RIL(即RIL_init) 注册到reference-ril库去等待,dopen()函数加载链接
附:RIL_init通过是onRequest()方法,将上层来的请求进行映射后转换成对应的AT命令发给硬件,rild通过RIL_register注册这一指针。
RIL_register作用二:创建rild socket主要是等待java层得数据通过,传到下一层,还创建debug socket*/ RIL_register(funcs); done: while(1) {
// sleep(UINT32_MAX) seems to return immediately on bionic
sleep(0x00ffffff); }
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 所有文件:
hardware/ril/libril$ ls
Android.mk NOTICE ril_event.h ril.cpp ril_event.cpp ril_commands.h ril_unsol_commands.h --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
funcs =rilInit(&s_rilEnv, argc, rilArgv);//实际是通过动态加载动态库的方式执行reference-ril.c中的RIL_Init 由RIL_Init的返回值开始的,这是一个RIL_RadioFunctions结构的指针。 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;
其中最重要的是onRequest域,上层来的请求都由这个函数进行映射后转换成对应的AT命令发给硬件。rild通过RIL_register注册这一指针。
RIL_register中要完成的另外一个任务,就是打开前面提到的跟上层通信的socket接口(s_fdListen是主接口,s_fdDebug供调试时使用)。
然后将这两个socket接口使用任务一中实现的机制进行注册(仅列出s_fdListen) ril_event_set (&s_listen_event, s_fdListen, false, listenCallback, NULL); rilEventAddWakeup (&s_listen_event);
这样将两个socket加到任务一中建立起来多路复用I/O的检查句柄集合中,一旦有上层来的(调试)请求,event机制便能响应处理了。 总结:0.监听所有socket
1.RIL_register()把ril_init()初始化后的vendor RIL注册到libril.so等待dlopen()h函数加载,调用,再后到select机制中的ril_event_loop()函数轮询
2.获取连个socket(rild socket和debug socket),而rild socket 是用来和java层通信得,debug socket是用来调试的
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
ril/libril/ril.cpp->RIL_register 函数
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
/*注册rild socket端口事件监听到事件循环中和bedug socket */
extern \
RIL_register (const RIL_RadioFunctions *callbacks) {//说明已经初始化完了 ,才回调
int ret; int flags;
LOGD(\ if (callbacks == NULL
|| ! (callbacks->version == RIL_VERSION || callbacks->version == 1) ) { LOGE(
\ \ return; }
if (s_registerCalled > 0) {
LOGE(\ \
LOGD(\ \ return; } //1:换个马甲
memcpy(&s_callbacks, callbacks, sizeof (RIL_RadioFunctions));
s_registerCalled = 1; // Little self-check
for (int i = 0; i < (int)NUM_ELEMS(s_commands); i++) { assert(i == s_commands[i].requestNumber); }
for (int i = 0; i < (int)NUM_ELEMS(s_unsolResponses); i++) { assert(i + RIL_UNSOL_RESPONSE_BASE == s_unsolResponses[i].requestNumber); }
// New rild impl calls RIL_startEventLoop() first // old standalone impl wants it here. if (s_started == 0) { RIL_startEventLoop(); }
// start listen socket 开始侦听套接字 #if 0
ret = socket_local_server
(SOCKET_NAME_RIL,ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
if (ret < 0) {
LOGE(\ exit (-1); }
s_fdListen = ret; #else
//2:获取init.rc定义的rild socket,,#define SOCKET_NAME_RIL \ s_fdListen = android_get_control_socket(SOCKET_NAME_RIL); if (s_fdListen < 0) {
LOGE(\ exit(-1); }
ret = listen(s_fdListen, 4); if (ret < 0) {
LOGE(\ s_fdListen, strerror(errno)); exit(-1); } #endif
/*如果ril socket 端口一有数据,就去执行listencallback函数 这个函数就建立起与客户端的监听话柄 s_fdLiten继续监听其他客户端的链接*/
/* note: non-persistent so we can accept only one connection at a time */
ril_event_set(&s_listen_event, s_fdListen, false, listenCallback, NULL); 将此处端口加入到
select队列去
rilEventAddWakeup (&s_listen_event); #if 1
// start debug interface socket 获取debug socket
s_fdDebug = android_get_control_socket(SOCKET_NAME_RIL_DEBUG); if (s_fdDebug < 0) {
LOGE(\ exit(-1); }
ret = listen(s_fdDebug, 4); if (ret < 0) {
LOGE(\ s_fdDebug, strerror(errno)); exit(-1); }
ril_event_set (&s_debug_event, s_fdDebug, true,debugCallback, NULL);
rilEventAddWakeup (&s_debug_event); #endif }
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Initialize an event
void ril_event_set(struct ril_event * ev, int fd, bool persist, ril_event_cb func, void * param)
{
LOGD(\* param)-\
dlog(\ memset(ev, 0, sizeof(struct ril_event)); ev->fd = fd; ev->index = -1; ev->persist = persist; ev->func = func; ev->param = param;
fcntl(fd, F_SETFL, O_NONBLOCK); }
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
从传入来的参数,可知道,是注册的ril_init返回的指针RIL_RadioFunctions 参数 说明,到这步的时候,vendor RIL 初始化已经完成(就是ril_init)
若rild socket数据来,就回去执行listencallback函数,这个listencallback函数主要是执行java层通过riled socket来的数据
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
static void listenCallback (int fd, short flags, void *param) {
int ret; int err;
int is_phone_socket; RecordStream *p_rs; struct sockaddr_un peeraddr; socklen_t socklen = sizeof (peeraddr); struct ucred creds;
socklen_t szCreds = sizeof(creds); struct passwd *pwd = NULL; assert (s_fdCommand < 0); assert (fd == s_fdListen);
LOGD(\--shi-xian-ril_regiter()----- ril_event_set (&s_debug_event, s_fdDebug, true,debugCallback, NULL);\//连接JAVA层来的sockcet
s_fdCommand = accept(s_fdListen, (sockaddr *) &peeraddr, &socklen); if (s_fdCommand < 0 ) {
LOGE(\ /* start listening for new connections again */ rilEventAddWakeup(&s_listen_event); return; }
/* check the credential of the other side and only accept socket from * phone process */ errno = 0;
is_phone_socket = 0;
err = getsockopt(s_fdCommand, SOL_SOCKET, SO_PEERCRED, &creds, &szCreds);
if (err == 0 && szCreds > 0) { errno = 0;
pwd = getpwuid(creds.uid); if (pwd != NULL) {
if (strcmp(pwd->pw_name, PHONE_PROCESS) == 0) { is_phone_socket = 1; } else {
LOGE(\ } } else {
LOGE(\ } } else {
LOGD(\ }
if ( !is_phone_socket ) {
LOGE(\ close(s_fdCommand); s_fdCommand = -1;
onCommandsSocketClosed();
/* start listening for new connections again */
rilEventAddWakeup(&s_listen_event);
return; }
ret = fcntl(s_fdCommand, F_SETFL, O_NONBLOCK); if (ret < 0) {
LOGE (\ }
LOGI(\
//有数据来的时候,把s_fdCommand绑定到record_stream_new 。目的就是保证数据的完整性
p_rs = record_stream_new(s_fdCommand, MAX_COMMAND_BYTES);
//当有数据来的时候,函数就去执行processCommandsCallback澹()函数,把数据读到p_record中
ril_event_set(&s_commands_event, s_fdCommand, 1, processCommandsCallback, p_rs);
rilEventAddWakeup(&s_commands_event); onNewCommandConnect();
}
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
static void processCommandsCallback(int fd, short flags, void *param) {
RecordStream *p_rs; void *p_record;
size_t recordlen; int ret;
assert(fd == s_fdCommand); p_rs = (RecordStream *)param;
LOGD(\ for (;;) {
/* loop until EAGAIN/EINTR, end of stream, or other error */
ret = record_stream_get_next(p_rs, &p_record, &recordlen); if (ret == 0 && p_record == NULL) { /* end-of-stream */ break; } else if (ret < 0) { break;
} else if (ret == 0) { /* && p_record != NULL */
//阻塞方式获取数据 到p_record 利用RecordStream机制保证数据完整 processCommandBuffer(p_record, recordlen); } }
if (ret == 0 || !(errno == EAGAIN || errno == EINTR)) { /* fatal error or end-of-stream */ if (ret != 0) {
LOGE(\ } else {
LOGW(\Closing command socket.\ }
close(s_fdCommand); s_fdCommand = -1;
ril_event_del(&s_commands_event);//删除
record_stream_free(p_rs);//释放了上面监听到的数据,再开始新的路程
/* start listening for new connections again 开始监听新的连接*/
rilEventAddWakeup(&s_listen_event);
说明:它只是把事件添加到里面而已,添加完后,再回去ril_event_loop()去监听新的事件,反复的轮询
onCommandsSocketClosed();
} }
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
上面数据来了,就要都得去找请求队列的序列号,就的做执行processCommandBuffer->status = p.readInt32 (&token); 假设是接收了dial指令,pRI->PCI->dispatchFunction(p,pRI),调用dispatchDial(p,pRI)
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- static int
processCommandBuffer(void *buffer, size_t buflen) {
Parcel p;
status_t status; int32_t request; int32_t token; RequestInfo *pRI; int ret;
LOGD(\ p.setData((uint8_t *) buffer, buflen); // status checked at end status = p.readInt32(&request);
status = p.readInt32 (&token);//请求队列的序列号 if (status != NO_ERROR) { LOGE(\ return 0; }
if (request < 1 || request >= (int32_t)NUM_ELEMS(s_commands)) { LOGE(\ // FIXME this should perhaps return a response return 0; }
pRI = (RequestInfo *)calloc(1, sizeof(RequestInfo)); pRI->token = token;
pRI->pCI = &(s_commands[request]);
ret = pthread_mutex_lock(&s_pendingRequestsMutex); assert (ret == 0);
pRI->p_next = s_pendingRequests; s_pendingRequests = pRI;
ret = pthread_mutex_unlock(&s_pendingRequestsMutex); assert (ret == 0);
/* sLastDispatchedToken = token; */
pRI->pCI->dispatchFunction(p, pRI);//电话来了,就执行这个函数 return 0; }
<四>--RIL层代码分析--整个电话来访过程
最近公司开发一个几百万的项目,要求重写系统RIL层,看了几个招聘信息,只要你会
RIL层开发的,工资上w每个月不是梦,这是几天研究的成果,希望对大家有所帮助,兄弟们加油吧!
先来一个总的流程图:
拨出电话流程:
1 在系统源码这个路径下/packages/apps/Phone/src/com/android/phone/DialtactsActivity.java
contacts的androidmanifest.xmlandroid:process=\说明此应用程序运行在acore进程中。DialtactsActivity的intent-filter的action属性设置为main,catelog属性设置为launcher,所以此activity能出现, 首先启动的就是这个activity在主菜单中,并且是点击此应用程序的第一个界面。dialtactsactivity包含四个tab,分别由TwelveKeyDialer、RecentCallsListActivity,两个activity-aliasDialtactsContactsEntryActivity和
DialtactsFavoritesEntryActivity分别表示联系人和收藏tab,但是正真的联系人列表和收藏是由ContactsListActivity负责。 2
进入TwelveKeyDialer OnClick方法,按住的按钮id为:R.id.digits,执行 placecall()
Intent intent = newIntent(Intent.ACTION_CALL_PRIVILEGED, Uri.fromParts(\
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); 3、
intert.ACTION_CALL_PRIVILEGED实际字符串为android.intent.action.CALL_PRIVILEGED,通过查找知道了packegs/phone下面的androidmanifest.xml中PrivilegedOutgoingCallBroadcasteractivity-alias设置了intent-filter,所以需要找到其targetactivity为OutgoingCallBroadcaster。所以进入OutgoingCallBroadcaster的
onCreate()
//如果为紧急号码马上启动intent.setClass(this,InCallScreen.class); startActivity(intent);
Intent broadcastIntent = new Intent(Intent.ACTION_NEW_OUTGOING_CALL);
if (number != null) broadcastIntent.putExtra(Intent.EXTRA_PHONE_NUMBER,number);
broadcastIntent.putExtra(EXTRA_ALREADY_CALLED, callNow);
broadcastIntent.putExtra(EXTRA_ORIGINAL_URI,intent.getData().toString());
if (LOGV) Log.v(TAG, \
sendOrderedBroadcast(broadcastIntent, PERMISSION, null, null,
Activity.RESULT_OK, number, null);
4、Intent.ACTION_NEW_OUTGOING_CALL实际字符串为android.intent.action.NEW_OUTGOING_CALL,通过查找知道了packegs/phone
下面的androidmanifest.xml中OutgoingCallReceiver Receiver接收此intent消息。找到OutgoingCallReceiver,执行
onReceive()函数
Intent newIntent = new Intent(Intent.ACTION_CALL, uri);
newIntent.putExtra(Intent.EXTRA_PHONE_NUMBER, number);
newIntent.setClass(context, InCallScreen.class);
newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 5、请求拨号的java部分流程
onCreate(第一次)/onNewIntent(非第一次)
internalResolveIntent
placeCall(intent);
PhoneUtils.placeCall(mPhone, number, intent.getData());
phone.dial(number);
mCT.dial(newDialString);
dial(dialString, CommandsInterface.CLIR_DEFAULT);
cm.dial(pendingMO.address,clirMode,obtainCompleteMessage());//obtainCompleteMessage(EVENT_OPERATION_COMPLETE);
send(rr);
msg =mSender.obtainMessage(EVENT_SEND, rr);
acquireWakeLock();
msg.sendToTarget();
RILSender.handleMessage()
case EVENT_SEND:
...
s.getOutputStream().write(dataLength);
s.getOutputStream().write(data);//从这里流程跑到下面ril.cpp中监听部份
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
上面步骤:为APP就是java层传下的信息(就是执行的动作:按键打电话),已经做完,下面讲RIL层如何来接受上层来的信息
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
第一、《创建轮询机制》
6.请求拨号的c/c++部分流程
/*初始化事件循环,启动串口监听,注册socket监听*/
6.1在til/rild/rild.c为入口函数 RIL_startEventLoop() 6.2在libril/ril.cpp 第一、建立事件循环线程
ret = pthread_create(&s_tid_dispatch, &attr, eventLoop, NULL);
RIL_startEventLoop(void){
..........
ret = pthread_create(&s_tid_dispatch, &attr, eventLoop, NULL);//创建线程,为入口函数,去实现它的功能
}
-------------------------------------------------------------------- eventLoop入口函数->跳到/ril/libril/ril.cpp 实现 ----------------------------------------------------------------------
eventLoop(void *param)
{
ril_event_init();/*初始化*/
void ril_event_init() {
MUTEX_INIT();
LOGD(\ FD_ZERO(&readFds); init_list(&timer_list);
init_list(&pending_list);
memset(watch_table, 0, sizeof(watch_table)); }
ret = pipe(filedes);
s_fdWakeupRead = filedes[0];/* 消息循环中侦听*/
s_fdWakeupWrite = filedes[1];/* 用于通知消息循环定义消息已发送*/
第二、注册进程唤醒事件回调
ril_event_set(&s_wakeupfd_event, s_fdWakeupRead, true, processWakeupCallback, NULL);/*注册
进程唤醒事件回调*/
static void processWakeupCallback(int fd, short flags, void *param) { char buff[16]; int ret;
LOGD(\ LOGV(\ /* empty our wakeup socket out */ do {
ret = read(s_fdWakeupRead, &buff,
sizeof(buff));
} while (ret > 0 || (ret < 0 && errno == EINTR)); }
rilEventAddWakeup (&s_wakeupfd_event);
->static void rilEventAddWakeup(struct ril_event *ev) {
ril_event_add(ev); triggerEvLoop(); } // Add event to watch list
void ril_event_add(struct ril_event * ev)
{
LOGD(\ dlog(\ MUTEX_ACQUIRE();
for (int i = 0; i < MAX_FD_EVLL) { //1:
watch_table[i] = ev;//把上面ril_event_add函数添加的事件_wakeupfd_event结
构体添加到这个数组来 ev->index = i;
dlog(\ dump_event(ev);
正在阅读:
Android中RIL层详细分析04-25
【责任书】【1】财务部安全目标责任书08-17
金蝶K3供应链常见问题集504-29
传染病护理学B卷06-06
新生儿的护理08-13
关于父母的爱诗歌03-21
机械制图试卷07-06
有趣的多肉作文500字06-15
- 多层物业服务方案
- (审判实务)习惯法与少数民族地区民间纠纷解决问题(孙 潋)
- 人教版新课标六年级下册语文全册教案
- 词语打卡
- photoshop实习报告
- 钢结构设计原理综合测试2
- 2014年期末练习题
- 高中数学中的逆向思维解题方法探讨
- 名师原创 全国通用2014-2015学年高二寒假作业 政治(一)Word版
- 北航《建筑结构检测鉴定与加固》在线作业三
- XX县卫生监督所工程建设项目可行性研究报告
- 小学四年级观察作文经典评语
- 浅谈110KV变电站电气一次设计-程泉焱(1)
- 安全员考试题库
- 国家电网公司变电运维管理规定(试行)
- 义务教育课程标准稿征求意见提纲
- 教学秘书面试技巧
- 钢结构工程施工组织设计
- 水利工程概论论文
- 09届九年级数学第四次模拟试卷
- Android
- 分析
- 详细
- RIL