ril - 2

/***************************************************************************/

 上文说到request是接收,是通过ril_event_loop中的多路复用I/O,也对初始化做了 分析.现在我们来仔细看看这个机制如何运转。

  ril_event_set负责配置一个event,主要有两种event:
ril_event_add添加使用多路I/O的event,它负责将其挂到队列,同时将event的通道 句柄fd加入到watch_table,然后通过select等待.
ril_timer_add添加timer event,它将其挂在队列,同时重新计算最短超时时间.
无论哪种add,最后都会调用triggerEvLoop来刷新队列,更新超时值或等待对象.

  刷新之后, ril_event_loop从阻塞的位置,select返回,只有两种可能,一是超时,二 是等待到了某I/O操作.
超时的处理在processTimeouts中,摘下超时的event,加入pending_list.
检查有I/O操作的通道的处理在processReadReadies中,将超时的event加入 pending_list.
最后在firePending中,检索pending_list的event并依次执行event->func.
这些操作完之后,计算新超时时间,并重新select阻塞于多路I/O.

  前面的初始化流程已分析得知,初始化完成以后,队列上挂了3个event对象,分别是:
s_listen_event: 名为rild的socket,主要requeset & response通道
s_debug_event: 名为rild-debug的socket,调试用requeset & response通道(流 程与s_listen_event基本相同,后面仅分析s_listen_event)
s_wakeupfd_event: 无名管道,用于队列主动唤醒(前面提到的队列刷新,就用它来 实现,请参考使用它的相关地方)。

 

/***************************************************************************/

 

明白了event队列的基本运行流程,我们可以来看看request是怎么传入和dispatch的了.
上层的部分,核心代码在 frameworks/base/telephony/java/com/android/internal/telephony/gsm/RIL.java, 这是android java框架处理radio(gsm)的核心组件.本文因为主要关注rild,也就是 驱动部分,所以这里只作简单介绍.
我们看一个具体的例子,RIL.java中的dial函数:
public void
dial (String address, int clirMode, Message result)
{
RILRequest rr = RILRequest.obtain(RIL_REQUEST_DIAL, result);

rr.mp.writeString(address);
rr.mp.writeInt(clirMode);

if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));

send(rr);
}

  rr是以RIL_REQUEST_DIAL为request号而申请的一个RILRequest对象.这个request 号在java框架和rild库中共享(参考RILConstants.java中这些值的由来)。

  RILRequest初始化的时候,会连接名为rild的socket(也就是rild中 s_listen_event绑定的socket),初始化数据传输的通道.
rr.mp是Parcel对象,Parcel是一套简单的序列化协议,用于将对象(或对象的成 员)序列化成字节流,以供传递参数之用.这里可以看到 String address和int clirMode都是将依次序列化的成员.在这之前,rr初始化的时候,request号跟 request的序列号(自动生成的递增数),已经成为头两个将被序列化的成员.这为 后面的request解析打下了基础。

  接下来是send到handleMessage的流程,send将rr直接传递给另一个线程的 handleMessage,handleMessage执行data = rr.mp.marshall()执行序列化操作, 并 将data字节流写入到rild socket。

  接下来回到我们的rild,select发现rild socket有了请求链接的信号,导致 s_listen_event被挂入pending_list,执行event->func,即
static void listenCallback (int fd, short flags, void *param);。

  接下来,s_fdCommand = accept(s_fdListen, (sockaddr *) &peeraddr, &socklen),获取传入的socket描述符,也就是上层的java RIL传入的连接。

  然后,通过record_stream_new建立起一个record_stream, 将其与s_fdCommand绑 定, 这里我们不关注record_stream 的具体流程, 我们来关注command event的回 调, processCommandsCallback函数, 从前面的event机制分析, 一旦s_fdCommand 上有数据, 此回调函数就会被调用. (略过onNewCommandConnect的分析)。

  processCommandsCallback通过record_stream_get_next阻塞读取s_fdCommand上发 来的 数据, 直到收到一完整的request(request包的完整性由record_stream的机 制保证), 然后将其送达processCommandBuffer。

  进入processCommandBuffer以后,我们就正式进入了命令的解析部分. 每个命令将 以RequestInfo的形式存在。
 

typedef struct RequestInfo {
int32_t token;
// this is not RIL_Token
CommandInfo * pCI;
struct RequestInfo
* p_next;
char cancelled;
char local; // responses to local commands do not go back to command process
} RequestInfo;

  这里的pRI就是一个RequestInfo结构指针, 从socket过来的数据流, 前面提到是 Parcel处理过的序列化字节流, 这里会通过反序列化的方法提取出来. 最前面的是 request号, 以及token域(request的递增序列号). 我们更关注这个request号, 前 面提到, 上层和rild之间, 这个号是统一的. 它的定义是一个包含ril_commands.h 的枚举, 在ril.cpp中
 

