Android通话应用设计

目录

Android通话应用设计 1

一、 背景 1

二、 应用框架设计 1

三、 常驻进程开机唤醒 2

1. Phone进程 2

2. TelecomServer进程 3

四、 应用间进程交互 6

1. 进程交互方式 6

2. Binder交互过程 6

五、 通话流程 7

1. 拨号流程 8

2. 电话状态更新流程 9

3. 用户挂断电话流程 10

4. 来电流程 11

六、 主要设计模式 12

1. 命令模式+观察者模式的复合使用 13

2. MVP模式 13

3. 状态机 14

a) CallAudioModeStateMachine 15

b) CallAudioRouteStateMachine 17

背景

以下内容基于Android N code。

本文会从应用框架、流程、设计模式几个方面,讲解Android手机中语音通话的应用层设计。

应用框架设计

Android电话模块是一个典型的分层结构设计,如下:

分为应用层、框架层(framework层,简称fw)、RIL(Radio Interface Layer)、modem。

其中:

应用层:app应用,包括Dialer.apk、TeleService.apk、Telecom.apk、InCallUI.apk。其中其中Dialer.apk跑在com.android.dialer进程中,TeleService.apk跑在常驻进程com.android.phone进程中,Telecom.apk跑在system进程中、InCallUI.apk跑在com.android.incallui进程中。

框架层:包括telephony fw、telecom fw。Code分别位于frameworks/opt/telephony、frameworks/base/telecomm。

RIL:位于User Libraries层中的HAL层,提供AP(Application Processor)和BP(Baseband Processor)之间的通信功能。RIL通常分为RILJ、RILC,RILJ即为java的RIL.java,code位于框架层,RILC才是真正的RIL层。Android的RIL驱动模块,在hardware/ril目录下,一共分rild,libril.so以及librefrence_ril.so三个部分。

Modem:位于BP,负责实际的无线通信能力处理

常驻进程开机唤醒

Phone进程

phone进程是常驻进程,它的唤醒过程如下:

在TeleService app code的AndroidManifest.xml中,application的属性配置了android:persistent=”true”和android:directBootAware=”true”。系统开机时,如果没有解锁,SystemServer会唤醒所有在AndroidManifest中配置了persistent=“true”且设置了directBootAware=”true”的应用,解锁后再唤醒persistent为true但是unaware的应用。

在TeleService app起来后会把PhoneInterfaceManager这个phone service加入到ServiceManager中。应用可以通过TelephonyManager.java中封装好的接口调用里面的方法。

TelecomServer进程

telecom app模块由于跑在system进程中,也会一开机就被唤醒。各个应用可以调用的TelecomManager的server端TelecomService也会在同一时间加入ServiceManager的services里。过程如下:

其时序图如下:

ContextImpl会保存一份system services的缓存,应用可以通过Context#getSystemService(name)来获取这些system service如TelecomService等,其具体添加和获取过程如下图:

应用间进程交互

进程交互方式

通话过程是一个多进程交互的过程,主要进程有dialer进程、phone进程、telecom进程(system)、incallui进程、audio进程。其中

dialer进程:负责拨号。

phone进程:负责通话逻辑,如实际向RIL拨号、挂电话,及电话状态如拨号、振铃、接通等的变更。

telecom进程(system进程,本文为了描述称为telecom进程):负责逻辑控制,是沟通各个进程交互的桥梁。

incallui进程:负责通话界面显示。

audio进程:负责通话声音的传输。

上面五个进程间通信都是通过Binder,只有phone进程和RIL间的交互是通过socket,如下图:

Binder交互过程

各进程间主要通过aidl进行交互,其中telecom进程维护向系统注册了的TelecomService,audio进程维护了向系统注册了的AudioService,其它进程可以通过TelecomManager.java和AudioManager.java封装好的接口和它们交互。

下图描述了实现各进程交互的aidl:

而它们实际的交互过程如下图:

可以看到:

