RILJ 概述

转自:http://book.51cto.com/art/201301/378269.htm (转载请注明出处)

《深入剖析Android系统》第9章RIL,本章在概述部分介绍了涉及Telephony部分的各个进程的交互状况,然后对运行在rild守护进程中nativce层的ril代码在接收RIL请求和主动上报消息时的执行流程做了详细介绍。接着,简要介绍了radiooptions工具。最后,介绍了Java层的RIL类(RILJ),包括它的RIL请求发送流程和接收消息的流程。本节为RILJ概述。

9.5  RILJ

9.5.1  RILJ概述

与rild守护进程交互的Java部分是com.android.internal.telephony.RIL类(以下简称RILJ),它们是通过UNIX套接字(/dev/socket/rild)进行通信。进程com.android.phone通过使用Teleohony框架来使用系统的各种Teleohony功能。如图9-2所示,Android Frameworks中的Telephony负责复杂的逻辑处理,这些逻辑最终通过RILJ向rild守护进程发送请求,或从rild收到消息,然后由Frameworks中Telephony的相关Java类去处理。

 
图9-2  RILJ调用关系图
关于Telephony部分的代码位于目录下,包括RILJ的代码:
 
 
  1. frameworks/base/telephony/java/com/android/internal/telephony/ 

Telephony的SDK API类的代码则位于:
 
 
  1. frameworks/base/telephony/java/android/telephony/ 

Java接口类CommandsInterface定义了与RILJ和rild交互的接口。RILConstants.java文件定义了RIL的各种请求request号和unsolicited号,它们必须与ril.h中的定义保持一致,用于标识请求号和unsolicited消息号。 这些标识号也必须出现在ril_commands.h和ril_unsol_commands.h中定义的数组元素中。

文件BaseCommands.java中的BaseCommands类实现了CommandsInterface的部分接口,用于通知手机各种内部状态的变化。它里面包含了很多注册者Registrant和注册者列表RegistrantList,它们代表着对某些事件感兴趣希望接收事件变化通知的接收者。另外,还提供了大量的registerXXX和unregisterXXX函数。当对某种状态变化感兴趣时,就可以调用registerXXX函数将自己注册为一个对某种状态感兴趣的通知接收者。注册时,在BaseCommands内部创建一个对应的registrant或将registrant添加到列表registrantList中。registrantList表示某种状态有多个感兴趣者,因此将registrant添加到RegistrantList中。若只能有一个感兴趣者则创建的是Registrant实例。

在调用registerXXX函数时,调用者将message和handler传递给Registrant对象;当有状态变化(processUnsolicited处理主动上报的信息则往往意味着状态的改变)时,则通过Registrant.NotifyXXX调用Handler将消息发送到消息队列上,Handler所在线程将处理这些消息,这也保证了尽可能实时通知调用者的目的。

RILJ负责与rild守护进程交互,提供API用于执行RIL请求,供Telephony Framework调用;它还提供底层对RIL请求的响应回复的处理,它将以消息的形式发送给调用者。RIL类中定义了两个嵌套类RILSender和RILReceiver,分别负责将RIL请求发送出去和接收rild侧的消息。RILSender是Handler的子类,将在发送线程中处理发送消息等事件;RILReceiver实现了Runnable接口类的Run接口函数,作为线程的执行体运行在RILJ创建的接收线程(见RIL类的构造函数)中,负责消息的接收。具体流程如图9-3所示。

 
图9-3  RILJ发送请求和接收回复信息图
Telephony Framework中的其他类调用RILJ的API,发送一个RILRequest,然后由sender线程调用RILSender将请求送给守护进程rild;对于守护进程侧发送来的消息,RILReceiver收到后,在区分是何种类型的消息后,调用两个process函数去处理它们,最终将msg封装到AsyncResult中发送出去,也就是发送到Handler的线程队列中,然后由Telephony Framework中对应的handler去处理它们,下面进行详细讲述。

9.5.2  RILRequest的发送过程

在文件RIL.java中定义了一个Java类RILRequest,代表着一个即将发送出去的RIL请求,它里面包含了Request请求号、序列号(自0开始累加)和保存请求结果的Message。 Java部分的Request请求号就是上述的RILConstants.java中的常量(与C/C++部分的请求号保持一致)。当需要执行某种RIL请求时,则需创建一个新的RILRequest--使用RILRequest的obtain函数。

 
 
  1. static RILRequest obtain(int request, Message result) 

该obtain静态函数用于从其内部维护的一个RIL request池sPool中取下一个request,得到一个RILRequest实例,它里面的请求号和请求结果消息来自传递的实参。也就是说,调用者在使用obtain函数时,指定了对应的RIL请求号(第一个参数)、请求结果保存在何地(实参result)和结果的处理者(Message中的Handler)。当一个RILRequest对象不再使用时,调用release()函数将其释放回池中。

RIL类继承自BaseCommands,实现了CommandsInterface的部分接口,包含了两个线程sender和receiver(在RIL的构造函数中创建并启动运行)。sender线程用于向rild发送RIL请求;receiver线程用于处理来自rild的response信息(包括对RIL请求的回复和Modem主动上报的unsolicited信息)。它们都是通过读/写UNIX本地套接字来实现的。RIL类还为RILConstants.java中的每一个定义的RIL请求号定义了一个调用API,也为来自Modem的response信息提供了处理函数responseXXX。

使用者在使用RIL请求API时,通过sender线程向rild发送RIL请求,具体是:在这些API函数中,得到一个RILRequest(通过其obtain静态函数)后,将要传递给RIL请求的数据写入到socket中,rild侧解析得到传递来的数据,根据请求号调用相应的分发函数转换成AT命令发送给Modem。

