Android源碼分析:Telephony部分–GSMPhone

來源:互聯網
上載者:User

標籤:android   blog   http   java   使用   strong   

Android源碼分析:Telephony部分–GSMPhone
紅狼部落格

PhoneProxy/GSMPhone/CDMAPhone

如果說RILJ提供了工具或管道,那麼Phone介面的子類及PhoneFactory則為packages/app/Phone這個應用程式進程使用RILJ這個工具或管道提供了極大的方便,它們一個管理整個整個手機的Telephony功能。

GSMPhone和CDMAPhone實現了Phone中定義的介面。介面類Phone定義了一套API,這套API用於使用RILJ(見後述RIL類)發送AT命令請求,也還有一套register和unregister函數;當調用者對一些內部狀態感興趣時,可以調用對應的register函數,當狀態變化時可以得到及時通知。

PhoneBase實現了Phone介面中定義的部分函數,還有一部分由其子類GSMPhone和CDMAPhone實現。PhoneProxy是GSMPhone和CDMAPhone的代理,讓使用者不用關注手機到底是GSM還是CDMA,它遵守Phone定義的API介面,因此繼承Phone。

PhoneFactory在建立Phone對象時,擁有的是PhoneProxy對象,PhoneProxy根據實際的網路類型建立對應的GSMPhone或CDMAPhone。

PhoneFactory同樣擁有CommandInterface的介面對象,即RIL的執行個體,該RIL執行個體將被傳遞給GSMPhone或CDMAPhone,即GSMPhone或CDMAPhone引用它,實現與rild的互動。

GSMPhone和CDMAPhone繼承自PhoneBase,它們(包括PhoneProxy)都是一個Handler。這樣,它們就可以線上程的迴圈Looper.loop中處理來自RILJ或自身發送的各種Message(只要Message中指定了目的handler)。

PhoneFactory負責建立各個Phone執行個體,其成員及成員函數為static,可以保證建立的執行個體在系統運行時的唯一性。

 

 

是PhoneFactory擁有著PhoneProxy對象(實際代理的GSMPhone或CDMAPhone的功能)、RIL對象、PhoneNotofier對象和Looper對象,Context對象由上層的應用程式Java App傳遞過來,即某個Activity 。Android的應用程式可以使用PhoneFactory.getDefaultPhone來獲得Phone對象,從而進行一些叫用作業。

 

在PhoneFactory建立某種Phone時,建立的PhoneNotifier和RIL等執行個體的程式碼片段如下:

sPhoneNotifier = new DefaultPhoneNotifier();

//擷取網路類型
//Get preferredNetworkMode from Settings.System
int networkMode = Settings.Secure.getInt(context.getContentResolver(),
Settings.Secure.PREFERRED_NETWORK_MODE, preferredNetworkMode);

Log.i(LOG_TAG, “Network Mode set to ” + Integer.toString(networkMode));

//Get preferredNetworkMode from Settings.System
int cdmaSubscription = Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.PREFERRED_CDMA_SUBSCRIPTION, preferredCdmaSubscription);

Log.i(LOG_TAG, “Cdma Subscription set to ” + Integer.toString(cdmaSubscription));

//reads the system properties and makes commandsinterface
sCommandsInterface = new RIL(context, networkMode, cdmaSubscription);//建立RIL對象

//根據網路類型擷取手機Phone類型GSM還是CDMA
int phoneType = getPhoneType(networkMode);

//PhoneProxy對象,它根據手機類型代理不同的Phone的對象
if (phoneType == RILConstants.GSM_PHONE) {
sProxyPhone = new PhoneProxy(new GSMPhone(context,
sCommandsInterface, sPhoneNotifier));
Log.i(LOG_TAG, “Creating GSMPhone”);
} else if (phoneType == RILConstants.CDMA_PHONE) {
sProxyPhone = new PhoneProxy(new CDMAPhone(context,
sCommandsInterface, sPhoneNotifier));
Log.i(LOG_TAG, “Creating CDMAPhone”);
}

在GSMPhone和CDMAPhone中,建立與電話功能相關的各個模組,下面的程式碼片段來自GSMPhone的建構函式:

前面的部分代碼用於建立各種對象,比如GsmCallTracker 對象mCT ;後面的register函數用於注 冊GSMPhone對某種狀態感興趣,當狀態變化時會得到通知,由handler(即自身this)去處理,GSMPPhone本身就是一個handler,可以處理這些狀態變化時發送過來的Message。

