Android电话系统之概述篇
发布日期:2021-06-30 21:23:27 浏览次数:2 分类:技术文章

本文共 9631 字,大约阅读时间需要 32 分钟。

首先抛开Android的一切概念来研究一下电话系统的最基本的描述。我们的手机首先用来打电话的,随后是需要一个电话本,随后是PIM,随后是网络应用,随后是云计算,随后是想我们的手机无所不能,替代PC。但是作为一个电话的基本功能如下:

0)拨叫电话,接听电话,挂断电话,发送短信,网络连接,PIM管理

1)由于电话运营商为我们提供了呼叫等待,电话会议等补充业务,所以我们的手机需要管理多路通话,如何管理?

2)来电时,我们要播出来电铃声,接通时我们需要切换语音通道,这个又跟多媒体系统打上了交道,例如有耳机插上了,有蓝牙耳机连上了,系统该做如何的管理和切换?

3)上网的网络通路建立(例如GSM GPRS),如何PPP连接并连接到LinuxSocket通道上的?系统如何管理数据连接?

4)AP跟Modem通讯时通过AT指令的,如何将AT指令变成一个个具体的操作函数,如何管理Modem发给我们的回应,AT命令通道,数据通道如何管理?

5)sim卡的电话本如何管理?

上面的关于手机的基本问题,Android电话系统设计者必须要解答的问题。该设计如何的管理框架,提出什么概念来表达?所以要分析Android的电话部分,还是需要理解电话实现的背景知识,通讯协议,大体框架。

    我们回到电话系统基本构成上,先从整体上去把握一下电话模块的大体框架,先从空中俯瞰。我给出的图是一般的智能手机的框架图,该框架基本能够概括所有手机电话模块的构成,当然也包括Android的电话系统构成。

Android电话系统之概述篇 - zhanghaihua415 - 水落石出的博客

 

 

    智能机架构一般是应用处理器+Modem。应用处理器与Modem的连接使用串口或者USB。在一个硬件串口通路上实现为了要同时实现数据传输并同时实现 控制Modem,就需要实现多路复用协议(GSM TS07.10),在底层我们在多路复用的基础上虚拟了两个串口,一个用于CMD通道,一个用于DATA通道。电话的所有控制通路都是在这连个通道上。

   RIL,Radio Interface Layer。本层为一个协议转换层,手机框架需要适应多类型的Modem接入到系统中,而对于不同的Modem有不同的特性,AT指令的格式或者回应有所 不同,但是这种特性在设计应用时不可能完全考虑和兼容。所以设计者在设计电话系统时,建立了一个虚拟电话系统,为该虚拟电话系统规定了标准的功能,上层的 电话管理都是建立在这些标准的功能基础之上。而RIL则是将虚拟电话系统的标准功能转换成实际的所使用的Modem的AT指令。

Android设计者将电话系统设计成了三部分。

Android电话系统之概述篇 - zhanghaihua415 - 水落石出的博客

 

 

    Andoird的Phone Service其实是PhoneApp。GSMPhone(CDMAPhone)是Phone Service核心的对象,他包含了如下的相关对象。

Android电话系统之概述篇 - zhanghaihua415 - 水落石出的博客

 

 

       我们的分析任务就是要把这些对象的相互关系,及其对象间数据传递关系弄清楚。首先我们给出以下的Android电话系统的框架,以便对Android电话系统有个概要的认识,然后从数据流的角度,以及对象的引用关系来分析系统。下面是android电话系统整体框架图。

Android电话系统之概述篇 - zhanghaihua415 - 水落石出的博客

 

 

Android电话系统之-rild

Rild是Init进程启动的一个本地服务,这个本地服务并没有使用Binder之类的通讯手段,而是采用了socket通讯这种方式。RIL(Radio Interface Layer)

Android 给出了一个ril实现框架。由于Android开发者使用的Modem是不一样的,各种指令格式,初始化序列都可能不一样,GSM和CDMA就差别更大 了,所以为了消除这些差别,Android设计者将ril做了一个抽象,使用一个虚拟电话的概念。这个虚拟电话对象就是 GSMPhone(CDMAPhone),Phon对象所提供的功能协议,以及要求下层的支撑环境都有一个统一的描述,这个底层描述的实现就是靠RIL来 完成适配。