拨号和来电都是dialer和phone进程调用TelecomManager的方法最后由位于telecom进程的TelecomService来实现。

Telecom进程分别通过IConnectionService.aidl和IInCallService.aidl来控制phone进程和Incallui进程。而phone进程和Incallui进程通过IConnectionServiceAdapter.aidl和IInCallAdapter.aidl来通知telecom进程需要变更。

通话流程

因为是分层结构,来电、挂断是从上到下,而来电和电话状态的变化则是从下到上。如下图:

用户的操作是通过APP->FW->RIL->Modem向下传,而网络消息是通过Modem->RIL->FW->APP向上传。除了分层结构外比较主要就是进程间交互,上面有讨论,下面将不再重复。

拨号流程

拨号是从上往下,即从APP到FW到RIL再到Modem。

如下图:

拨号盘通过TelecomManager的接口拨号,telecom一边向phone进程请求拨号,一边通知incallui显示拨号界面。

电话状态更新流程

电话状态更新从结构上看是从下往上,通过一层层的监听和通知通过观察者模式从Modem通知到RIL到FW到APP。

如下图:

开机时,Phone进程起来,PhoneFactory会构建Phone和其对应的RILJ,Phone会构建其对应的CallTracker,CallTracker会向RILJ注册call状态变更的监听,RILJ通过Socket向RILC注册监听。拨号成功或者来电后Telephony会构建一个封装了telephony fw Connection的TeleponyConnection,它在构建时会向Phone注册Call状态变更,所以只要网络端返回状态更新就可以从底层一直通知到应用层的phone进程,再通过AIDL跨进程更新到telecom进程,telecom再通知incallui更新界面。

用户挂断电话流程

挂断电话,从用户在通话界面点击“挂断”按钮开始也是一个自上而下的过程:

来电流程

来电和状态更新相比的不同点就是多了注册来电消息接收的过程,注册监听后通过观察者模式从下而上通知到应用来电。下面这张图就是描述注册来电消息的流程:

开机后phone进程起来会根据手机的待机模式构建Phone,每个Phone会创建一个处理关于语音通话的对象GsmCdmaCallTracker,CallTracker会向RILJ注册通话状态变化的监听,RILJ会通用Socket向RIL层注册监听。而当Telephony APP监听到sim卡可用时会向该卡的Phone注册来电监听。这样Modem收到来电消息传到RIL到RILJ后,RILJ会通知CallTracker,CallTracker再通知监听它的Phone的监听者。

主要设计模式

Android源码中运用了很多设计模式使整个系统设计很良好也很美观,如上面介绍流程时频繁涉及到的观察者模式。下面介绍几种通话模块中运用比较典型的设计模式。

命令模式+观察者模式的复合使用

Call状态的各种变更都是通过观察者模式来实现,而telephony fw通过RIL向modem下发拨号接听等命令是通过命令模式来实现,下面介绍一下这两种模式的复合使用。

先看图:

其中把一个Phone的所有操作,如拨号、接听等封装成一个Command,通过CallTracker这个Invoker去调用Command中的方法执行如dial()、accept()。Command执行的结果返回给Receiver RegistrantList。RegistrantList再通知向它注册过的接收者(RegistrantList是Android封装好的观察者模式Subject的实现,具体就不介绍了)。

MVP模式

Incallui通话界面的实现是典型的MVP模式,先看其类结构图,如下:

这个实现中叠用了两层MVP设计:

外面的一层:InCallPresenter为P,CallList为M,里面的一层结构MVP做为V。当CallList有数据发生变化时通知InCallPresenter,InCallPresenter再通知View去更新。

里面一层:P根据显示内容的不同分为CallCardPresenter、CallButtonPresenter、AnswerPresenter、DialpadPresenter、VideoCallPresenter五部分,它们分别对应的view为xxxFragment,而M部分则是Call和TelecomAdapter,实际上是telecom fw的Call,上图为了简单只画了Call。当Call有更新时通知xxxPresenter更新View xxxFragment,当xxxFragment有用户操作时通过其对应的xxxPresenter更新Call。

