Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android 4.4 Kitkat Phone工作流程淺析(八)__Phone狀態分析

Android 4.4 Kitkat Phone工作流程淺析(八)__Phone狀態分析

編輯:關於Android編程

 

前置文章:

《Android 4.4 Kitkat Phone工作流程淺析(一)__概要和學習計劃》

《Android 4.4 Kitkat Phone工作流程淺析(二)__UI結構分析》

《Android 4.4 Kitkat Phone工作流程淺析(三)__MO(去電)流程分析》

《Android 4.4 Kitkat Phone工作流程淺析(四)__RILJ工作流程簡析》

《Android 4.4 Kitkat Phone工作流程淺析(五)__MT(來電)流程分析》

《Android 4.4 Kitkat Phone工作流程淺析(六)__InCallActivity顯示更新流程》

《Android 4.4 Kitkat Phone工作流程淺析(七)__來電(MT)響鈴流程》

概述

通過前面一系列的文章,我們對整個Phone模塊有了基本的了解,本文主要目的是分析在整個Telephony架構中Phone的狀態以及它們之間的關系。關於Phone狀態改變後的通知流程,請大家參看《Android 4.4 Kitkat Phone工作流程淺析(六)__InCallActivity顯示更新流程》。

在《Android 4.4 Kitkat Phone工作流程淺析(六)__InCallActivity顯示更新流程》的概述中,我們提到Call的狀態分為6種:ACTIVEHOLDINGDIALINGALERTINGINCOMINGWAITING。這裡的依據是什麼呢?在Google AOSP代碼中,我們可以看到google使用的是AT+CLCC的方式來獲取當前通話信息的,CLCC的狀態描述總共有6種,也就是:active(0)、held(1)、dialing(2)、alterting(3)、incoming(4)、waiting(5),括號裡為狀態對應的數值,關於AT+CLCC的指令描述,請大家參考相關AT文檔。這些狀態值由Modem端返回,也就是說所有Call狀態的源頭在Modem端。

但是,MTK並沒有使用CLCC查詢方式,而是改用了AT+ECPI的方式,根據ECPI的msg_type來判斷當前Modem的狀態,歸根結底還是上面提到6種狀態。詳細請參看《Android 4.4 Kitkat Phone工作流程淺析(六)__InCallActivity顯示更新流程》中Telephony Framework接收處理反饋部分,該部分有簡單分析MTK自己添加的AT指令ECPI。

我們還是按照自底向上的方式分析狀態改變的流程,從Telephony Framework開始,然後是TeleService,最後是InCallUI,整個流程如下圖:

\

狀態來源—— Modem

通話狀態的起始源自Modem狀態的改變,而Modem會將這些信息通過串口方式返回給RILC,再由RILC返回給RILJ,我們在《Android 4.4 Kitkat Phone工作流程淺析(四)__RILJ工作流程簡析》中有對這個流程簡單分析,最後由RILJ來處理這些返回信息。比如當有一通來電時,我們會在radio Log中看到如下信息:
01-01 18:11:47.047   682   705 D use-Rlog/RLOG-AT: AT< +ECPI: 1,130,0,0,0,0,13800138000,129,
ECPI的格式如下:
+ECPI:,,,,,,[,],[]
對應的信息如下圖: \

其中msg_type就是Modem返回的狀態信息,這些狀態信息以具體的數值表示,黑體加粗為目前MTK Android 4.4 有使用到的幾種類型。

DriverCall. State狀態獲取

在GsmCallTracker的handleCallProgressInfo()方法中,首先會對收到的msg_type進行歸類,並得到DriverCall.State,這裡的handleCallProgressInfo()的作用實際上和AOSP中的handlePollCalls()的作用一致,關鍵代碼如下:
//... ...省略
if (msgType == 132 || msgType == 6)
    dc.state = DriverCall.State.ACTIVE;
else if (msgType == 131)
    dc.state = DriverCall.State.HOLDING;
else if (msgType == 130 && callId != 254)
    dc.state = DriverCall.State.DIALING;
else if (msgType == 2)
    dc.state = DriverCall.State.ALERTING;