Andoid将RIL层分为两个代码空间:RILD管理 框架,AT相关的xxxril.so动态链接库。将RIL独立成一个动态链接库的好处就是Android系统适应不同的Modem,不同的Mode可以有 一个独立的Ril与之对应。从这个层面上看,Rild更多是一个管理框架。

Android电话系统之概述篇 - zhanghaihua415 - 水落石出的博客

  

 

    而ril是具体的AT指令合成者和应答解析者。从最基本的功能来讲,ril建立了一个侦听Socket,等待客户端的连接,然后从该连接上读取RIL- Java成传递来的命令并转化成AT指令发送到Modem。并等待Modem的回应,然后将结果通过套接口传回到Ril-Java层。下图是Ril-D的 基本框架:

Android电话系统之概述篇 - zhanghaihua415 - 水落石出的博客

   

 

下面的数据流传递描述图表描述了RIL-JAVA层发出一个电话指令的5 步曲。

Android电话系统之概述篇 - zhanghaihua415 - 水落石出的博客

 

 

 

在 AT通讯的过程中有两类响应:一种是请求后给出应答,一种是通知类,即为不请自来的,例如短信通知达到,我们称该类通知为URC。在Rild中URC和一 般的Response是分开处理的,概念上URC由handleUnsolicited@Atchannel.c处理,而Response由 handleFinalResponse来处理。

1 Event Loop

Rild 管理的真正精髓在ril.cpp,ril_event.cpp中,在研究的过程中,可以看到设计者在抽象上所下的功夫,设计得很优美。Event Loop的基本工作就是等待在事件端口(串口,Socket),一旦有数据到达就根据登记的Event回调函数进行处理。现在来看Ril设计者是如何建立 一套管理框架来完成这些工作的?

1.1 Event对象

Event对象构成:(fd,index,persist,func,param)

fd 事件相关设备句柄。例如对于串口数据事件,fd就是相关串口的设备句柄
index  
persist 如果是保持的,则不从watch_list中删除。
func 回调事件处理函数
param 回调时参数

    为了统一管理事件,Android使用了三个队列:watch_list,timer_list,pending_list,并使用了一个设备句柄池readFDS。

readFDS:是Linux的fd_set,readFDS保存了Rild中所有的设备文件句柄,以便利用select函数统一的完成事件的侦听。

watch_list:监测时间队列。需要检测的事件都放入到该队列中。

timer_list:timer队列

pending_list:待处理事件队列,事件已经触发,需要所回调处理的事件。

事件队列队列的操作:ril_event_add,ril_event_del, ril_timer_add

Android电话系统之概述篇 - zhanghaihua415 - 水落石出的博客

 

 

在添加操作中,有两个动作:

(1) 加入到watch_list

(2) 将句柄加入到readFDS事件句柄池。

1.2 ril_event_loop()

   我们知道对于Linux设备来讲,我们可以使用select函数等待在FDS上,只要FDS中记录的设备有数据到来,select就会设置相应的标志位并 返回。readFDS记录了所有的事件相关设备句柄。readFDS中句柄是在在AddEvent加入的。所有的事件侦听都是建立在linux的 select readFDS基础上。

Android电话系统之概述篇 - zhanghaihua415 - 水落石出的博客

 

 

ril_event_loop 利用select等待在readFDS(fd_set)上,当select设备有数据时,ril_event_loop会从select返回,在 watch_list中相应的Event放置到pend_list,如果Event是持久性的则不从watch_list中删除。然后 ril_event_loop遍历pengding_list处理Event事件,发起事件回调函数。

1.3 几个重要的Event

上面分析了ril-d的框架,在该框架上跑的事件有什么

(1)s_listen_event- (s_fdListen,listenCallback)

listenCallback处理函数,

接收客户端连接:s_fdCommand=accepte(..)

添加s_commands_event()

重新建立s_listen_event,等待下一次连接

(2) s_command_event(s_fdCommand,ProcessCommandsCallback)

从fdCommand  Socket连接中读取StreamRecord

使用ProcessCommandBufer处理数据

s_listen_event在大的功能上处理客户端连接(Ril-JAVA层发起的connect),并建立s_commands_event去处理Socket连接发来的Ril命令。ProcessCommandBufer实际上包含了Ril指令的下行过程。