下面简述第二层MVP模式中每一个MVP的功能:

CallCardPresenter和CallCardFragment:控制联系人信息的显示,如联系人头像、姓名、电话号码等。

CallButtonPresenter和CallButtonFragment:控制通话界面用户可以点击的功能button的显示和交互,如静音、扬声器、dtmf盘等。

AnswerPresenter和AnsewrFragment:控制来电动画和操作的显示。

DialpadPresenter和DialpadFragment:控制dtmf内容的显示和用户点击数字的操作。

VideoCallPresenter和VideoCallFragment:控制视频通话界面本机图像窗口和对端图像窗口的显示。

状态机

Telecom app中控制音频和audio交互的过程是通过状态机StateMachine来实现的,分为CallAudioModeStateMacine和CallAudioRouteStateMachine。其中:

CallAudioModeStateMacine:主要功能是向AudioManager发送setMode请求,即设置声音模式为normal还是ringtone还是incall。语音通话中只有设置了声音模式为incall后,通话中才能听到对方的声音,本机的声音也才能传输到对端。网络通话则设置声音模式为communication。

CallAudioRouteStateMachine:主要功能是控制通话过程中对端声音在本机的播放方式,如听筒、耳机、扬声器、蓝牙等。

CallAudioModeStateMachine

先看CallAudioModeStateMachine的状态图,一张图表示一个状态对外的变更。

可以看到这个状态机共有5总状态,分别是UnFocusedState、RingingFocusState、SimCallFocusState、OtherFocusState、SimCallFocusState,初始状态为UnFocusedState。

Entry表示进入这个状态会做什么操作,比如进入SimCallFocusState会调用AudioManager.setMode(MODE_IN_CALL),而进入UnFocusedState会调用AudioManager.setMode(MODE_NORMAL)。其中进入每个状态都会去setCallAudioRouteFocusState(xxx),这个是触发CallAudioRouteStateMachine状态机状态变更的一个重要条件,下面会讲到。

Exit表示退出这个状态时会做什么操作,如退出RingingFocusState会停止响铃。

箭头:表示从一个状态切到另外一个状态的触发条件,比如手机待机状态处于UnFocusState,此时拨号就会进入SimCallFocusState,电话结束播放挂断铃声时会进入OtherFocusState,挂断铃声播放后又没有任何通话就会回到UnFocusedState。

CallAudioRouteStateMachine

状态图如下:

CallAudioRouteStateMachine总共有9种状态,分成4类:EarpieceRoute、HeadsetRoute、SpeakerRouter、BluetoothRoute。每类Route都有quiescent和active两种状态,BluetoothRoute多了ringing的状态。初始状态都是quiescent route,具体类别根据当前手机所连设备来确定。如果当前手机蓝牙可用,初始状态为QuiescentBluetoothRoute,如果当前手机蓝牙不可用但有耳机插入则为QuiescentHeadsetRoute,否则为QuiescentEarpieceRoute。当quiescent状态收到CallAudioModeStateMachine发送过来的ACTIVE_FOCUS或RINGING_FOCUS时转换到其对应的active状态或ringing状态,具体看上面的状态图。进入active route时会根据当前的状态来处理speaker bluetooth的开关和前前的audio state。比如进入ActiveEarpieceRoute时会关掉speaker和bluetooth,audio state切为ROUTE_EARPIECE,进入ActiveSpeakerRoute时会打开speaker关掉bluetooth,audio state切为ROUTE_SPEAKER。


原创内容欢迎转载,但请注明出处,谢谢!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,706评论 4 366
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,002评论 1 301
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,462评论 0 250
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,375评论 0 216
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,763评论 3 294
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,849评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,033评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,768评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,490评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,734评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,204评论 1 264
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,566评论 3 260
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,227评论 3 241
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,137评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,934评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,926评论 2 283
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,774评论 2 274

推荐阅读更多精彩内容