Android中RIL层详细分析

更新时间:2024-04-25 06:13:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

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--.so

比如:

libril-techfaith-124.so 其中:

libril:所有vendor RIL的开头

:公司缩写 :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接口,实现回调(回调方式参见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);

本文来源:https://www.bwwdw.com/article/j7gp.html

Top