1.4 下行命令翻译及其组织@ProcessCommandBuffer

RIL_JAVA传递的命令格式:Parcel , 由命令号,令牌,内容组成。RIL_JAVA到达RIL_C时转为构建本地RequestInfo,并将被翻译成具体的AT指令。由于每条AT命令的参数 是不同的,所以对不同的AT指令,有不同的转换函数,在此Android设计在这里做了一个抽象,做了一个分发框架,通过命令号,利用sCommand数 组,获得该命令的处理函数。

sComand[]={

<...>

}

sComand 存在于Ril_command.h中。

&sComand[]=

<

  {RIL_REQUEST_GET_IMEI, dispatchVoid, responseString},

  {RIL_REQUEST_DIAL, dispatchDial, responseVoid},

{….}

>

dispatchXxx函数一般都放在在Reference-ril.c中,Reference-ril.c这个就是我们需要根据不同的Modem来修改的文件。

1.5 send_at_command框架

send_at_command是同步的,命令发送后,send_at_command将等待在s_commandcond,直到有sp_response->finalResponse。

2 read loop@Atchannel.c

Read loop是解决的问题是:解析从Modem发过来的回应。如果遇到URC则通过handleUnsolicited上报的RIL_JAVA。如果是命令的应答,则通过handleFinalResponse通知send_at_command有应答结果。 

 

Android电话系统之概述篇 - zhanghaihua415 - 水落石出的博客

   

 

对于URC,Rild同样使用一个抽象数组@Ril.CPP.

static UnsolResponseInfo s_unsolResponses[] = {

#include "ril_unsol_commands.h"

};

并利用RIL_onUnsolicitedResponse将URC向上层发送。

3 Ril-d的整体数据流及其控制流示意图

Android电话系统之概述篇 - zhanghaihua415 - 水落石出的博客

 

 

Android RIL-Java

Android电话系统之概述篇 - zhanghaihua415 - 水落石出的博客

 

    RIL-Java在本质上就是一个RIL代理,起到一个转发的作用,是Android Java概念空间中的电话系统的起点。在RIL-D的分析中,我们知道RILD建立了一个侦听套接口,等待RIL-Java的连接。一旦连接成 功,RIL-JAVA就可发起一个请求,并等待应答,并将结构发送到目标处理对象。在RIL-Java中,这个请求称为RILRequest。为了直观起 见,我还是不厌其烦的给出RIL-Java的框架图。

Android电话系统之概述篇 - zhanghaihua415 - 水落石出的博客

 

 

RIL-Java的大框架包含了四个方面:

Receiver,Sender,CommandInterface,异步通知机制

(1) Command Interface

   在ril.java源代码中,我们可以看到RIL-JAVA对象提供了如下的Command Interface:

getlccCardStatus

getCurrrentCalls

dial

acceptCall

rejectCall

sendDTMF

sendSMS

setupDataCall

setRadioPower

为什么要定义这些接口呢?这函数接口不是凭空捏造出来的,这些都是电话的基本功能的描述,是对Modem AT指令的提炼抽象。大多数Modem都是根据通讯协议提供接口,我们如果不熟悉通讯协议,请参阅3GPP的相关文档,以及自己使用的Modem的SPEC说明。

V.25ter AT Commands

  3GPP 07.07 AT Comamnds-General commands

3GPP 07.07 AT Comamnds-Call Control commans

3GPP 07.07 AT Comamnds-Network Service related commands

3GPP 07.07 AT Comamnds-MT control and status command

3GPP 07.07 AT Comamnds-GPRS Commands

3GPP 07.07 Mobile Termination Errors

3GPP 07.05 SMS AT Commands

(2)Receiver

Android电话系统之概述篇 - zhanghaihua415 - 水落石出的博客

 

 

Android电话系统之概述篇 - zhanghaihua415 - 水落石出的博客

 

 

Receiver 连接到RILD的服务套接口,接收读取RILD传递过来的Response Parcel。Response分为两种类型,一种是URC,一种是命令应答。对于URC将会直接分发到通知注册表中的Handler。而命令应答则通过 Receiver的异步通知机制传递到命令的发送者进行相应处理。