mCM.setPhoneType(Phone.PHONE_TYPE_GSM);

mCT = new GsmCallTracker(this);
mSST = new GsmServiceStateTracker (this);
mSMS = new GsmSMSDispatcher(this);
mIccFileHandler = new SIMFileHandler(this);
mSIMRecords = new SIMRecords(this);
mDataConnection = new GsmDataConnectionTracker (this);
mSimCard = new SimCard(this);
if (!unitTestMode) {
mSimPhoneBookIntManager = new
SimPhoneBookInterfaceManager(this);
mSimSmsIntManager = new SimSmsInterfaceManager(this);
mSubInfo = new PhoneSubInfo(this);
}
mStkService = StkService.getInstance(mCM, mSIMRecords, mContext,
(SIMFileHandler)mIccFileHandler, mSimCard);

mCM.registerForAvailable(this, EVENT_RADIO_AVAILABLE, null);
mSIMRecords.registerForRecordsLoaded(this,
EVENT_SIM_RECORDS_LOADED, null);
mCM.registerForOffOrNotAvailable(this,
EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
mCM.registerForOn(this, EVENT_RADIO_ON, null);
mCM.setOnUSSD(this, EVENT_USSD, null);
mCM.setOnSuppServiceNotification(this, EVENT_SSN, null);
mSST.registerForNetworkAttach(this, EVENT_REGISTERED_TO_NETWORK, null);

 

揭示了GSMPhone和CDMAPhone擁有的類執行個體,以及這些執行個體的類的繼承關係。圖的上半部分幾個類本身都是handler,因此可以處理各種線程隊列上的Message,見它們的handlerMessage函數。下半部分的幾個類繼承自XXX.stub,它是服務端的具體實現。調用者如Java APP(用戶端)通過AIDL介面最終會調用到它們實現的函數。

 

 程式碼分析樣本–GSMCallTracker

GSMCallTracker實現了電話的撥打(Dial)、接聽/拒絕(accept/reject)、掛斷(hangup)、保持(hold)、切換以及電話會議等功能,它還負責查詢Modem當前有多少路通話,維護電話狀態等功能。

GSMCallTracker中包含了GsmConnection、RegistrantList、 GSMCall和Phone.State等類的對象執行個體。

GSMCall包含可以支援多路通話,每路通話意味著一個通話串連GsmConnection(最多5個)當它們狀態均為DISCONNECTED時意外著該GSMCall為IDLE狀態。

在GSMCallTracker建構函式中向RIL類執行個體註冊了RegistrantList,當通話狀態及射頻Radio狀態變化時,就會通知GSMCallTracker:

GsmCallTracker (GSMPhone phone) {
this.phone = phone;
cm = phone.mCM;

cm.registerForCallStateChanged(this, EVENT_CALL_STATE_CHANGE, null);
cm.registerForOn(this, EVENT_RADIO_AVAILABLE, null);
cm.registerForNotAvailable(this, EVENT_RADIO_NOT_AVAILABLE, null);

而GSMCallTracker的handleMessage就會做出相應處理:

case EVENT_CALL_STATE_CHANGE:
pollCallsWhenSafe();
break;
case EVENT_RADIO_AVAILABLE:
handleRadioAvailable();
break;
case EVENT_RADIO_NOT_AVAILABLE:
handleRadioNotAvailable();

它們最終都調用的是pollCallsWhenSafe去查詢當前的通話狀態。

dial:撥打到電話。它首先clearDisconnected()和canDial()清空過去的非串連狀態的Connections,然後檢查是否可以撥打到電話。接著檢查foregroundCall是否處於Active狀態,若是則調用switchWaitingOrHoldingAndActive將它們切換到後台,調用fakeHoldForegroundBeforeDial將前台中的串連全部清空到後台,並且狀態變為HOLDING。在進行這些前期檢查和準備後,建立一個GsmConnection執行個體即pendingMO,檢查傳遞過來的電話號碼是否有效合法,若不合法則調用pollCallsWhenSafe(),目的是將其標為dropped;若合法則設定為非靜音後,調用RIL. dial進行撥號。最後,更新Phone狀態並通知給註冊者。

acceptCall:電話中。若ringingCall正處於INCOMING則調用RIL.acceptCall去電話中;若是WAITING狀態,則調用switchWaitingOrHoldingAndActive將其切換到前台。

rejectCall:拒接電話。當ringingCall處於INCOMING時,則調用RIL.rejectCall拒絕;否則拋出異常,表示沒有來電卻去接聽它。

hangup:掛斷某個GSMCall的電話。它區分是ringingCall、foregroundCall還是backgroundCall。若是ringingCall,則調用RIL.hangupWaitingOrBackground;若是foregroundCall,並且是在DIALING或ALERTING狀態則調用調用hangup (GsmConnection conn)掛斷,否則調用hangupForegroundResumeBackground掛斷前台通話後恢複後台通話;若是backgroundCall且ringingCall在響鈴,則調用hangupAllConnections掛斷所有在backgroundCall的通話串連,否則調用hangupWaitingOrBackground掛斷插撥和後台通話。

當上面這些功能API函數完成後,均由下面的case分支處理:

case EVENT_OPERATION_COMPLETE:
ar = (AsyncResult)msg.obj;
operationComplete();
break;

explicitCallTransfer:調用RIL.explicitCallTransfer進行交換,AT執行完成後返回的Message由handlerMessage中的case分支EVENT_ECT_RESULT進行處理。

switchWaitingOrHoldingAndActive():調用RIL.witchWaitingOrHoldingAndActive進行切換,AT執行完成後返回的Message由handlerMessage中的case分支EVENT_SWITCH_RESULT進行處理。

conference():調用RIL.conference進行電話會議,AT執行完成後返回的Message由handlerMessage中的case分支EVENT_CONFERENCE_RESULT進行處理。

separate:調用RIL.separateConnection分離出一路通話,AT執行完成後返回的Message由handlerMessage中的case分支EVENT_SEPARATE_RESULT進行處理。

對它們執行結果的處理如下:

case EVENT_SWITCH_RESULT:
case EVENT_CONFERENCE_RESULT:
case EVENT_SEPARATE_RESULT:
case EVENT_ECT_RESULT:
ar = (AsyncResult)msg.obj;
if (ar.exception != null){
phone.notifySuppServiceFailed(getFailedService(msg.what));
}
operationComplete();
break;

在未發生異常的情況下,會調用operationComplete()檢查是否是最後一個操作,若是則發送EVENT_POLL_CALLS_RESULT類型訊息,進而繼續由handlerMessage處理:

case EVENT_POLL_CALLS_RESULT:
ar = (AsyncResult)msg.obj;//擷取回送的結果
if (msg == lastRelevantPoll) {
if (DBG_POLL) log(
“handle EVENT_POLL_CALL_RESULT: set needsPoll=F”);
needsPoll = false;
lastRelevantPoll = null;
handlePollCalls((AsyncResult)msg.obj);//處理結果
}
break;

handlePollCalls處理查詢結果,將回送的Modem中的通話列表逐個解析出來。

其它函數解釋:

fakeHoldForegroundBeforeDial()將前台電話(ForegroundCall)中的電話串連(GSMConnections)clone後台後,將這些串連刪除,將Foreground置為IDLE狀態,將後台電話BackgroundCall置為HOLDING狀態。

clearDisconnected():清除已Disconnected的電話串連並更新電話狀態,然後通知註冊者當前最新的狀態。

internalClearDisconnected():將ringingCall、 foregroundCall和 backgroundCall中狀態為DISCONNECTED的 connections清除掉,若沒有connection則將該GSMCall置為idle狀態。

updatePhoneState():更新Phone狀態,並向註冊者通知語音通話開始或結束。

canDial():檢查是否可以撥打到電話,只有同時滿足下列條件才可以:(1)射頻Raido未關閉(2)PendingMO這個Connection為空白(3)RingCall這個GSMCall未處於響鈴狀態(4)系統沒有禁止電話撥打功能(5)ForegroundCall和BackgroundCall這2個GSMCall都不處於活動狀態。其中當為INCOMING 和WAITING表示在響鈴;當為DIALING 和 ALERTING時 表示在撥號;當不為IDLE 、 DISCONNECTED 和DISCONNECTING時表示活動狀態,即處在上述的響鈴、撥號、ACTIVE 和HOLDING時表示處於活動狀態。

canConference():當ForegroundCall處於Active、BackgroundCall處於HOLDING以及它們的串連數少於5個時則可進行電話會議

canTransfer():當ForegroundCall處於Active、BackgroundCall處於HOLDING時則可進行交換。

hangup (GsmConnection conn):掛斷某路電話串連

hangupWaitingOrBackground():掛斷插撥和後台電話

hangupForegroundResumeBackground():掛斷前台電話並恢複後台電話

hangupConnectionByIndex(GsmCall call, int index):掛斷某個索引指定的前台通話

hangupAllConnections(GsmCall call):掛斷所有電話通路

 

擷取通話狀態

Android提供的介面:TelephonyManager和PhoneStateListener

提供了諸如資料連線狀態(串連、未串連)和傳輸方向(上傳或下載等)、電話狀態(空閑IDLE、摘機OFFHOOK、RINGING響鈴)。OFFHOOK狀態起始於開始撥打到電話(包括還未接通)或開始電話中到電話被掛斷這個時間段。API中未區分開始撥叫到對方應答這一狀態。

這樣,如果只用API的話就無法擷取準確的撥叫計時(從接通開始的那一刻)資訊,只能得到從開始呼叫的那一刻開始的計時資訊。

但在底層的庫中電話狀態詳細區分了這些狀態,見Call類。它定義了更詳細的狀態:

IDLE, ACTIVE, HOLDING, DIALING, ALERTING, INCOMING, WAITING, DISCONNECTED;

Call類只是個抽象類別,GSM和CDMA手機有其自己的實現。其中GSM的實現是GsmCall,其狀態的更新依賴於DriverCall類。在DriverCall中,使用ATResponseParser解析AT命令CLCC的執行結果,即可得知通話狀態:

+CLCC: 1,0,2,0,0,”+18005551212″,145 index,isMT,state,mode,isMpty(,number,TOA)

具體見DriverCall.stateFromCLCC函數:

switch(state) {

case 0: return State.ACTIVE;

case 1: return State.HOLDING;

case 2: return State.DIALING;

case 3: return State.ALERTING;

case 4: return State.INCOMING;

case 5: return State.WAITING;

default:

throw new ATParseEx(“illegal call state ” + state);

}

 

這樣,如果想獲知通話狀態從呼叫開始到呼叫接通這一變化,只能使用底層的未開放的API:

Phone phone = PhoneFactory.getDefaultPhone();

Call.State state = phone.getForegroundCall().getState();

因此,這種需要調用底層API的程式就不能在SDK上進行開發。它必須和Android原始碼一起編譯。使用它的代價是很可能導致這種程式在不同Android平台上的不相容,如運行不正確甚至根本不能運行等。

 

資料連線管理

資料連線管理是由GsmDataConnectionTracker來完成。

boolean trySetupData(String reason) 嘗試建立資料連線。它只是檢查當前手機狀態是具備建立資料連線的條件,當符合條件時,就去建立串連。這些條件包括當前是否處在正常的非串連狀態、phone是否處在空閑狀態、SIM卡是否就緒、是否允許資料連線、資料連線是否受限、以及電源狀態也允許的情況下。接著調用buildWaitingApns去構建一個可供使用的APN列表(使用者佈建的preferred APN,若沒設定則APN的類型匹配亦可,詳見buildWaitingApns說明),若列表不為空白,若就調用setupData建立資料連線。

boolean setupData(String reason)

setupData使用PdpConnection的connect函數(最終調用到RIL的setupDataCall函數),從備選APN列表waitingApns中,選擇第一個APN作為配置去建立資料連線,詳見buildWaitingApns函數。

 

APN的使用

createAllApnList:用當前SIM卡對應的電訊廠商查詢系統的所有APN,並調用createApnList

建立APN列表,並找到使用者佈建的preferred APN

createApnList:用查詢得到的資料集,建立APN列表

onApnChanged:當APN被使用者更改時,將調用到此函數,重建立立資料連線

buildWaitingApns:使用使用者佈建的preferred APN構建一個可用於資料連線的備選APN列表,即waitingApns列表(當有preferred APN,該列表就只有一個)。若使用者沒有設定preferred APN,則將所有類型匹配的APN添加到waitingApns列表(如default類型)。當trySetupData函數檢查有waitingApns列表中有可用的APN時,就會去嘗試建立串連。

getNextApn():當PdpConnection需要用到APN建立資料連線時,將調用該函數從waitingApns備選列表中擷取第一個APN,直至成功建立資料連線為止。

setPreferredApn:當使用者沒有設定preferred APN時,將當前資料連線成功的那個APN設定為preferred APN。詳見onDataSetupComplete。

getPreferredApn:使用者擷取使用者佈建的preferred APN

網路資訊

包括IP地址、網關和DNS資訊,應使用GSMPhone

getIpAddress

getGateway

getDnsServers

上層應用程式應該用GSMPhone來使用這些API

本文連結地址: http://www.redwolf-blog.com/?p=1100

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.