else if (msgType == 0)
{
    for (j = 0; j < MAX_CONNECTIONS; j++) {
        if (mConnections[j] != null) {
            count ++;
        }
    }
    if (mState == PhoneConstants.State.IDLE || 
        (count == 0 &&  mForegroundCall.getState() == GsmCall.State.DIALING))
    {
        dc.state = DriverCall.State.INCOMING;
    }
    else
        dc.state = DriverCall.State.WAITING;
}
//... ...省略

也就是說DriverCall.State由ECPI的msg_type和callId值共同決定,這裡我們主要看msg_type,它們之間的對應關系如下圖:

\ DriverCall實際反映了Modem端真實的通話連接信息。

Call. State (Internal)狀態獲取

這裡的Call指的是com.android.internal.telephony.Call,源碼路徑在SourceCode/frameworks/opt/telephony/src/java/com/android/internal/telephony/Call.java。該類是一個抽象類,其子類有GsmCall、CDMACall,這裡我們只關心GsmCall。

在GsmCallTracker的handleCallProgressInfo()方法中,完成DriverCall.State的轉換後,便開始執行DriverCall.State和Call.State的轉換了,關鍵代碼如下:

//... ...省略
if (conn == null) 
{
    log(1. new connection appeared!!);
    if (mPendingMO != null)
    {
        //DriverCall.State.DIALING
        if (msgType == 130)
        {
            log(1.1. it is a MO call);
            mConnections[i] = mPendingMO;
            mPendingMO.mIndex = i;
            //GsmConnection根據DriverCall更新GsmCall
            mPendingMO.update(dc);
            mPendingMO = null;
            //... ...省略
        }
        //... ...省略
    }
    //DriverCall.State.INCOMING/DriverCall.State.WAITING
    else if (msgType == 0) 
    {
        log(1.2 it is a MT call);
        //根據DriverCall新建GsmConnection對象,並根據DriverCall狀態獲取
        //對應的GsmCall對象
        mConnections[i] = new GsmConnection(mPhone.getContext(), dc, this, i);
        //... ...省略
    }
    //... ...省略
}
//... ...省略

從以上代碼可以看出,針對MT和MO流程采用了不同的方式獲取Call.State。

MO流程獲取Call. State (Internal)

MO流程使用GsmConnection的update()方法來獲取Call.State(internal),關鍵代碼如下:

update (DriverCall dc) {
    GsmCall newParent;
    //... ...省略
    //根據DriverCall.State獲取與之對應的GsmCall對象
    newParent = parentFromDCState(dc.state);
    //... ...省略
    //更新Call.State
    if (newParent != mParent) {
        if (mParent != null) {
            //移除先前連接,並將State設置為Call.State.IDLE
            mParent.detach(this);
        }
	//增加當前連接,並更新State
        newParent.attach(this, dc);
        mParent = newParent;
        changed = true;
    } else {
        boolean parentStateChange;
        //更新State
        parentStateChange = mParent.update (this, dc);
        changed = changed || parentStateChange;
    }
    //... ...省略
    return changed;
}

我們看到parentFromDCState()方法,這裡實際上是根據DriverCall.State來獲取對應的GsmCall對象,如下:

private GsmCall
parentFromDCState (DriverCall.State state) {
    switch (state) {
        case ACTIVE:
        case DIALING:
        case ALERTING:
            return mOwner.mForegroundCall;
        //break;
        case HOLDING:
            return mOwner.mBackgroundCall;
        //break;
        case INCOMING:
        case WAITING:
            return mOwner.mRingingCall;
        //break;
        default:
            throw new RuntimeException(illegal call state:  + state);
    }
}
通過以上代碼可以知道GsmCall的三種狀態:foregroundCall、backgroundCall、ringingCall它們所對應的DriverCall.State,如下圖:

\

在根據DriverCall.State獲取GsmCall對象之後,便根據GsmCall的detach()、attach()、update()方法來更新Call.State,而更新代碼的關鍵是stateFromDCState(),關鍵代碼如下:

mState = stateFromDCState (dc.state);

static State
stateFromDCState (DriverCall.State dcState) {
    switch (dcState) {
        case ACTIVE:        return State.ACTIVE;
        case HOLDING:       return State.HOLDING;
        case DIALING:       return State.DIALING;
        case ALERTING:      return State.ALERTING;
        case INCOMING:      return State.INCOMING;
        case WAITING:       return State.WAITING;
        default:            throw new RuntimeException (illegal call state: + dcState);
    }   
}

