目錄
Android通話應(yīng)用設(shè)計(jì) 1
a) CallAudioModeStateMachine 15
b) CallAudioRouteStateMachine 17
以下內(nèi)容基于Android N code。
本文會(huì)從應(yīng)用框架魔熏、流程衷咽、設(shè)計(jì)模式幾個(gè)方面,講解Android手機(jī)中語(yǔ)音通話的應(yīng)用層設(shè)計(jì)蒜绽。
Android電話模塊是一個(gè)典型的分層結(jié)構(gòu)設(shè)計(jì)兵罢,如下:
分為應(yīng)用層、框架層(framework層滓窍,簡(jiǎn)稱fw)、RIL(Radio Interface Layer)巩那、modem吏夯。
其中:
應(yīng)用層:app應(yīng)用此蜈,包括Dialer.apk、TeleService.apk噪生、Telecom.apk裆赵、InCallUI.apk。其中其中Dialer.apk跑在com.android.dialer進(jìn)程中跺嗽,TeleService.apk跑在常駐進(jìn)程com.android.phone進(jìn)程中战授,Telecom.apk跑在system進(jìn)程中、InCallUI.apk跑在com.android.incallui進(jìn)程中桨嫁。
框架層:包括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驅(qū)動(dòng)模塊,在hardware/ril目錄下夕土,一共分rild迂曲,libril.so以及l(fā)ibrefrence_ril.so三個(gè)部分。
Modem:位于BP臊旭,負(fù)責(zé)實(shí)際的無(wú)線通信能力處理
phone進(jìn)程是常駐進(jìn)程,它的喚醒過(guò)程如下:
在TeleService app code的AndroidManifest.xml中箩退,application的屬性配置了android:persistent=”true”和android:directBootAware=”true”离熏。系統(tǒng)開機(jī)時(shí),如果沒(méi)有解鎖戴涝,SystemServer會(huì)喚醒所有在AndroidManifest中配置了persistent=“true”且設(shè)置了directBootAware=”true”的應(yīng)用滋戳,解鎖后再喚醒persistent為true但是unaware的應(yīng)用。
在TeleService app起來(lái)后會(huì)把PhoneInterfaceManager這個(gè)phone service加入到ServiceManager中啥刻。應(yīng)用可以通過(guò)TelephonyManager.java中封裝好的接口調(diào)用里面的方法奸鸯。
telecom app模塊由于跑在system進(jìn)程中,也會(huì)一開機(jī)就被喚醒可帽。各個(gè)應(yīng)用可以調(diào)用的TelecomManager的server端TelecomService也會(huì)在同一時(shí)間加入ServiceManager的services里娄涩。過(guò)程如下:
其時(shí)序圖如下:
ContextImpl會(huì)保存一份system services的緩存,應(yīng)用可以通過(guò)Context#getSystemService(name)來(lái)獲取這些system service如TelecomService等,其具體添加和獲取過(guò)程如下圖:
通話過(guò)程是一個(gè)多進(jìn)程交互的過(guò)程蓄拣,主要進(jìn)程有dialer進(jìn)程扬虚、phone進(jìn)程、telecom進(jìn)程(system)球恤、incallui進(jìn)程辜昵、audio進(jìn)程。其中
dialer進(jìn)程:負(fù)責(zé)撥號(hào)咽斧。
phone進(jìn)程:負(fù)責(zé)通話邏輯堪置,如實(shí)際向RIL撥號(hào)、掛電話张惹,及電話狀態(tài)如撥號(hào)舀锨、振鈴、接通等的變更诵叁。
telecom進(jìn)程(system進(jìn)程雁竞,本文為了描述稱為telecom進(jìn)程):負(fù)責(zé)邏輯控制,是溝通各個(gè)進(jìn)程交互的橋梁拧额。
incallui進(jìn)程:負(fù)責(zé)通話界面顯示碑诉。
audio進(jìn)程:負(fù)責(zé)通話聲音的傳輸。
上面五個(gè)進(jìn)程間通信都是通過(guò)Binder侥锦,只有phone進(jìn)程和RIL間的交互是通過(guò)socket进栽,如下圖:
各進(jìn)程間主要通過(guò)aidl進(jìn)行交互,其中telecom進(jìn)程維護(hù)向系統(tǒng)注冊(cè)了的TelecomService恭垦,audio進(jìn)程維護(hù)了向系統(tǒng)注冊(cè)了的AudioService快毛,其它進(jìn)程可以通過(guò)TelecomManager.java和AudioManager.java封裝好的接口和它們交互。
下圖描述了實(shí)現(xiàn)各進(jìn)程交互的aidl:
而它們實(shí)際的交互過(guò)程如下圖:
可以看到:
撥號(hào)和來(lái)電都是dialer和phone進(jìn)程調(diào)用TelecomManager的方法最后由位于telecom進(jìn)程的TelecomService來(lái)實(shí)現(xiàn)番挺。
Telecom進(jìn)程分別通過(guò)IConnectionService.aidl和IInCallService.aidl來(lái)控制phone進(jìn)程和Incallui進(jìn)程唠帝。而phone進(jìn)程和Incallui進(jìn)程通過(guò)IConnectionServiceAdapter.aidl和IInCallAdapter.aidl來(lái)通知telecom進(jìn)程需要變更。
因?yàn)槭欠謱咏Y(jié)構(gòu)玄柏,來(lái)電襟衰、掛斷是從上到下,而來(lái)電和電話狀態(tài)的變化則是從下到上粪摘。如下圖:
用戶的操作是通過(guò)APP->FW->RIL->Modem向下傳瀑晒,而網(wǎng)絡(luò)消息是通過(guò)Modem->RIL->FW->APP向上傳。除了分層結(jié)構(gòu)外比較主要就是進(jìn)程間交互徘意,上面有討論苔悦,下面將不再重復(fù)。
撥號(hào)是從上往下椎咧,即從APP到FW到RIL再到Modem玖详。
如下圖:
撥號(hào)盤通過(guò)TelecomManager的接口撥號(hào),telecom一邊向phone進(jìn)程請(qǐng)求撥號(hào),一邊通知incallui顯示撥號(hào)界面竹宋。
電話狀態(tài)更新從結(jié)構(gòu)上看是從下往上劳澄,通過(guò)一層層的監(jiān)聽和通知通過(guò)觀察者模式從Modem通知到RIL到FW到APP。
如下圖:
開機(jī)時(shí)蜈七,Phone進(jìn)程起來(lái),PhoneFactory會(huì)構(gòu)建Phone和其對(duì)應(yīng)的RILJ莫矗,Phone會(huì)構(gòu)建其對(duì)應(yīng)的CallTracker飒硅,CallTracker會(huì)向RILJ注冊(cè)call狀態(tài)變更的監(jiān)聽,RILJ通過(guò)Socket向RILC注冊(cè)監(jiān)聽作谚。撥號(hào)成功或者來(lái)電后Telephony會(huì)構(gòu)建一個(gè)封裝了telephony fw Connection的TeleponyConnection三娩,它在構(gòu)建時(shí)會(huì)向Phone注冊(cè)Call狀態(tài)變更,所以只要網(wǎng)絡(luò)端返回狀態(tài)更新就可以從底層一直通知到應(yīng)用層的phone進(jìn)程妹懒,再通過(guò)AIDL跨進(jìn)程更新到telecom進(jìn)程雀监,telecom再通知incallui更新界面。
掛斷電話眨唬,從用戶在通話界面點(diǎn)擊“掛斷”按鈕開始也是一個(gè)自上而下的過(guò)程:
來(lái)電和狀態(tài)更新相比的不同點(diǎn)就是多了注冊(cè)來(lái)電消息接收的過(guò)程会前,注冊(cè)監(jiān)聽后通過(guò)觀察者模式從下而上通知到應(yīng)用來(lái)電。下面這張圖就是描述注冊(cè)來(lái)電消息的流程:
開機(jī)后phone進(jìn)程起來(lái)會(huì)根據(jù)手機(jī)的待機(jī)模式構(gòu)建Phone匾竿,每個(gè)Phone會(huì)創(chuàng)建一個(gè)處理關(guān)于語(yǔ)音通話的對(duì)象GsmCdmaCallTracker瓦宜,CallTracker會(huì)向RILJ注冊(cè)通話狀態(tài)變化的監(jiān)聽,RILJ會(huì)通用Socket向RIL層注冊(cè)監(jiān)聽岭妖。而當(dāng)Telephony APP監(jiān)聽到sim卡可用時(shí)會(huì)向該卡的Phone注冊(cè)來(lái)電監(jiān)聽临庇。這樣Modem收到來(lái)電消息傳到RIL到RILJ后,RILJ會(huì)通知CallTracker昵慌,CallTracker再通知監(jiān)聽它的Phone的監(jiān)聽者假夺。
Android源碼中運(yùn)用了很多設(shè)計(jì)模式使整個(gè)系統(tǒng)設(shè)計(jì)很良好也很美觀,如上面介紹流程時(shí)頻繁涉及到的觀察者模式斋攀。下面介紹幾種通話模塊中運(yùn)用比較典型的設(shè)計(jì)模式已卷。
Call狀態(tài)的各種變更都是通過(guò)觀察者模式來(lái)實(shí)現(xiàn),而telephony fw通過(guò)RIL向modem下發(fā)撥號(hào)接聽等命令是通過(guò)命令模式來(lái)實(shí)現(xiàn)蜻韭,下面介紹一下這兩種模式的復(fù)合使用悼尾。
先看圖:
其中把一個(gè)Phone的所有操作,如撥號(hào)肖方、接聽等封裝成一個(gè)Command闺魏,通過(guò)CallTracker這個(gè)Invoker去調(diào)用Command中的方法執(zhí)行如dial()、accept()俯画。Command執(zhí)行的結(jié)果返回給Receiver RegistrantList析桥。RegistrantList再通知向它注冊(cè)過(guò)的接收者(RegistrantList是Android封裝好的觀察者模式Subject的實(shí)現(xiàn),具體就不介紹了)。
Incallui通話界面的實(shí)現(xiàn)是典型的MVP模式泡仗,先看其類結(jié)構(gòu)圖埋虹,如下:
這個(gè)實(shí)現(xiàn)中疊用了兩層MVP設(shè)計(jì):
外面的一層:InCallPresenter為P,CallList為M娩怎,里面的一層結(jié)構(gòu)MVP做為V搔课。當(dāng)CallList有數(shù)據(jù)發(fā)生變化時(shí)通知InCallPresenter,InCallPresenter再通知View去更新截亦。
里面一層:P根據(jù)顯示內(nèi)容的不同分為CallCardPresenter爬泥、CallButtonPresenter、AnswerPresenter崩瓤、DialpadPresenter袍啡、VideoCallPresenter五部分,它們分別對(duì)應(yīng)的view為xxxFragment却桶,而M部分則是Call和TelecomAdapter境输,實(shí)際上是telecom fw的Call,上圖為了簡(jiǎn)單只畫了Call颖系。當(dāng)Call有更新時(shí)通知xxxPresenter更新View xxxFragment嗅剖,當(dāng)xxxFragment有用戶操作時(shí)通過(guò)其對(duì)應(yīng)的xxxPresenter更新Call。
下面簡(jiǎn)述第二層MVP模式中每一個(gè)MVP的功能:
CallCardPresenter和CallCardFragment:控制聯(lián)系人信息的顯示集晚,如聯(lián)系人頭像窗悯、姓名、電話號(hào)碼等偷拔。
CallButtonPresenter和CallButtonFragment:控制通話界面用戶可以點(diǎn)擊的功能button的顯示和交互蒋院,如靜音、揚(yáng)聲器莲绰、dtmf盤等欺旧。
AnswerPresenter和AnsewrFragment:控制來(lái)電動(dòng)畫和操作的顯示。
DialpadPresenter和DialpadFragment:控制dtmf內(nèi)容的顯示和用戶點(diǎn)擊數(shù)字的操作蛤签。
VideoCallPresenter和VideoCallFragment:控制視頻通話界面本機(jī)圖像窗口和對(duì)端圖像窗口的顯示辞友。
Telecom app中控制音頻和audio交互的過(guò)程是通過(guò)狀態(tài)機(jī)StateMachine來(lái)實(shí)現(xiàn)的,分為CallAudioModeStateMacine和CallAudioRouteStateMachine震肮。其中:
CallAudioModeStateMacine:主要功能是向AudioManager發(fā)送setMode請(qǐng)求称龙,即設(shè)置聲音模式為normal還是ringtone還是incall。語(yǔ)音通話中只有設(shè)置了聲音模式為incall后戳晌,通話中才能聽到對(duì)方的聲音鲫尊,本機(jī)的聲音也才能傳輸?shù)綄?duì)端。網(wǎng)絡(luò)通話則設(shè)置聲音模式為communication沦偎。
CallAudioRouteStateMachine:主要功能是控制通話過(guò)程中對(duì)端聲音在本機(jī)的播放方式疫向,如聽筒咳蔚、耳機(jī)、揚(yáng)聲器搔驼、藍(lán)牙等谈火。
先看CallAudioModeStateMachine的狀態(tài)圖,一張圖表示一個(gè)狀態(tài)對(duì)外的變更舌涨。
可以看到這個(gè)狀態(tài)機(jī)共有5總狀態(tài)糯耍,分別是UnFocusedState、RingingFocusState囊嘉、SimCallFocusState谍肤、OtherFocusState、SimCallFocusState哗伯,初始狀態(tài)為UnFocusedState。
Entry:表示進(jìn)入這個(gè)狀態(tài)會(huì)做什么操作篷角,比如進(jìn)入SimCallFocusState會(huì)調(diào)用AudioManager.setMode(MODE_IN_CALL)焊刹,而進(jìn)入U(xiǎn)nFocusedState會(huì)調(diào)用AudioManager.setMode(MODE_NORMAL)。其中進(jìn)入每個(gè)狀態(tài)都會(huì)去setCallAudioRouteFocusState(xxx)恳蹲,這個(gè)是觸發(fā)CallAudioRouteStateMachine狀態(tài)機(jī)狀態(tài)變更的一個(gè)重要條件虐块,下面會(huì)講到。
Exit:表示退出這個(gè)狀態(tài)時(shí)會(huì)做什么操作嘉蕾,如退出RingingFocusState會(huì)停止響鈴贺奠。
箭頭:表示從一個(gè)狀態(tài)切到另外一個(gè)狀態(tài)的觸發(fā)條件,比如手機(jī)待機(jī)狀態(tài)處于UnFocusState错忱,此時(shí)撥號(hào)就會(huì)進(jìn)入SimCallFocusState儡率,電話結(jié)束播放掛斷鈴聲時(shí)會(huì)進(jìn)入OtherFocusState,掛斷鈴聲播放后又沒(méi)有任何通話就會(huì)回到UnFocusedState以清。
狀態(tài)圖如下:
CallAudioRouteStateMachine總共有9種狀態(tài)儿普,分成4類:EarpieceRoute、HeadsetRoute掷倔、SpeakerRouter眉孩、BluetoothRoute。每類Route都有quiescent和active兩種狀態(tài)勒葱,BluetoothRoute多了ringing的狀態(tài)浪汪。初始狀態(tài)都是quiescent route,具體類別根據(jù)當(dāng)前手機(jī)所連設(shè)備來(lái)確定凛虽。如果當(dāng)前手機(jī)藍(lán)牙可用死遭,初始狀態(tài)為QuiescentBluetoothRoute,如果當(dāng)前手機(jī)藍(lán)牙不可用但有耳機(jī)插入則為QuiescentHeadsetRoute涩维,否則為QuiescentEarpieceRoute殃姓。當(dāng)quiescent狀態(tài)收到CallAudioModeStateMachine發(fā)送過(guò)來(lái)的ACTIVE_FOCUS或RINGING_FOCUS時(shí)轉(zhuǎn)換到其對(duì)應(yīng)的active狀態(tài)或ringing狀態(tài)袁波,具體看上面的狀態(tài)圖。進(jìn)入active route時(shí)會(huì)根據(jù)當(dāng)前的狀態(tài)來(lái)處理speaker bluetooth的開關(guān)和前前的audio state蜗侈。比如進(jìn)入ActiveEarpieceRoute時(shí)會(huì)關(guān)掉speaker和bluetooth篷牌,audio state切為ROUTE_EARPIECE,進(jìn)入ActiveSpeakerRoute時(shí)會(huì)打開speaker關(guān)掉bluetooth踏幻,audio state切為ROUTE_SPEAKER枷颊。
原創(chuàng)內(nèi)容歡迎轉(zhuǎn)載,但請(qǐng)注明出處该面,謝謝夭苗!