(3)Sender

Android电话系统之概述篇 - zhanghaihua415 - 水落石出的博客

 

 

Sender应该分为两部分架构,

上层函数调用Command Interface将请求消息发送到Sender的架构。

Sender接收到EVENT_SEND消息后,将请求发送到RILD的架构。

(4)异步应答框架

    对于异步应答来讲,命令的发起者发送后,并不等待应答就返回,应答的回应是异步的,处理结果通过消息的方式返回。站在设计者的角度思考如何设计合适的框架 来完成异步通讯的功能呢?对于异步系统我们首先应该考虑的是如何标识命令和结果,让命令和结果有一个对应关系,还有命令没有响应了,如何管理命令超时?让 我们来看看Android设计者如何完成这些工作。

Android设计者利用了Result Message 和RILRequest对象来完成Request和Result的对 应对于关系。在上层做调用的时候生成Result Message对象传递到ril_java,并在Modem有应答后,通过Result Message对象带回结果。如何保证该应答是该RILRequest的呢?Android设计者还提供了一个Token(令牌)的概念。在源代码中 RILRequest的mSerail就用作了Token。Token用来唯一标识每次发送的请求,并且Token将被传递到RILD,RILD在组装应 答是将Token写入,并传回到ril-java,ril-java根据该Token找到相应的Request对象。

Android电话系统之概述篇 - zhanghaihua415 - 水落石出的博客

 

 

(4.1)RIL命令的发送模式

协议的真正实现是在rild中,RIL-JAVA更多的是一个抽象和代理,我们在研究源代码的过程中就会体会到到RIL-JAVA中的命令函数都有一个共同的框架。

SendXxxCmd(传入参数Data,传出参数result){

组合RILRequest(请求号,result,mSerail)

Data->RR

send(RILRequest): Message

}

1)RILRequest

Android电话系统之概述篇 - zhanghaihua415 - 水落石出的博客

 

请求号:

request 将传递到RILD用以标识命令,request代表某个功能。例如拨叫的request号为:RIL_REQUEST_DIAL。在 libs/telephony/ril_commands.h有定义。RILRequest.obtain@RILRequest根据命令请求号,传入参 数Result Message,mSerail构造了一个RILRequest。Result Message将带回应答信息回到命令的发起者。

mSerail:

Android使用了一个RILRequest对象池来管理Andoird RILRequest。mSerail是一个递增的变量,用来唯一标识一个RILRequest。在发送时正是用了该变量为Token,在rild层看到的token就是该mSerail。

EVENT_END:http://zhanghaihua415.blog.163.com/blog/getBlog.do?bid=fks_087067086082088067082094094068072087081069085080087065080081

Android电话系统之概述篇 - zhanghaihua415 - 水落石出的博客 Android电话系统之概述篇 - zhanghaihua415 - 水落石出的博客 

2)发送步骤:

第一步:

生成RILRequest,此时将生成m_Serial(请求的Token)并将请求号,数据,及其Result Message 对象填入到RILRequest中

第二步:

使用send将RILRequest打包到EVENT_SEND消息中发送到到RIL Sender Handler,

第三步:

RilSender 接收到EVENT_SEND消息,将RILRequest通过套接口发送到RILD,同时将RILRequest保存在mRequest中以便应答消息的返回。

(4.2) 接收模式

第一步:分析接收到的Parcel,根据类型不同进行处理。

Android电话系统之概述篇 - zhanghaihua415 - 水落石出的博客

 

第二步:根据数据中的Token(mSerail),反查mRequest,找到对应的请求信息。

第三步:将是数据转换成结果数据。

第四步:将结果放在RequestMessage中发回到请求的发起者。

4.3)详细的GSMCallTracker,RIL-Java函数对照

Android电话系统之概述篇 - zhanghaihua415 - 水落石出的博客

 

 

Android电话系统之概述篇 - zhanghaihua415 - 水落石出的博客

 

 

Android电话系统之GSMCallTracker

通话连接管理

GSMCallTracker在本质上是一个Handler。

Android电话系统之概述篇 - zhanghaihua415 - 水落石出的博客

 

GSMCallTracker是Android的通话管理层。GSMCallTracker建立了ConnectionList来管理现行的通话连接,并向上层提供电话调用接口。

