版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
Android的功能介绍——整个RIL文件夹的分析介绍本文档对AndroidRIL局部的容进展了介绍,其重点放在了AndroidRIL的原生代码局部。包括四个主题:1.AndroidRIL框架介绍2.AndroidRIL与WindowsMobileRIL3.AndroidRILporting4.AndroidRIL的java框架在本文档中将Android代码中的重要模块列出进展分析,并给出了相关的程序执行流程介绍,以加深对模块间交互方式的理解。对于java代码局部,这里仅进展简单的介绍。如果需要深入了解,可以查看相关参考资料。本文档中还对AndroidRIL的Porting局部容进展了描述和分析。针对对uni*操作系统环境并不熟悉的读者,本文档中所涉及到的相关知识包括:
Uni*filesystem
Uni*socket
Uni*thread
Uni*
下I/O多路转接以上信息可以在任意一份描述Uni*系统调用的文档中找到。1.AndroidRIL框架介绍术语:fd
Linu*文件描述符pipe
Linu*管道cond
一般是conditionvariable的缩写tty
通常使用tty来简称各种类型的终端设备unsolicitedresponse
被动请求命令来自basebandeventloop
android的消息队列机制,由Linu*的系统调用select()实现init.rc
init守护进程启动后被执行的启动脚本。HAL
硬件抽象层〔HardwareAbstractionLayer,HAL〕1.1AndroidRIL概况AndroidRIL提供了无线硬件设备与效劳之间的抽象层。下列图展示了RIL在Android体系中的位置。android的ril位于应用程序框架与核之间,分成了两个局部,一个局部是rild,它负责socket与应用程序框架进展通信。另外一个局部是VendorRIL,这个局部负责向下是通过两种方式与radio进展通信,它们是直接与radio通信的AT指令通道和用于传输包数据的通道,数据通道用于手机的上网功能。对于RIL的java框架局部,也被分成了两个局部,一个是RIL模块,这个模块主要用于与下层的rild进展通信,另外一个是Phone模块,这个模块直接暴露功能接口给应用开发用户,供他们调用以进展功能的实现。1.2AndroidRIL目录构造Android的RIL模块位于Android/hardware/ril文件夹,有三个子模块:rild,libril,reference-ril所在目录构造:/hardware/ril/
|--ril
〔无线电抽象层〕
|
|--include
〔头文件〕
|
|--libril
〔库〕
|
|--reference-cdma-sms〔cdma短信参考〕
|
|--reference-ril
〔ril参考〕
|
|--rild
〔ril后台效劳程序〕hardware/ril$lsincludelibrilreference-cdma-sms
reference-ril
rild
1.hardware/ril/rild$lsAndroid.mk
MODULE_LICENSE_APACHE2
NOTICE
radiooptions.c
rild.c2.hardware/ril/include/telephony$lsril_cdma_sms.h
ril.h3.hardware/ril/libril$lsAndroid.mk
NOTICE
ril_event.h
ril_mands.h
ril_unsol_mands.hMODULE_LICENSE_APACHE2
4.hardware/ril/reference-cdma-sms$lsAndroid.mk
reference-cdma-sms.c
reference-cdma-sms.h5.hardware/ril/reference-ril$lsAndroid.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的加载过程,它会执行如下操作:动态加载VendorRIL的.so文件执行RIL_startEventLoop()开启消息队列以进展事件监听通过执行VendorRIL的rilInit()方法来进展VendorRIL与libril的关系建立。在rild文件夹中还包括一个radiooptions.c文件,它的作用是通过串口将一些radio相关的参数直接传给rild来对radio进展配置。●libril文件夹:在编译时libril被链入rild,它为rild提供了event处理功能,还提供了在rild与VendorRIL之间传递请求和响应消息的能力。Libril提供的主要功能分布在两个主要方法,一个是RIL_startEventLoop()方法,另一个是RIL_register()方法RIL_startEventLoop()方法所提供的功能就是启用eventLoop线程,开场执行RIL消息队列。RIL_register()方法的主要功能是启动名为rild的监听端口,等待java端通过socket进展连接。●reference-ril文件夹:Android自带的VendorRIL的参考实现。被编译成.so文件,由于本局部是厂商定制的重点所在。所以被设计为松散耦合,且可灵活配置的。在rild过opendl()的方式加载。librefrence.so负责直接与radio通信,这包括将来自libril的指令转换为AT指令,并且将AT指令写入radio中。reference-ril会接收调用者传来的参数,参数容为与radio的通信方式。如通过串口连接radio,则参数为这种形式:-d/dev/ttyS*1.3.AndroidRIL中的消息〔event〕队列机制在AndroidRIL中,为了到达等待多路输入并且不出现阻塞的目的,使用了IO多路复用机制。如果使用阻塞I/O进展网络的读取写入,这意味着假设需要同时从两个网络文件描述符中读容,则如果读取操作在等待网络数据到来,这将可能很长时间阻塞在一个描述符上,另一个网络文件描述符不管有没有数据到来都无法被读取。一种解决方案是:如果使用非阻塞I/O进展网络的读取写入,在读取其中一个网络文件描述符如果阻塞将直接返回,再读取另外一个,这种方式的循环被称之为轮询。轮询方式确实能解决进展多路io操作时的阻塞问题,但是这种方法的缺乏之处是反复的执行读写调用将浪费cpu时钟。I/O多路转接技术在这里提供了另一种比拟好的解决方案:它会先构造一有关I/O描述符的列表,然后调用select函数,当IO描述符列表中的一个描述符准备好进展I/O时,该函数返回,并告知可以读或写哪个描述符。AndroidRIL中消息队列的核心实现思想就是这种I/O多路转接技术。消息队列机制的实现在ril_event.cpp中,其中被定义的ril_event构造是消息的主体。每个ril_event构造,与一个fd句柄绑定(可以是文件,socket,管道等),并且带一个func指针,这个func指针所指的函数是个回调函数,它指定了当所绑定的fd准备好进展读取时所要进展的操作。消息队列的开场点为RIL_startEventLoop函数。RIL_startEventLoop在ril.cpp中实现,它的主要目的是通过pthread_create(&s_tid_dispatch,&attr,eventLoop,NULL)建立一个dispatch线程,线程入口点在eventLoop.而在eventLoop中,会调ril_event.cpp中的ril_event_loop()函数,建立起消息队列机制。ril_event是一个带有链表行为的struct,它最主要的成员一个是fd,一个是func:structril_event{structril_event*ne*t;structril_event*prev;intfd;intinde*;boolpersist;structtimevaltimeout;ril_event_cbfunc;void*param;};初始化一个新ril_event的操作是通过ril_event_set〔〕来完成的,并通过ril_event_add〔〕参加到消息队列之中,add会把队列里所有ril_event的fd,放入一个fd集合readFds中。这样ril_event_loop能通过一个多路复用I/O的机制(select)来等待这些fd。在进入ril_event_loop〔〕之前,在eventLoop中已经创立和挂入了s_wakeupfd_event,它是通过pipe的机制实现的,这个管道fd的回调函数并没有实现什么功能,它的目的只是为了让select方法能返回一次,这样select()方法就能重新跟踪新参加事件队列的fd和timeout设置。所以在添加新fd到eventLoop时,往往不是直接调用ril_event_add,实际通常用rilEventAddWakeup来添加,这个方法除了会间接调用ril_event_add外,还会调用triggerEvLoop()函数来向s_fdWakeupWrite中写入一个空字符,这样select()函数会返回并重新执行,新参加的文件描述符便得以被select()加载并跟踪。如果在ril_event队列中任何一个fd已经准备好,则进入分析流程:processTimeouts(),processReadReadies(&rfds,n),firePending()其中firePending()方法执行这个event的func,也就是回调函数。在AndroidRIL初始化完成后,将有几个event被挂入到eventLoop中:1.s_listen_event:名为rild的socket,主要requeset&response通道2.s_debug_event:名为rild-debug的socket,调试用requeset&response通道3.s_wakeupfd_event:无名管道,用于队列主动唤醒这其中最为重要的event就是s_listen_event,它作为request与response的通道实现。在ril_event.cpp中还持有一个watch_table数组,一个timer_list链表和一个pending_list链表。watch_table数组的目的很单纯,存放当前被eventLoop等待的ril_event〔非timerevent〕,供eventLoop唤醒时使用。timer_list是存放timerevent的链表,在eventLoop唤醒时要对这些timerevent单独进展处理pending_list:待处理(对其回调函数进展调用)的所有ril_event的链表。1.4.AndroidRIL中初始化流程分析●
Rild的初始化流程初始化流程从rild.c中的main函数开场,它被init守护进展调用执行:首先在main()函数会首先通过dlopen()函数加载VendorRIL〔在自带的参考实现中为librefrence_ril.so〕。接着调用RIL_startEventLoop〔〕函数来启动消息队列机制。调用librefrence_ril.so的RIL_Init()函数来进展VendorRIL的初始化。RIL_Init()函数执行后会返回一个RIL_RadioFunction构造体,这个构造体最重要的成员就是onRequest()方法。onRequest()方法会被dispatchFunction调用,也就是说dispatchFunction调用是程序流从rild转入VendorRIL的分界点。RIL_register()函数将实现两个目地,一个是将RIL_INIT中获得的RIL_RadioFunction进展注册,rild通过此种方式保证自己持有一个RIL_RadioFunction实例,第二个是将s_fdListen参加到消息队列机制中,开启s_fdListen的事件监听。●
VendorRIL的初始化流程:RIL_Init被调用后首先通过参数获取硬件接口的设备文件或模拟硬件接口的socket。〔参见上文中对reference-ril文件夹的介绍〕接下来是创立mainLoop线程,并跳入到线程执行。mainLoop会建立起与硬件的通信,然后通过read方法阻塞等待硬件的主动上报或响应。mainLoop还会调用initlizeCallBack()函数来向radio发送一系列的AT命令来进展radio的初始化设置工作。1.5.AndroidRIL中request流程分析上层应用开场向rild通过socket传输数据时,通过RIL消息队列机制,s_listen_event的回调函数listenCallBack将会被调用,开场进展数据流的分析与处理。接下来s_fdmand=accept(s_fdListen,(sockaddr*)&peeraddr,&socklen),获取传入的socket描述符,也就是上层的javaRIL传入的连接。然后,通过record_stream_new()建立起一个RecordStream,将这个record_stream与s_fdmand绑定,RecordStream实际上是一个用于存放数据的构造体,这个构造体提供了一些操作类来保证这个RecordStream所绑定的文件描述符被读取时里面的数据会被完整读取。一旦s_fdmand中有数据,它的回调函数processmandsCallback()将会被调用,processmandsCallback()通过record_stream_get_ne*t阻塞读取s_fdmand上发来的数据,直到收到一完整的request。然后将其传递进processmandBuffer()函数,processmandBuffer()正式进入了命令的解析局部。每个接收到的命令将以RequestInfo的形式存在。从socket过来的数据流,是Parcel处理过的序列化字节流,在这里会通过反序列化的方法提取出来。最前面的是request号,以及token域(request的递增序列号)。request号是一个mandInfo,它在ril_mand.h中定义。接下来,这个RequestInfo会被挂入pending的request队列,执行具体的dispatchFunction(),进展详细解析。dispatchFunction方法有着多种实现,如dispatchVoid,dispatchString,它们的调用取决于Parcel的参数传入形式。比方说在dispatchDial方法中,Parcel对象将被解析为RIL_Dial构造。这是disptachFunction的任务之一,它的另一个任务就是调用onRequest()方法,并将解析的容传入onRequest()方法。从onRequest方法开场,程序控制流脱离了RILD,进入到了VendorRIL中。onRequest方法会通过传入的请求类型来调用指定的request×××()方法,request×××()方法则负责组装AT指令并下发给at_send_mand()方法集合中的一个,这个方法集合提供了针对不同类型AT指令的实现,如单行AT指令at_send_mand_singleline(),短信息指令at_send_mand_sms()等。最后,执行at_send_mand_full(),再通过一个互斥的at_send_mand_full_nolock()调用,完成最终的写出操作,在writeline()中,写出到初始化时翻开的设备中。需要注意的是:at_send_mand_full_nolock()在将指令写入radio后并不会直接返回,而是通过条件变量等待响应信息,得到响应信息后会携带这些信息返回。具体流程可以参考下面的response流程分析。1.6.AndroidRIL中response流程分析AT的response有两种,一种是unsolicited。另一种是普通response,也就是命令的响应。response信息的获取在readerLoop()中。由readline()函数读取上来。读取到的line将被传入processLine()函数进展解析,processLine()函数首先会判断当前的响应是主动响应还是普通响应,如果是主动响应,将调用handleUnsolicited()函数,如果为普通响应,则将调用handleFinalResponse()函数进展处理对响应串的主要的解析过程,由at_tok.c中的各种解析函数完成,提供字符串分析解析功能。●
对主动上报的解析handleUnsolicited()方法处理主动上报,它会调用onUnsolicited()来进展进一步的解析,这个函数在Vendor-RIL初始化时被传入at_open〔〕函数,onUnsolicited只解析出头部(一般是+****的形式),然后按类型决定下一步操作,操作为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,onPDPConte*tListChanged等回调,不用返回上层,部处理就可以。●
对普通上报的解析IsFinalResponse()和isFinalResponseError()所处理的是一条AT指令的响应上报,它们将转入handleFinalResponse方法。handleFinalResponse()函数会将所有响应信息装入sp_response,这是一个ATResponse构造,它的成员包括成功与否〔success〕以及一个中间结果〔p_intermediates〕。handleFinalResponse()在将响应结果保存至sp_response后,设置s_mandcond这一条件变量,此条件变量由at_send_mand_full_nolock等待。at_send_mand_full_nolock获得到了完整的响应信息〔在sp_response中〕,便开场进展响应信息的处理,最后由RIL_onRequestplete将响应数据序列化并通过sendResponse传递至与应用层通信的socket,这一局部与RIL_onUnsolicitedResponse()函数的功能非常相似,请参考对主动上报的解析局部。2.AndroidRIL与WindowsMobileRILAndroidRIL与WindowsMobileRIL在设计思路上都是作为一个radio的抽象,为上层提供效劳,但在实现方式上两者有着一定的差异,这种差异的产生主要是源自操作系统机制的不同。AndroidRIL被实现为HAL,相对于windowsmobile中被实现为驱动的方式,AndroidRIL模块的聚性更为理想,可维护性也将更强,你也可以把AndroidRil看做一个中间件。AndroidRIL局部的开发工作,只需要拿到相应的radio文件描述符,就可以进展操作,无需关注radio的I/O驱动实现。2.1两者在与应用通信上的实现比照WindowsMobileRIL在实现与应用的通信时提供了RILPro*y,在这个层面中它定义了大量的RIL_***()函数来作为效劳请求。这一点与AndroidRIL的实现比拟相似,AndroidRIL中在ril.h提供了一系列的宏来定义效劳请求。在Android中的rild功能类似于windowsmobileRIL的RILpro*y。它同样也是起到一个中介的作用,为上层接口向下传递请求,并上传回响应。在windowsmobileRIL中要为每一个应用程序客户提供一份RilPro*y实例。对于这两种操作系统平台,RIL所定义的所有请不可更改的。2.2两者在线程构造与回调机制上的比照在windowsmobile的设计中,request与response被设计为异步执行的,他们分别使用两个队列来对它们的异步行为进展管理,执行命令下发和上报命令处理的过程也互不影响,下发命令与命令的相应响应之间的依赖关系由应用程序来捏合。在androidril中的request与response设计与windowsmobile不同,它的命令与响应之间是同步的过程。也就是说一条命令被下发后,将等待执行结果,并进展处理,再上向上层发。而不是直接异步的进展处理和向上发送。3.AndroidRILporting3.1.命名要实现*个无线模块的RIL,需要创立一个实现了所有请求方法的共享库,保证Android能够响应无线通信请求。所有的请求被定义ril.h中。不同的Modem使用不同的端口,这个在init.rc中设置。
Android提供了一个参考VendorRIL,RIL参考源码在reference-ril。
将你自己的VendorRIL实现编译为共享库形式:
libril-<panyname>-<RILversion>.so
比方:
libril-techfaith-124.so
其中:
libril:所有vendorRIL的开头
<panyname>:公司缩写
<RILversion>:RIL版本number
so:文件扩展3.2.AndroidRIL的配置与加载在init.rc文件中,将通过这种方式来进展AndroidRIL的加载。serviceril-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.AndroidRIL的编译构造rild:被编译成可执行文件,rild以守进程的形式执行。libril:将被编译为共享库,并被链入rild。VendorRIL:可以以两种方式来运行,如果定义了RIL_SHLIB宏,则它将被编译成共享库,如果没定义RIL_SHLIB宏,它将以守护进程程序的方式被调用执行。4.AndroidRIL的java框架
AndroidRIL的Java局部也被分为了两个模块,RIL模块与Phone模块。其中RIL模块负责进展请求以及相应的处理,它将直接与RIL的原声代码进展通信。而Phone模块则向应用程序开发者提供了一系列的功能接口。模块构造在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来进展处理。模块构造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机制〕。参考资料相关:code.google./android/android-dls..ibm./developerworks//opensource/theme/android/5功能概述Android的RadioInterfaceLayer(RIL)提供了效劳和的radio硬件之间的抽象层。RadioInterfaceLayerRIL(RadioInterfaceLayer)负责数据的可靠传输、AT命令的发送以及response的解析。应用处理器通过AT命令集与带GPRS功能的无线通讯模块通信。
ATmand由Hayes公司创造,是一个调制解调器制造商采用的一个调制解调器命令语言,每条命令以字母"AT"开头。JAVAFramework代码的路径为:frameworks/base/telephony/java/android/telephonyCore
native:在hardware/ril目录中,提供了对RIL支持的本地代码,包括4个文件夹:hardware/ril/include
hardware/ril/libril
hardware/ril/reference-ril
hardware/ril/rildkernelDriver
在Linu*核的驱动中,提供了相关的驱动程序的支持,可以建立在UART或者SDIO,USB等高速的串行总线上。功能各个局部:hardware/ril/include/telephony/目录中的ril.h文件是ril局部的根底头文件。其中定义的构造体RIL_RadioFunctions如下所示:typedefstruct{
intversion;
RIL_RequestFunconRequest;
RIL_RadioStateRequestonStateRequest;
RIL_Supportssupports;
RIL_CancelonCancel;
RIL_GetVersiongetVersion;}RIL_RadioFunctions;
RIL_RadioFunctions中包含了几个函数指针的构造体,这实际上是一个移植层的接口,下层的库实现后,由rild守护进程得到这些函数指针,执行对应的函数。几个函数指针的原型为:typedefvoid(*RIL_RequestFunc)(intrequest,void*data,
size_tdatalen,RIL_Tokent);typedefRIL_RadioState(*RIL_RadioStateRequest)();typedefint(*RIL_Supports)(intrequestCode);typedefvoid(*RIL_Cancel)(RIL_Tokent);typedefconstchar*(*RIL_GetVersion)(void);其中最为重要的函数是onRequest(),它是一个请求执行的函数。5.1rild守护进程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的加载过程,它会执行如下操作:动态加载VendorRIL的.so文件执行RIL_startEventLoop()开启消息队列以进展事件监听通过执行VendorRIL的rilInit()方法来进展VendorRIL与libril的关系建立。在rild文件夹中还包括一个radiooptions.c文件,它的作用是通过串口将一些radio相关的参数直接传给rild来对radio进展配置。5.2libreference-ril.so动态库libreference-ril.so动态库的路径是:hardware/ril/reference-ril其中主要的文件是reference-ril.c和atchannel.c。这个库必须实现的是一个名称为RIL_Init的函数,这个函数执行的结果是返回一个RIL_RadioFunctions构造体的指针,指针指向函数指针。这个库在执行的过程中需要创立一个线程来执行实际的功能。在执行的过程中,这个库将翻开一个/dev/ttyS***的终端〔终端的名字是从上层传入的〕,然后利用这个终端控制硬件执行。附:在编译时libril被链入rild,它为rild提供了event处理功能,还提供了在rild与VendorRIL之间传递请求和响应消息的能力。Libril提供的主要功能分布在两个主要方法,一个是RIL_startEventLoop()方法,另一个是RIL_register()方法RIL_startEventLoop()方法所提供的功能就是启用eventLoop线程,开场执行RIL消息队列。RIL_register()方法的主要功能是启动名为rild的监听端口,等待java端通过socket进展连接。5.3libril.so动态库
libril.so库的目录是:hardware/ril/libril
其中主要的文件为ril.cpp,这个库主要需要实现的以下几个接口为:RIL_startEventLoop(void);voidRIL_setcallbacks(constRIL_RadioFunctions*callbacks);RIL_register(constRIL_RadioFunctions*callbacks);RIL_onRequestplete(RIL_Tokent,RIL_Errnoe,void*response,size_tresponselen);
voidRIL_onUnsolicitedResponse(intunsolResponse,void*data,
size_tdatalen);RIL_requestTimedCallback(RIL_TimedCallbackcallback,void*param,
conststructtimeval*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/ttyS*5.4ril层的所有代码分析5.4.1ril/rild下的文件rild.c->mian()为函数入口
intmain(intargc,char**argv){//....//OpenLib:*endif
switchUser();//翻开dlopen()加载vendorRIL获取由RIL_register(funcs);注册进来的参数,并解析dlHandle=dlopen(rilLibPath,RTLD_NOW);
if(dlHandle==NULL){
fprintf(stderr,"dlopenfailed:%s\n",dlerror());
e*it(-1);}//1:消息队列的入口1.到select阻塞//每当看到打印信息,不按顺序打下来说明阻塞RIL_startEventLoop();//通过dlsym函数得到rilInit函数指针的引用rilInit=(constRIL_RadioFunctions*(*)(conststructRIL_Env*,int,char**))dlsym(dlHandle,"RIL_Init");if(rilInit==NULL){
fprintf(stderr,"RIL_Initnotdefinedore*portedin%s\n",rilLibPath);
e*it(-1);
}
if(hasLibArgs){
rilArgv=argv+i-1;
argc=argc-i+1;
}else{
staticchar*newArgv[MA*_LIB_ARGS];
staticcharargs[PROPERTY_VALUE_MA*];
rilArgv=newArgv;
property_get(LIB_ARGS_PROPERTY,args,"");
argc=make_argv(args,rilArgv);
}
//Makesurethere'sareasonableargv[0]
rilArgv[0]=argv[0];
//2:利用得到的rilInit函数指针,调用真正的RIL_Init
funcs=rilInit(&s_rilEnv,argc,rilArgv);RIL_register(funcs);done:
while(1){
//sleep(UINT32_MA*)seemstoreturnimmediatelyonbionic
sleep(0*00ffffff);
}ril/libril->ril.cpp中RIL_startEventLoop()函数分析RIL_startEventLoop(void){
intret;
pthread_attr_tattr;
LOGD("-2--ril.cpp--RIL_startEventLoop---");
/*spinupeventLoopthreadandwaitforittogetstarted*/
s_started=0;
pthread_mute*_lock(&s_startupMute*);
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
LOGD("-1-ril.cpp--kai-kou--pthread_create(&s_tid_dispatch,&attr,eventLoop,NULL);------");
ret=pthread_create(&s_tid_dispatch,&attr,eventLoop,NULL);//创立线程,为入口函数
LOGD("-2.....-ril.cpp--tiao-chu--rpthread_create(&s_tid_dispatch,&attr,eventLoop,NULL);----");
while(s_started==0){
pthread_cond_wait(&s_startupCond,&s_startupMute*);
}
pthread_mute*_unlock(&s_startupMute*);
if(ret<0){
LOGE("Failedtocreatedispatchthreaderrno:%d",errno);
return;
}
}---------------------------------------------------------eventLoop(void*param){1.---ril_event_init();//初始化voidril_event_init()
{
MUTE*_INIT();
LOGD("-2.2-Ril_evnet.cpp-ril_event_init-shi-*ian-han-shu-");
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实现voidril_event_set(structril_event*ev,intfd,boolpersist,ril_event_cbfunc,void*param)
{
LOGD("----RIL_evnent.cpp-shi-*ian--voidril_event_set(structril_event*ev,intfd,boolpersist,ril_event_cbfunc,void*param)-");
dlog("~~~~ril_event_set%*~~~~",(unsignedint)ev);
memset(ev,0,sizeof(structril_event));
ev->fd=fd;
ev->inde*=-1;
ev->persist=persist;
ev->func=func;
ev->param=param;
ftl(fd,F_SETFL,O_NONBLOCK);
}3.1processWakeupCallbackstaticvoidprocessWakeupCallback(intfd,shortflags,void*param){
charbuff[16];
intret;
LOGD("--2.5-ril_event_set--processWakeupCallback-read(s_fdWakeupRead,&buff,sizeof(buff));---");
LOGV("processWakeupCallback");/*emptyourwakeupsocketout*/
do{
ret=read(s_fdWakeupRead,&buff,sizeof(buff));
}while(ret>0||(ret<0&&errno==EINTR));
}4.---rilEventAddWakeup(&s_wakeupfd_event);staticvoidrilEventAddWakeup(structril_event*ev){
LOGD("-2.7-rilEventAddWakeup--write--tiao--ru--
ril_event_add(ev);---");
ril_event_add(ev);
LOGD("--2.8--ril_event_add(ev);---tiao--chu---");
triggerEvLoop();}4.1---voidril_event_add(structril_event*ev)
在ril/libril/ril_event.cpp实现{
LOGD("---RIL_evnent.cpp--shi--*ian--voidril_event_add(structril_event*ev)----");
dlog("~~~~+ril_event_add~~~~");
MUTE*_ACQUIRE();
for(inti=0;i<MA*_FD_EVLL){
//1:
watch_table[i]=ev;//把上面ril_event_add函数添加的事件_wakeupfd_event构造体添加到这个数组来
ev->inde*=i;
dlog("~~~~addedat%d~~~~",i);
dump_event(ev);
//2:
FD_SET(ev->fd,&readFds);
if(ev->fd>=nfds)nfds=ev->fd+1;
dlog("~~~~nfds=%d~~~~",nfds);
break;ENTS;i++){
if(watch_table[i]==NU
}
}
MUTE*_RELEASE();
dlog("~~~~-ril_event_add~~~~");
}4.2---staticvoidtriggerEvLoop(){
intret;
LOGD("---ret=write(s_fdWakeupWrite,"",1);---");
if(!pthread_equal(pthread_self(),s_tid_dispatch)){
/*triggereventlooptowakeup.Noreasontodothis,
*ifwe'reintheeventloopthread*/
do{
ret=write(s_fdWakeupWrite,"",1);
}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的阻塞的去读取上层发下来的命令,并响应--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
intmain(intargc,char**argv){
constchar*rilLibPath=NULL;
char**rilArgv;
void*dlHandle;
constRIL_RadioFunctions*(*rilInit)(conststructRIL_Env*,int,char**);
constRIL_RadioFunctions*funcs;
charlibPath[PROPERTY_VALUE_MA*];
unsignedcharhasLibArgs=0;
........OpenLib:
*endif
switchUser();
/*翻开dlopen()函数,就会动态去加载动态库vendorRIL获取由RIL_register(funcs);注册进来的参数,并解析*/
dlHandle=dlopen(rilLibPath,RTLD_NOW);
if(dlHandle==NULL){
fprintf(stderr,"dlopenfailed:%s\n",dlerror());
e*it(-1);
}
/*消息队列的入口,添加到select,用阻塞方式去读取那些ril_event_set()的数据**每当看到打印信息,不按顺序打下来说明阻塞***/
RIL_startEventLoop();
/*通过dlsym函数得到rilInit函数指针的引用*/
rilInit=(constRIL_RadioFunctions*(*)(conststructRIL_Env*,int,char**))dlsym(dlHandle,"RIL_Init");
if(rilInit==NULL){
fprintf(stderr,"RIL_Initnotdefinedore*portedin%s\n",rilLibPath);
e*it(-1);
}
if(hasLibArgs){
rilArgv=argv+i-1;
argc=argc-i+1;
}else{
staticchar*newArgv[MA*_LIB_ARGS];
staticcharargs[PROPERTY_VALUE_MA*];
rilArgv=newArgv;
property_get(LIB_ARGS_PROPERTY,args,"");
argc=make_argv(args,rilArgv);
}
//Makesurethere'sareasonableargv[0]
rilArgv[0]=argv[0];
/*利用得到的rilInit函数指针,调用真正的RIL_Init,实际是动态加载动态库去reference-ril.c,由dlopen()函数加载*/
funcs=rilInit(&s_rilEnv,argc,rilArgv);
/*RIL_register作用一:把vendorRIL(即RIL_init)注册到reference-ril库去等待,dopen()函数加载
附:RIL_init通过是onRequest()方法,将上层来的请求进展映射后转换成对应的AT命令发给硬件,rild通过RIL_register注册这一指针。
RIL_register作用二:创立rildsocket主要是等待java层得数据通过,传到下一层,还创立debugsocket*/
RIL_register(funcs);done:
while(1){
//sleep(UINT32_MA*)seemstoreturnimmediatelyonbionic
sleep(0*00ffffff);
}---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------所有文件:hardware/ril/reference-ril$lsAndroid.mk
atchannel.h
at_tok.h
misc.h
NOTICE
atchannel.cat_tok.cril_event.h
reference-ril.c
misc.c
MODULE_LICENSE_APACHE2
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------在编译时libril被链入rild,它为rild提供了event处理功能,还提供了在rild与VendorRIL之间传递请求和响应消息的能力。libril.so驻留在rild这一守护进程中,主要完成同上层通信的工作,承受ril请求并传递给librefrence_ril.so,同时把来自librefrence_ril.so的反应回传给调用进程。librefrence.so负责直接与modem通信,这包括将来自libril的指令转换为AT指令,并且将AT指令写入modem中。reference-ril会接收调用者传来的参数,参数容为与radio的通信方式。如通过串口连接modem,则参数为这种形式:-d/dev/ttyS*funcs=rilInit(&s_rilEnv,argc,rilArgv);//实际是通过动态加载动态库的方式执行reference-ril.c中的RIL_Init-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
reference-ril/reference-ril.c->RIL_RadioFunctions*RIL_Init()函数--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------constRIL_RadioFunctions*RIL_Init(conststructRIL_Env*env,intargc,char**argv){
intret;
intfd=-1;
intopt;
pthread_attr_tattr;
s_rilenv=env;
LOGD("--referencen_ril.c--RIL_RadioFunctions-shi-*ian--*RIL_Init-----");
while(-1!=(opt=getopt(argc,argv,"p:d:s:"))){
switch(opt){
case'p':
s_port=atoi(optarg);
if(s_port==0){
usage(argv[0]);
returnNULL;
}
LOGI("Openingloopbackport%d\n",s_port);
break;
//获取dev/ttyUSB,只是翻开而已,是否获取,由线程mainLoop执行完才知道
case'd':
s_device_path=optarg;
LOGI("Openingttydevice%s\n",s_device_path);
break;
case's':
s_device_path
=optarg;
s_device_socket=1;
LOGI("Openingsocket%s\n",s_device_path);
break;
default:
usage(argv[0]);
returnNULL;
}
}
if(s_port<0&&s_device_path==NULL){
usage(argv[0]);
returnNULL;
}
//上面获取了设备节点和socket
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
LOGD("---reference-ril.c--ril_init--ru-kou-
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.翻开rildsocket〔init.rc传过来的〕是用于等待read()后java层传来的数据(再去调nitializeCallback发at命令)2.就是调用RIL_requestTimedCallback(initializeCallback,NULL,&TIMEVAL_0);函数,用来向modem发生AT命令-------------------------------------------------
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 吉他制作工岗前变更管理考核试卷含答案
- 执业医病例分析题鉴别诊断及治疗原则
- 人类基因组编辑技术与应用
- 手术室无菌技术的操作原则
- 格林巴利综合症护理查房
- 医学课件-少儿青春期发育特点
- 数字孪生手术技能培训的市场前景分析
- 《营养过剩病》课件
- 静脉输液的安全管理专家讲座
- 原发性脊柱侧弯的康复治疗
- 【MOOC】研究生英语科技论文写作-北京科技大学 中国大学慕课MOOC答案
- 2024年私人借款合同范例
- 2024年秋新冀教版一年级上册数学 1.2.1 加法与减法的初步认识 教学课件
- Be动词是个好妈妈她有三个乖娃娃(课件)英语三年级上册
- 水电站安全守护制度
- DL-T825-2021电能计量装置安装接线规则
- 英语四六级词汇汇总(带音标+免费下载)
- 如愿三声部合唱简谱
- 《发现雕塑之美》第4课时《加法与减法的艺术》
- GB/T 3292.1-2008纺织品纱线条干不匀试验方法第1部分:电容法
- 第四届编校大赛试题及答案(含编辑、校对)
评论
0/150
提交评论