因RIL请求众多,所以RIL类中这类成员函数也较多。一个向SIM/USIM卡提供PIN码的API函数如下(见文件RIL.java):

 

发送请求的send函数负责将请求放置到发送队列上(上面的行00704),它实际上是往sender线程的队列上放了一个msg,该msg的what类型是 EVENT_SEND。发送队列中的待发送请求由发送线程调用RILSender去处理,也就是由RILSender的handleMessage中的switch-case的EVENT_SEND分支处理。这样,线程在处理消息队列的循环中根据what类型将RILRequest请求发送出去。

RILSender的handleMessage函数的switch-case 处理EVENT_SEND类型消息的代码片段如下(见文件RIL.java):

 
上面的行294从消息里得到RILRequest,RIL请求信息在其里面的Parcel成员变量mp中。在得到本地socket后,将Parcel中待写入的数据进行优化后(下面的行00330),由data指向它,然后使用socket的输出流将它们写到socket中,见下面代码中的行00348:
 


这样,RIL的调用者使用API发送RIL请求,将其放置到消息队列上,然后sender线程将请求信息写入到ocket,rild侧在收到后通过dispatch线程将请求分发出去。

在RIL类中还维护了一个RILRequest请求列表,RILRequest类中的整型序列号serial作为其id标识。当RILSender发送一个RIL请求后,则将其添加到该列表中(若发送时出现异常则需再清除);当请求完成并得到回送的response消息后,则将其移除(见 processSolicited 函数)。从列表中移除该RIL请求的函数是:

 
 
  1. private RILRequest findAndRemoveRequestFromList(int serial) 

从上可见,一个RIL请求执行是一个异步的过程:调用者调用RIL类的API函数只是往线程的消息队列上添加了一个消息就返回;然后线程在执行无限循环时将其写入socket中,并将RILRequest对象添加到一个列表中;rild收到请求后进行分发、发送AT命令时还被阻塞等待。当AT命令没有得到响应回复,rild的分发线程被阻塞。Java部分的RIL请求送出去后将得不到处理。RIL请求得到正常处理时,RILReciever所在线程收到数据并解析,然后查询系列号后得到这是某个先前的RIL请求后,将AT执行的返回结果放到AsynResult中,并赋值给Message中的obj成员后,由Message.sendToTarget回送到调用者并由其处理。下面介绍其执行过程。


9.5.3  Response的处理过程(2)

当出现不能识别的请求号时,会抛出异常,然后由行2329~2342来处理:输出信息,然后将结果报给调用者。当执行AT出现错误时,则由行2345~2349处理;当执行正常,AT未出现错误时(也就是大多数情况下),则由后面的代码(行2351~2359)处理:输出log信息;若有结果需要上报,则发送给调用者(行2354~2357)。

因此,函数processSolicited的整个过程执行如下:读取RIL请求序列号并依次在RILRequest列表中查找并选择它。若AT执行成功并有合适数据需要获取,则进入到switch-case语句根据请求号调用相应的responseXXX函数获得AT执行结果数据,放置在Object对象ret中;若有异常发生,则ret保持为null空值。若AT执行发生错误时,则调用RILRequest的onError函数,它也会调用到上面的forMessage函数(实际作用是将三个实参数据封装到Message的obj成员中去),接着调用Message.sendToTarget,由message实参提供者(即RIL请求调用者)去处理,一般在调用者的某个线程(实际为phone进程的主线程)的Handler中去处理这个经过封装返回的消息。当执行成功,输出对应的log信息,然后若有结果需要上报,则送往请求调用者去处理。

不管哪种情况,包括AT执行发生错误在RILRequest.onError中,都会调用到AsyncResult 的 forMessage函数和Message.sendToTarget()函数。

AsyncResult的forMessage函数将三个实参封装到一个AsyncResult实例中,然后将该实例赋值给消息Message的obj成员。forMessage函数具体执行过程如下:创建一个AsyncResult实例ret,三个参数被传递给该实例ret,即AsyncResult实例ret中的数据成员是指向三个实参的引用;最后让第一个实参Message中的 obj对象指向这个新创建的AsyncResult对象。因此,forMessage函数将其三个实参以被引用的形式封装到AsyncResult 中,然后让Message的第一个实参的obj成员指向它,相关代码如下(参见文件AsyncResult.java):

 

Message.sendToTarget()函数则是将消息发送到Msg对应的Handler队列中去,由相应的线程去处理。

对于Unsolicited消息,processUnsolicited函数处理有两个过程:一是根据unsolicited号获取上报的消息数据,二是上报的信息如判断是否有状态改变,从而调用相应的通知函数通知注册者。其分别由两个switch-case分支完成。第一个switch-case得到上报的数据信息,代码如下:

 
第二个switch-case对相应的结果进行进一步处理,如通知感兴趣的注册者,代码如下:
 
本节分析了用C语言编写的RIL的请求发送过程和数据回复上报过程,它们位于rild守护进程空间中,而Java层的RIL则位于phone进程中。RIL请求来自于phone进程,经过RILJ,通过socket送往rild守护进程,经过一系列解释后,变成AT命令字符串写入到AT接口设备。AT接口设备可以返回AT命令执行结果,或送出主动上报的消息,这个过程与请求发送过程的顺序相反,从AT接口设备中出来,由reader线程读取,经过一长串的调用流程中后,通过socket,送往phone进程,由RILJ接收后返还给调用者,或通知注册了自己的感兴趣者,由它们进一步去处理。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值