到這裡完成了MO流程DriverCall.State和Call.State(internal)的映射。

MT流程獲取Call. State (Internal)

MO的Call.State獲取是通過pendingMO.update()方法發起的,而MT流程則是通過實例化GsmConnection對象發起的,代碼如下:

mConnections[i] = new GsmConnection(mPhone.getContext(), dc, this, i);

GsmConnection (Context context, DriverCall dc, GsmCallTracker ct, int index) {
    //... ...省略
    //根據DriverCall.State獲取GsmCall
    mParent = parentFromDCState (dc.state);
    //增加GsmConnection並更新Call.State
    mParent.attach(this, dc);
}

後續和MO的流程一致,不在贅述。

在Call.State(internal)狀態獲取流程中,看起來似乎有些復雜,我們簡單總結如下:

1. 6種DriverCall.State分別對應GsmCall對象fgCall、bgCall以及ringingCall;

2. Call.State(internal)是由GsmConnection發起更新的;系統中有三個GsmCall對象,分別是fgCall、bgCall已經ringingCall,GsmConnection根據DriverCall.State的改變,將自己劃分到不同的GsmCall對象中;

(PS:比如來電的時候建立一個GsmConnection,此時它屬於ringingCall;在來電接通後它會將自己更為屬於fgCall,如果此時你再撥打一通電話,那麼該GsmConnection又會將自己更改為屬於bgCall。)

通過以上分析我們可以知道DriverCall.State與Call.State的對應關系如下:

\

TelephonyManager. CALL_STATE狀態獲取

TelephonyManager狀態用於給三方應用提供Phone狀態,這一小節實際上可以分為兩部分即:Phone.State和TelephonyManager.CallState,前者是內部使用,後者供外部使用;前者根據Call.State(Internal)獲取對應狀態,後者根據Phone.State獲取對應狀態。

在Android 4.4中並沒有所謂的Phone.State,這是Android 4.0之前的稱呼,實際指的是PhoneConstants.State。PhoneConstants.State就是Android 4.0以及之前版本中的Phone.State,其使用IDLE、RINGING、OFFHOOK來表示當前Phone的狀態,這些狀態將提供給TelephonyManager並暴露給三方應用。

在GsmCallTracker的handleCallProgressInfo()中,經過了DriverCall.State的獲取與Call.State(internal)的獲取之後,通過updatePhoneState()來更新PhoneConstants.State,關鍵代碼如下:

private void
updatePhoneState() {
    PhoneConstants.State oldState = mState;
    if (mRingingCall.isRinging()) {
        mState = PhoneConstants.State.RINGING;
    } else if (mPendingMO != null ||
            !(mForegroundCall.isIdle() && mBackgroundCall.isIdle())) {
        mState = PhoneConstants.State.OFFHOOK;
    } else {
        mState = PhoneConstants.State.IDLE;
    }
    if (mState == PhoneConstants.State.IDLE && oldState != mState) {
    //如果是IDLE狀態則發起通知,語音通話結束,告知數據連接可用
        mVoiceCallEndedRegistrants.notifyRegistrants(
            new AsyncResult(null, null, null));
    } else if (oldState == PhoneConstants.State.IDLE && oldState != mState) {
    //如果是非IDLE狀態,語音通話開始,告知數據連接不可用
        mVoiceCallStartedRegistrants.notifyRegistrants (
                new AsyncResult(null, null, null));
    }
    log(updatePhoneState: old:  + oldState + , new:  + mState);
    if (mState != oldState) {
        //通知三方應用
        mPhone.notifyPhoneStateChanged();
    }
}
通過代碼我們可以很清楚的看到,PhoneConstants.State是由Call.State(internal)決定的,分別根據fgCall、bgCall、ringingCall來獲取Call.State(internal)的狀態。簡單的分析下它們之間的對應關系。

PhoneConstants. State. RINGING

根據前面的代碼,我們需要查看GsmCall中的isRinging()方法的返回值,如下:

public boolean isRinging() {
    return getState().isRinging();
}

//這裡的mState是com.android.internal.telephony.State對象
public State getState() {
    return mState;
}