Android电话系统之概述篇 - zhanghaihua415 - 水落石出的博客

 

在 GSMCallTracker中维护着通话列表:connections。顺序记录了正连接上的通话,这些通话包 括:ACTIVE,DIALING,ALERTING,HOLDING,INCOMING,WAITING等状态的连接。GSMCallTracker将 这些连接分为了三类别进行管理:

RingingCall: INCOMING ,WAITING

ForegourndCall: ACTIVE, DIALING ,ALERTING

BackgroundCall: HOLDING

上层函数通过getRingCall(),getForegrouandCall()等来获得电话系统中特定通话连接。

为了管理电话状态,GSMCallTracker在构造时就将自己登记到了电话状态变化通知表中。RIL-Java一收到电话状态变化的通知,就会使用EVENT_CALL_STATE_CHANGE通知到GSMCallTacker

    在一般的实现中,我们的通话Call Table是通过AT+CLCC查询到的,CPI可以通知到电话的改变,但是CPI在各个Modem的实现中差别比较大,所以参考设计都没有用到CPI这 样的电话连接改变通知,而是使用最为传统的CLCC查询CALL TABLE。在GSMTracker中使用connections来管理Android电话系统中的通话连接。每次电话状态发生变化是GSMTracker就会使用CLCC查询来更新connections内容,如果内容有发生变化,则向上层发起电话状态改变的通知。

1 RIL-JAVA中发起电话连接列表操作

在RIL-JAVA中涉及到CurrentCallList查询的有以下几个操作:

(1)hangup

(2)dial

(3)acceptCall

(4)rejectCall

在GSMcallTracker在发起这些调用的时候都有一个共同的ResultMessage构造函数:obtainCompleteMessage()。obtainCompleteMessage()实际上是调用:

obtainCompleteMessage(EVENT_OPERATION_COMPLETE)

这 就意味着在这些电话操作后,GSMCallTracker会收到EVENT_OPERATION_COMPLETE消息,于是我们将目光转移到 handleMessage()@GSMCallTracker的EVENT_OPERATION_COMPLETE事件处 理:operationComplete@GSMCallTracker。

operationComplete() 操作会使用cm.getCurrentCalls(lastRelevantPoll)调用,向RILD发起 RIL_REQUEST_GET_CURRENT_CALLS调用,这个最终就是向Modem发起AT+CLCC,获取到真正的电话列表。

2 在RILD中,引起getCurrentCalls调用

(1)在RILD中,收到URC消息:

+CRING

RING

NO CARRIER

+CCWA

将会使用RIL_onUnsolicitedResponse( RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED),主动向ril-java上报RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED消息。

(2) 在处理requestCurrentCalls时,使用CLCC查询通话连接(CALL TABLE)后,如何发现有call Table不为空则开启一个定时器,主动上报RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED消息,直到没有电话连接为止。

在 RIL-Java层收到RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED这个URC,并利用 mCallStateRegistrants.notifyRegistrants(new AsyncResult(null, null, null))来通知电话状态的变化,此时GSMTracker会接收到EVENT_CALL_STATE_CHANGE消息,并使用

                pollCallsWhenSafe()->  cm.getCurrentCalls(lastRelevantPoll);

来发起查询,并更新JAVA层的电话列表。

3 handlePollCalls电话列表刷新

      首先我们来看看是什么引起了handlePollCalls的调用。

     上面的1,2分析了,Android电话系统中所有引起电话连接列表更新的条件及其处理。他们共同的调用了cm.getCurrentCalls(lastRelevantPoll) 来完成电话列表的获取。

lastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT)

我们这里就从可以看到获取到的电话列表Result使用handlePollCalls进行了处理。Result实际上是一个DriverCall列表,handlePollCalls的工作就是将当前电话列表与RIL-Java的电话列表对比,使用DriverCall列表更新CallTracker的电话列表 

转载地址:https://liwangjiang.blog.csdn.net/article/details/89631987 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:Android7.0 拨号盘应用源码分析(一) 界面浅析
下一篇:mysql8.0.15安装过程中报错服务无法启动

发表评论

最新留言

路过按个爪印,很不错,赞一个!
[***.219.124.196]2024年04月26日 12时33分52秒