static CommandInfo s_commands[] = {
#include
" ril_commands.h "
};
pRI直接访问这个数组, 来获取自己的pCI.
这是一个CommandInfo结构:
typedef struct {
int requestNumber;
void ( * dispatchFunction) (Parcel & p, struct RequestInfo * pRI);
int ( * responseFunction) (Parcel & p, void * response, size_t responselen);
} CommandInfo;

  基本解析到这里就完成了, 接下来, pRI被挂入pending的request队列, 执行具体 的pCI->dispatchFunction, 进行详细解析.

 

 

/***************************************************************************/

 

 

 

对dial而言, CommandInfo结构是这样初始化的:
{RIL_REQUEST_DIAL, dispatchDial, responseVoid},

  这里执行dispatchFunction, 也就是dispatchDial这一函数.我们可以看到其实有 很多种类的dispatch function, 比如dispatchVoid, dispatchStrings, dispatchSIM_IO等等, 这些函数的区别, 在于Parcel传入的参数形式,Void就是不 带参数的,Strings是以string[]做参数,又如Dial等,有自己的参数解析方式,以此类推。

  request号和参数现在都有了,那么可以进行具体的request函数调用了 

  s_callbacks.onRequest(pRI->pCI->requestNumber, xxx, len, pRI)完成这一操作。

  s_callbacks是上篇文章中提到的获取自libreference-ril的RIL_RadioFunctions 结构指针,request请求在这里转入底层的libreference-ril处理,handler是 reference-ril.c中的onRequest。

  onRequest进行一个简单的switch分发,我们依然来看RIL_REQUEST_DIAL,流程是 onRequest-->requestDial-->at_send_command-->at_send_command_full-->at_send_command_full_nolock-->writeline。

  requestDial中将命令和参数转换成对应的AT命令,调用公共send command接口 at_send_command。

  除了这个接口之外,还有at_send_command_singleline,at_send_command_sms, at_send_command_multiline等,这是根据at返回值,以及发命令流程的类型来区别 的.比如at+csq这类,需要 at_send_command_singleline,而发送短信,因为有 prompt提示符">",传裸数据,结束符等一系列操作,需要专门用 at_send_command_sms来实现。

  然后执行at_send_command_full,前面几个接口都会最终到这里,再通过一个互斥的 at_send_command_full_nolock调用,然后完成最终的写出操作,在writeline中,写 出到初始化时打开的设备中。writeline返回之后,还有一些操作,如保存type等信息,供response回来时候使用, 以及一些超时处理. 不再详述。到这里,request的详细流程,就分析完毕了。

 

 

 

 

 

参考资源链接:[Android通话流程解析:RIL-Java到DriverCall](https://wenku.youkuaiyun.com/doc/4br72n9v8b?utm_source=wenku_answer2doc_content) 要通过RIL-Java实现电话呼叫的异步处理流程,首先需要理解RIL-Java在Android电话系统中的作用。RIL-Java作为中间层,负责封装与RIL的通信,并提供上层应用程序所需的接口。以下步骤将指导您实现电话呼叫的异步处理流程: 1. 使用CommandInterface定义电话呼叫的命令接口,通过这些接口可以发起拨打电话、接听电话等操作。例如,拨打电话通常使用`outgoingCall`方法。 2. RILSender负责将这些命令通过监听套接字发送给RILD。这里需要创建一个RILRequest对象,设置相应的命令和参数,然后通过套接字发送给RILD进行处理。 3. RILReceiver负责接收来自RILD的响应。在异步处理机制下,响应并不是立即返回给请求发起者的,而是通过注册的回调接口异步接收。当RILD处理完命令并产生响应时,它将通过监听套接字返回结果,RILReceiver再将这个响应转发给对应的回调接口。 4. 实际的电话呼叫处理逻辑在RILD中完成。RILD接收到命令后,会转换成硬件级别的操作,如通过AT命令与基带处理器通信。当硬件操作完成并返回结果时,RILD将结果封装成响应消息回传给RIL-Java。 5. 通过RIL-Java的异步处理机制,将RILD返回的结果通过回调接口传递给最初发起请求的应用程序或服务,应用程序可以更新UI或执行其他相关操作。 在这个过程中,您需要深入理解RIL-Java中的异步处理机制,以及如何利用RILSender发送命令和RILReceiver接收响应。要实现这一流程,推荐您阅读《Android通话流程解析:RIL-Java到DriverCall》这本书。它详细解析了Android系统的通话流程,涉及到了RIL-Java、Call、Phone、Connection、DriverCall和CallTracker等核心组件。通过这本书的学习,您可以更加深入地掌握电话呼叫的异步处理流程,并且对RIL-Java组件有一个全面的理解。 参考资源链接:[Android通话流程解析:RIL-Java到DriverCall](https://wenku.youkuaiyun.com/doc/4br72n9v8b?utm_source=wenku_answer2doc_content)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值