public boolean isRinging() {
    return this == INCOMING || this == WAITING;
}
根據以上代碼可以知道PhoneConstants.State.RINGING相當於Call.State.INCOMING和Call.State.WAITING。

PhoneConstants. State. OFFHOOK

這裡就需要查看mPendingMO對象是否為null以及fgCall和bgCall的isIdle()方法的返回值,如下:
//如果mPendingMO不為null,則表示當前是MO流程。
//後面的判斷表示只要fgCall或者bgCall其中之一不是IDLE狀態,則是Phone狀態為OFFHOOK
mPendingMO != null || !(mForegroundCall.isIdle() && mBackgroundCall.isIdle())

//isIdle與isAlive是互斥的
public boolean isIdle() {
    return !getState().isAlive();
}

//如果Call.State是IDLE/DISCONNECTED/DISCONNECTING中的任意一種狀態,則返回false
//反之則為true
public boolean isAlive() {
    return !(this == IDLE || this == DISCONNECTED || this == DISCONNECTING);
}
總的來講,如果當前為MO流程以及Call.State(internal)不為IDLE、DISCONNECTED、DISCONNETING三者中的任意一種,即表示當前Phone狀態為OFFHOOK。

PhoneConstants. State. IDLE

除了PhoneConstants.State.RINGING和PhoneConstants.State.OFFHOOK之外的狀態都屬於PhoneConstants.State.IDLE,對應於Call.State.IDLE、Call.State.DISCONNECTING、Call.State.DISCONNECTED任意一種。

在updatePhoneState()方法的最後,調用了notifyPhoneStateChanged()將Phone狀態向TelephonyManager傳送,並最終通過mRegistry.notifyCallState()方法將Phone狀態傳遞給所有注冊了PhoneStateChangeListener。這裡我們主要看到DefaultPhoneNotifier.notifyPhoneState()方法,在這裡最終實現了Phone.State向TelePhonyManager.Call_STATE的過度,整個過程關鍵代碼如下:

//GsmCallTracker updatePhoneState()方法中調用
if (mState != oldState) {
    mPhone.notifyPhoneStateChanged();
}

//這裡的this是GSMPhone對象
/*package*/ void notifyPhoneStateChanged() {
    updateCipherIndication();
    mNotifier.notifyPhoneState(this);
}

public void notifyPhoneState(Phone sender) {
    //這裡是com.android.internal.telephony.Call, sender是GSMPhone對象
    Call ringingCall = sender.getRingingCall();
    String incomingNumber = ; 
    if (ringingCall != null && ringingCall.getEarliestConnection() != null){
        incomingNumber = ringingCall.getEarliestConnection().getAddress();
    }   
    try {
        //將Phone狀態通知給所有注冊了PhoneStateChange的Listener
        //根據conertCallState方法將PhoneConstants.State轉換為TelephonyManager.CALL_STATE
        mRegistry.notifyCallState(convertCallState(sender.getState()), incomingNumber);
    } catch (RemoteException ex) {
        // system process is dead
    }   
}
//這裡重點關注三個方法:
//1. sender.getRingingCall()
//2. sender.getState()
//3. convertCallState(sender.getState())

public GsmCall getRingingCall() {
    //mCT為GsmCalltracker對象,mRingingCall為GsmCall對象
    return mCT.mRingingCall;
}

public PhoneConstants.State getState() {
    //mCT為GsmCallTracker對象
    //mState為PhoneConstants.State對象初始值為PhoneConstants.State.IDLE
	//mState就是前面提到的Phone.State,也就是PhoneConstants.State
    return mCT.mState;
}

public static int convertCallState(PhoneConstants.State state) {
    switch (state) {
        case RINGING:
            return TelephonyManager.CALL_STATE_RINGING;
        case OFFHOOK:
            return TelephonyManager.CALL_STATE_OFFHOOK;
        default:
            return TelephonyManager.CALL_STATE_IDLE;
    }
}

這裡可以很清楚的看到它們之間的對應關系,普通APP便可以通過獲取TelephonyManager對象的CALL_STATE來判斷當前Phone的狀態,這裡我們還是用表格來直觀的看看Phone.State與Call.State(internal)以及TelephonyManager.CALL_STATE之間的對應關系,如下:

\

Call. State (TeleService)狀態獲取

這裡的Call指的是在com.android.services.telephony.common.Call,源碼位於SourceCode/packages/services/Telephony/common/src/com/android/services/telephony/common/Call.java,這裡在Call.State後面加上了TeleService用以和前面framework中的Call加以區別。

在《Android 4.4 Kitkat Phone工作流程淺析(六)__InCallActivity顯示更新流程》中我們已經分析了,通話狀態從底層傳遞到上層的整個流程。在經過了Telephony Framework的處理之後,便傳遞到TeleService中,具體流程請參看本文開頭的“通話狀態更新時序圖”。當更新流程來到CallModeler的onPhoneStateChanged()方法中時,我們注意到以下關鍵代碼:

private void onPhoneStateChanged(AsyncResult r) {
    Log.i(TAG, onPhoneStateChanged: );
    //這裡為com.android.services.telephony.Call
    final List updatedCalls = Lists.newArrayList();
    //根據Call.State(internal)更新Call.State(TeleService)
    doUpdate(false, updatedCalls);
    //... ...省略
    // M: add skip update logic. When 1A + 1R, skip update calls to InCallUI while query is running.
    if (!ignoreUpdate()) {
        if (updatedCalls.size() > 0) {
            for (int i = 0; i < mListeners.size(); ++i) {
                //將狀態改變向後繼續傳遞,最終到達InCallUI
                mListeners.get(i).onUpdate(updatedCalls);
            }
        }
    }
    //... ...省略
}
在該方法中完成了Call.State(Internal)和Call.State(TeleService)的轉換,重點關注doUpdate()方法,關鍵代碼如下:
private void doUpdate(boolean fullUpdate, List out) {
//... ...省略
//通過分析可以知道connection的getState()方法實際為,獲取connection對應的
//Call(Internal)的狀態,也就是Call.State(Internal)
//當Call.State(Internal)不屬於IDLE/DISCONNECTED/INCOMING/WAITING時
//shouldUpdate返回true,我們可以理解為Phone狀態位OFFHOOK時才需要更新
/*final*/ boolean shouldUpdate =
        (connection.getState() !=
                com.android.internal.telephony.Call.State.DISCONNECTED &&
        connection.getState() !=
                com.android.internal.telephony.Call.State.IDLE &&
        !connection.getState().isRinging())
        || fullUpdate; 
//... ...省略
final boolean isDisconnecting = connection.getState() ==
                com.android.internal.telephony.Call.State.DISCONNECTING;
//如果Phone狀態位OFFHOOK,shouldCreate返回true
final boolean shouldCreate = shouldUpdate && !isDisconnecting;
//根據connection對象創建與之對應的TeleService Call對象
final Call call = getCallFromMap(mCallMap, connection, shouldCreate /* create */); 
//... ...省略
//跳轉實現Call.State(Internal)和Call.State(TeleService)的映射
boolean changed = updateCallFromConnection(call, connection, false);
//... ...省略
}
繼續查看updateCallFromConnection()方法,關鍵代碼如下:
private boolean updateCallFromConnection(Call call, Connection connection,
        boolean isForConference) {
    boolean changed = false;
    //根據GsmConnection對象,獲取Call.State(Internal)並更新Call.State(TeleService)
    final int newState = translateStateFromTelephony(connection, isForConference);
    //... ...省略
    return changed;
}

private int translateStateFromTelephony(Connection connection, boolean isForConference) {
    //connection.getState實際上為connection所屬的GsmCall的狀態也就是Call.State(Internal)
    com.android.internal.telephony.Call.State connState = connection.getState();
    //... ...省略
    //Call.State(Internal)與Call.State(TeleService)對應關系
    int retval = State.IDLE;
    switch (connState) {
        case ACTIVE:
            retval = State.ACTIVE;
            break;
        case INCOMING:
            retval = State.INCOMING;
            break;
        case DIALING:
        case ALERTING:
            if (PhoneGlobals.getInstance().notifier.getIsCdmaRedialCall()) {
                retval = State.REDIALING;
            } else {
                retval = State.DIALING;
            }
            break;
        case WAITING:
            retval = State.CALL_WAITING;
            break;
        case HOLDING:
            retval = State.ONHOLD;
            break;
        case DISCONNECTING:
            retval = State.DISCONNECTING;
            break;
        case DISCONNECTED:
            retval = State.DISCONNECTED;
        default:
    }

    //獲取ConferenceCall的Call.State(TeleService)
    if (!isForConference) {
        // 如果是conferenceCall則返回State.CONFERENCED
        if (isPartOfLiveConferenceCall(connection) && connection.isAlive()) {
            return State.CONFERENCED;
        }
    }
    return retval;
}
在經過以上處理之後,Call.State(Internal)就成功的轉化為Call.State(TeleService)了,Call.State(TeleService)總共有11個狀態,如下:
INVALID = 0;
IDLE = 1;           /* The call is idle.  Nothing active */
ACTIVE = 2;         /* There is an active call */   
INCOMING = 3;       /* A normal incoming phone call */ 
CALL_WAITING = 4;   /* Incoming call while another is active */ 
DIALING = 5;        /* An outgoing call during dial phase */
REDIALING = 6;      /* Subsequent dialing attempt after a failure */
ONHOLD = 7;         /* An active phone call placed on hold */
DISCONNECTING = 8;  /* A call is being ended. */
DISCONNECTED = 9;   /* State after a call disconnects */
CONFERENCED = 10;   /* Call part of a conference call */
那麼Call.State(Internal)與Call.State(TeleService)是如何對應的呢?我們用下面這張表格來說明,如下:
\

InCallState狀態獲取

在Android 4.4中,因為原來的Phone已經被拆解為InCallUI和TeleService了,所以google又新增了一個InCallState用於標識InCallActivity的狀態。InCallState的狀態總共有4種,分別是NO_CALLS、INCOMING、INCALL、OUTGOING。通過查找我們可以在SourceCode/packages/apps/InCallUI/src/com/android/incallui/InCallPresenter.java中找到InCallState的定義,如下:

public enum InCallState {
    // InCall Screen is off and there are no calls
    // InCallUI界面退出並且沒有通話
    NO_CALLS,

    // Incoming-call screen is up
    // 顯示來電界面
    INCOMING,

    // In-call experience is showing
    // 處於通話中
    INCALL,

    // User is dialing out
    // 主動呼叫即MT
    OUTGOING;

    public boolean isIncoming() {
        return (this == INCOMING);
    }   

    public boolean isConnectingOrConnected() {
        return (this == INCOMING ||
                this == OUTGOING ||
                this == INCALL);
    }   
}
該用於表示InCallActivity當前所處的狀態,那麼這些狀態與Call.State(Internal)以及Call.State(TeleService)之間的對應關系又是什麼呢?這裡又需要我們回到本文開頭的那張圖,在來電狀態變更之後,經過了Telephony Framework和TeleService的處理之後,會將相關信息傳遞到InCallUI中。此時,CallList便會處理這些信息並更新InCallState的狀態。

無論當前通話是來電或者掛斷或者是呼叫保持,這些都能夠在CallList中找到與之對應的處理方法,如:onIncoming、onDisconnect、onUpdate。通過這些方法可以完成對狀態信息的處理以及更新,它們的共通特點是都會調用updateCallInMap()方法。在該方法中完成對Call(TeleService)對象的創建以及和GsmConnection對象的關聯,關鍵代碼如下:

private boolean updateCallInMap(Call call) {
    //... ...省略
    if (call.getState() == Call.State.DISCONNECTED) {
        //... ...省略
        mCallMap.put(id, call);
    } else if (!isCallDead(call)) {
        mCallMap.put(id, call);
        //... ...省略
    return updated;
}
這裡為什麼會提到CallList呢?因為根據時序圖我們可以知道在CallList處理之後,便會將消息通知到InCallPresenter.onCallListChange()方法中,正是在這裡將我們更新了InCallState的狀態,首先看到InCallPresenter.onCallListChange()關鍵代碼:
@Override
public void onCallListChange(CallList callList) {
    if (callList == null) {
        return;
    }
    //將Call.State(TeleService)轉換成InCallState
    InCallState newState = getPotentialStateFromCallList(callList);
    newState = startOrFinishUi(newState);
    //... ...省略
}
繼續查看getPotentialStateFromCallList()方法,如下:
public static InCallState getPotentialStateFromCallList(CallList callList) {
    //InCallState默認狀態位NO_CALLS
    InCallState newState = InCallState.NO_CALLS;
    //如果calllist為null返回默認狀態NO_CALLS
    if (callList == null) {
        return newState;
    }
    //INCOMING/OUTGOING/INCALL的對應
    if (callList.getIncomingCall() != null) {
        newState = InCallState.INCOMING;
    } else if (callList.getOutgoingCall() != null) {
        newState = InCallState.OUTGOING;
    } else if (callList.getActiveCall() != null ||
            callList.getBackgroundCall() != null ||
            callList.getDisconnectedCall() != null ||
            callList.getDisconnectingCall() != null) {
        newState = InCallState.INCALL;
    }
    return newState;
}
這裡我們主要看下INCOMING、OUTGOING以及INCALL這幾個狀態是如何與Call.State(TeleService)對應的。

InCallState. INCOMING

根據前面的代碼,首先查看CallList中的getInComingCall()方法,可以看到:
public Call getIncomingCall() {
    Call call = getFirstCallWithState(Call.State.INCOMING);
    if (call == null) {
        call = getFirstCallWithState(Call.State.CALL_WAITING);
    }   
    //... ...省略
    return call;
}

public Call getFirstCallWithState(int state) {
    return getCallWithState(state, 0);
}

//在HashMap mCallMap中查找valuses
//是否有與對應state匹配的Call(TeleService)
public Call getCallWithState(int state, int positionToFind) {
    Call retval = null;
    int position = 0;
    for (Call call : mCallMap.values()) {
        if (call.getState() == state) {
            if (position >= positionToFind) {
                retval = call;
                break;
            } else {
                position++;
            }
        }
    }
    return retval;
}
代碼中所使用的是com.android.services.telephony.common.Call,通過分析可以知道,如果在CallList的mCallMap.valuses中有找到處於Call.State.INCOMING或者Call.State.CALL_WAITING的Call對象,即表示InCallState狀態為INCOMING。

InCallState. OUTGOING

同樣我們需要查看CallList中的getOutgoingCall()方法,關鍵代碼如下:
public Call getOutgoingCall() {
    Call call = getFirstCallWithState(Call.State.DIALING);
    if (call == null) {
        call = getFirstCallWithState(Call.State.REDIALING);
    }
    return call;
}
因為後面處理過程和前面InCallState.INCOMING類似,這裡就不再重復。通過分析可以知道InCallState.OUTGOING與TeleService Common中的Call.State.DIALING和Call.State.REDIALING對應。

InCallState. INCALL

查看getActiveCall()、getBackgroundCall()、getDisconnectedCall()、getDisconnectingCall()方法,關鍵代碼如下:
//ACTIVE
public Call getActiveCall() {
    return getFirstCallWithState(Call.State.ACTIVE);
}
//ONHOLD
public Call getBackgroundCall() {
    return getFirstCallWithState(Call.State.ONHOLD);
}
//DISCONNECTED
public Call getDisconnectedCall() {
    return getFirstCallWithState(Call.State.DISCONNECTED);
}
//DISCONNECTING
public Call getDisconnectingCall() {
    return getFirstCallWithState(Call.State.DISCONNECTING);
}
同樣我們可以得出InCallState.INCALL對應於TeleService Common中的:Call.State.ACTIVE、Call.State.ONHOLD、Call.State.DISCONNECTED、Call.State.DISCONNECTING。

InCallState. NO_CALLS

如果不滿足INCOMING、OUTGOING、INCALL狀態的,都屬於NO_CALLS,但實際上NO_CALLS與Call.State.IDLE對應。

通過前面的分析,我們大致知道了InCallState的作用以及來源,還是用一張圖來看看InCallState與Call.State(TeleService)的對應關系,如下:

\

這裡大家可能會覺得奇怪,為什麼Call.State.INVALID和Call.State.CONFERENCED沒有與InCallState.NO_CALLS對應呢?INVALID是Call.State初始時賦的值,而實際狀態不會為INVALID,而是IDLE。對於CONFERENCED來說,因為Conference Call會有自己單獨的處理,這一點在CallModeler裡面可以看到,因此也不屬於NO_CALLS。

小結

通過以上分析,我們知道了Telephony中的各種狀態,以及它們之間的對應關系,這裡簡單的總結一下本文所述的內容:

1. Telephony中關於Call、Phone的狀態有如下幾6種:

(1). DriverCall.State;

將Modem返回的通話狀態轉換成最基本的Call狀態,DriverCall.State包含ACTIVE、HOLDING、DIALING、ALERTING、INCOMING、WAITING 6種

(2). Call.State(internal);

在整個Telephony結構中,有且只有三種Call(internal)即:foregroundCall、backgroundCall、ringingCall,這三種類型描述了系統中所有存在的Call(internal)類型,而這三種Call的狀態用Call.State(internal)來描述,包含ACTIVE、HOLDING、DIALING、ALERTING、INCOMING、WAITING、IDEL、DISCONNECTING、DISCONNECTED,總共9種類型

在實際使用中,我們並不會直接使用Call.State(internal),取而代之的是GsmConnection對象的getState方法。一個GsmCall對象可以擁有多個GsmConnection,比如在使用會議電話時,一路通話中擁有多個連接GsmConnection對象會根據DriverCall.State的狀態,將自己分配到不同的Call( fgCall、bgCall、ringingCall )對象中比如當有一路來電時,此時會建立GsmConnection對象,並歸屬於ringingCall對象;而當來電被接聽後,該GsmConnection對象會將自己分配到foregroundCall對象中

(3). PhoneConstants.State;

在Android 4.0以及之前叫做Phone.State,用於描述手機在通話過程中的狀態,其狀態更新來源於Call.State(internal)。根據Call.State(internal)的狀態劃分為三類:IDLE、RINGING、OFFHOOK。這些狀態供系統以及系統級APP使用。

(4). TelephonyManager.CALL_STATE_XX;

該狀態源自PhoneConstants.State,並與其一一對應,即包含類型TelephonyManager.CALL_STATE_IDLE、TelephonyManager.CALL_STATE_RINGING、TelephonyManager.CALL_STATE_OFFHOOK。這些將會通過“廣播”以及“PhoneStateChanged回調”通知給三方應用,該狀態的主要目的也是暴露給三方使用。

(5). Call.State(TeleService);

在Android 4.4中,Phone模塊被劃分為InCallUI和TeleService兩部分,而這裡的Call.State(TeleService)正是通話狀態在TeleService中的表現,同時該狀態也將為後面的InCallState提供參考基准。Call.State(TeleService)包含了基本的11種類型:ACTIVE、ONHOLD、DIALING、REDIALING、INCOMING、CALL_WAITING、DISCONNECTED、DISCONNECTING、IDLE、CONFERENCE、INVALID

我們知道com.android.internal.telephony.Call也就是前面提及的Call(internal),GsmCall和CDMACall都是其子類,主要作用是對通話這種屬性的一種抽象。而Call(TeleService)實際上是com.android.services.telephony.common.Call,在Telephony Framework中完成了GsmCall對象的處理和操作之後,會將相關的信息在TeleService中轉換為Call(TeleService)對象,並存儲在HashMap中。com.android.services.telephony.common.Call實現了Parcelable接口,其作用是描述一路通話及其狀態,在這裡能夠獲得許多關於該路通話的詳細信息。

(6). InCallPresenter.InCallState;

InCallState是用於決定InCallActivity所處狀態,其包含類型為4種:NO_CALLS、INCALL、OUTGOING、INCOMING。該類型是首次出現在Android Telephony中,InCallUI的顯示則依賴於此狀態。

特別注意:InCallState.INCALL等價於Call.State(TeleService)的ACTIVE、ONHOLD、DISCONNECTING、DISCONNECTED;而PhoneConstants.State.OFFHOOK對應於Call.State(TeleService)的ACTIVE、ONHOLD、DIALING、REDIALING

2. Telephony中的各種狀態並不是獨立存在的,它們之間是一種由底向上的依賴關系,即底層Modem端通話狀態發生了改變,那麼頂層的InCallSate狀態也會隨之而變化,不同的狀態具有不同的作用。

最後,用兩張圖來反應本文的分析結論,分別是Telephony架構中各種State的映射關系,如圖1:

\

圖 1

下圖2為Call.State(internal)各個狀態之間的切換流程以及與PhoneConstants.State之間的對應關系,如下:

\

圖 2

本文涉及圖片以及各資源,免積分下載,點這裡。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved