Android 6.0 telephony 状态分析

一、DriverCall. State状态获取

1、当用户发起MO(去电)流程之后,告诉Modem执行Dial操作,此时Modem的状态就会随之改变,并将状态改变信息通知到上层,因此这里会涉及到一些系统的radio log,可以看到发起拨号操作的相关重要log信息如下:

注:MO跟MT获取到DriverCall. State后的处理方式一样,但是MO更直观一些,当用户发起MO的时候就开始与modem开始交互
这里简单的分析一下重要的log信息:

01-05 06:02:13.788099  1596  1596 D RILJ    : [0118]> DIAL [SUB1]
01-05 06:02:13.806830   746   758 D AT      : AT> ATD13611936864;

这表示发起DIAL请求,紧接着执行ATD即AT拨号指令:
可以根据serial号查看AT指令的配对,Log中的“>”表示request,“<”表示response
根据AT指令的serial号”0118”我们可以在后面找到对应的response:

01-05 06:02:13.822931  1596  1811 D RILJ    : [0118]< DIAL  [SUB1]

这表明整个拨号的request和response已经完成,在此期间Modem主动返回了以下信息:

该条AT指令+ECPI是MTK添加的,在标准AT指令中查询不到,具体含义如下:

+ECPI:<call_id>,<msg_type>,<is_ibt>,<is_tch>,<dir>,<call_mode>,[<number>,<type>],[<disc_cause>]

对应着这个表我们来梳理一下以上log重要信息如下:
call_id:1,call id 为1;
msg_type:130,表示CSMCC_CALL_ID_ASSIGN_MSG,也就是Dialing;
dir:0,CLCC_MO_CALL,表示MO操作;
call_mode:0,CLCC_VOICE_CALL,表示普通语音拨号;
number:138001380000,表示主叫号码;
type:129,National call,也就是本国电话,如果是145则表示国际电话;
在Modem完成DIAL操作之后,紧接着返回了以下log:

01-05 07:10:05.482199  1596  1811 D RILJ    : [UNSL]< UNSOL_RESPONSE_CALL_STATE_CHANGED [SUB1]

接下来我们直接以MT的流程为例研究一下状态从Modem上传到InCallUI的过程
以上log是一条UnSolicited response消息,处理方法是RIL.java中的processUnsolicited(),类型为:UNSOL_RESPONSE_CALL_STATE_CHANGED 。

2、在GsmCallTracker的handlePollCalls()方法中,首先会对收到的msg_type进行归类,并得到DriverCall.State,DriverCall.State由ECPI的msg_type和callId值共同决定,这里我们主要看msg_type,它们之间的对应关系如下图:

这里写图片描述

3、当状态发生改变后就会调用notifyPreciseCallStateChanged 向上层发出响应

GsmCallTracker.java (frameworks\opt\telephony\src\java\com\android\internal\telephony\gsm)

    @Override
    protected synchronized void handlePollCalls(AsyncResult ar) {
            // 当状态改变之后便会通过GsmPhone的notifyPreciseCallStateChanged()方法发起响应
            mPhone.notifyPreciseCallStateChanged();
    }

二、在GsmCallTracker的handlePollCalls()方法中,完成DriverCall.State的转换后,便开始执行DriverCall.State和Call.State的转换了,关键代码如下:

    @Override
    protected synchronized void handlePollCalls(AsyncResult ar) {
            if (conn == null && dc != null) {
                // Connection appeared in CLCC response that we don't know about
                if (mPendingMO != null && mPendingMO.compareTo(dc)) {
                    // It's our pending mobile originating call
                    mConnections[i] = mPendingMO;
                    mPendingMO.mIndex = i;
                    //GsmConnection根据DriverCall更新GsmCall  (MO流程获取状态方式)
                    mPendingMO.update(dc);
                    mPendingMO = null;
                }

             } else if (conn != null && dc != null && !conn.compareTo(dc)) {
                //根据DriverCall新建GsmConnection对象,并根据DriverCall状态获取对应的GsmCall对象(MT流程获取状态方式)
                mConnections[i] = new GsmConnection (mPhone.getContext(), dc, this, i);

    }

从以上代码可以看出,针对MT和MO流程采用了不同的方式获取Call.State。

1、MO流程获取Call. State (Internal)

MO流程使用GsmConnection的update()方法来获取Call.State(internal),关键代码如下:
1)、GsmConnection.java (frameworks\opt\telephony\src\java\com\android\internal\telephony\gsm)

    // Returns true if state has changed, false if nothing changed
    /*package*/ boolean
    update (DriverCall dc) {
        GsmCall newParent;
        boolean changed = false;
        boolean wasConnectingInOrOut = isConnectingInOrOut();
        boolean wasHolding = (getState() == GsmCall.State.HOLDING);
        //... ...省略  
        //根据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;
        }
        if (wasConnectingInOrOut && !isConnectingInOrOut()) {
            onConnectedInOrOut();
        }
        if (changed && !wasHolding && (getState() == GsmCall.State.HOLDING)) {
            // We've transitioned into HOLDING
            onStartedHolding();
        }
        return changed;
    }

2)、我们看到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,如下图:
这里写图片描述
3)、在根据DriverCall.State获取GsmCall对象之后,便根据GsmCall的detach()、attach()、update()方法来更新Call.State,而更新代码的关键是stateFromDCState(),关键代码如下:
Call.java (frameworks\opt\telephony\src\java\com\android\internal\telephony)

    public 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)的映射。

2、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状态获取

1、TelephonyManager状态用于给三方应用提供Phone状态,这一小节实际上可以分为两部分即:Phone.State和TelephonyManager.CallState,前者是内部使用,后者供外部使用;

前者根据Call.State(Internal)获取对应状态,后者根据Phone.State获取对应状态。
Phone.State,其使用IDLE、RINGING、OFFHOOK来表示当前Phone的状态,这些状态将提供给TelephonyManager并暴露给三方应用。
在GsmCallTracker的handlePollCalls()中,经过了DriverCall.State的获取与Call.State(internal)的获取之后,通过updatePhoneState()来更新PhoneConstants.State,关键代码如下:
GsmCallTracker.java (frameworks\opt\telephony\src\java\com\android\internal\telephony\gsm)

    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));
        }
        if (mState != oldState) {
            //通知三方应用  
            mPhone.notifyPhoneStateChanged();
        }
    }

通过代码我们可以很清楚的看到,PhoneConstants.State是由Call.State(internal)决定的,分别根据fgCall、bgCall、ringingCall来获取Call.State(internal)的状态。简单的分析下它们之间的对应关系。

2、PhoneConstants. State. RINGING

根据前面的代码,我们需要查看Call.java中的isRinging()方法的返回值,如下:
Call.java (frameworks\opt\telephony\src\java\com\android\internal\telephony)

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。

3、PhoneConstants. State. OFFHOOK

这里就需要查看mPendingMO对象是否为null以及fgCall和bgCall的isIdle()方法的返回值,如下:
GsmCallTracker.java (frameworks\opt\telephony\src\java\com\android\internal\telephony\gsm)

//如果mPendingMO不为null,则表示当前是MO流程。  
//后面的判断表示只要fgCall或者bgCall其中之一不是IDLE状态,则是Phone状态为OFFHOOK 
private void updatePhoneState() {
        if (mPendingMO != null ||
                !(mForegroundCall.isIdle() && mBackgroundCall.isIdle())) {
            mState = PhoneConstants.State.OFFHOOK;
        }
    }

Call.java (frameworks\opt\telephony\src\java\com\android\internal\telephony)

//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);  
}  

4、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()方法中调用  
private void updatePhoneState() {
        if (mState != oldState) {
            mPhone.notifyPhoneStateChanged();
        }
    }

// GSMPhone.java (frameworks\opt\telephony\src\java\com\android\internal\telephony\gsm)
void notifyPhoneStateChanged() {
        mNotifier.notifyPhoneState(this);
    }

//DefaultPhoneNotifier.java (frameworks\opt\telephony\src\java\com\android\internal\telephony)
    @Override
    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 {
            if (mRegistry != null) {
                //将Phone状态通知给所有注册了PhoneStateChange的Listener  
                //根据conertCallState方法将PhoneConstants.State转换为TelephonyManager.CALL_STATE  
                mRegistry.notifyCallStateForPhoneInfo(phoneId, phoneType, subId,
                      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;  
}  
  // Phone.State转化为TelephonyManager.CallState
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之间的对应关系,如下:

这里写图片描述

注:
PhoneConstants.State.OFFHOOK 摘机状态,至少有个电话活动。该活动或是拨打(dialing)或是通话,或是 on hold。并且没有电话是ringing or waiting
PhoneConstants.State.IDLE 空闲状态,没有任何活动。
PhoneConstants.State.RINGING 来电状态,电话铃声响起的那段时间或正在通话又来新电,新来电话不得不等待的那段时间。

四、Call. State (TeleService)状态获取

Call.java (packages\apps\incallui\src\com\android\incallui)

    private void updateFromTelecommCall() {
        // 实现了 Call.State(Internal)和Call.State(TeleService)的映射
        setState(translateState(mTelecommCall.getState()));
        setDisconnectCause(mTelecommCall.getDetails().getDisconnectCause());
        mChildCallIds.clear();
        for (int i = 0; i < mTelecommCall.getChildren().size(); i++) {
            mChildCallIds.add(
                    CallList.getInstance().getCallByTelecommCall(
                            mTelecommCall.getChildren().get(i)).getId());
        }
    }
    private static int translateState(int state) {
    //Call.State(Internal)与Call.State(TeleService)对应关系 
        switch (state) {
            case android.telecom.Call.STATE_NEW:
            case android.telecom.Call.STATE_CONNECTING:
                return Call.State.CONNECTING;
            case android.telecom.Call.STATE_SELECT_PHONE_ACCOUNT:
                return Call.State.SELECT_PHONE_ACCOUNT;
            case android.telecom.Call.STATE_DIALING:
                return Call.State.DIALING;
            case android.telecom.Call.STATE_RINGING:
                return Call.State.INCOMING;
            case android.telecom.Call.STATE_ACTIVE:
                return Call.State.ACTIVE;
            case android.telecom.Call.STATE_HOLDING:
                return Call.State.ONHOLD;
            case android.telecom.Call.STATE_DISCONNECTED:
                return Call.State.DISCONNECTED;
            case android.telecom.Call.STATE_DISCONNECTING:
                return Call.State.DISCONNECTING;
            default:
                return Call.State.INVALID;
        }
    }

在经过以上处理之后,Call.State(Internal)就成功的转化为Call.State(TeleService)了,Call.State(TeleService)总共有13个状态,如下:

这里写图片描述

五、InCallState状态获取

1、在Android 4.4中,因为原来的Phone已经被拆解为InCallUI和TeleService了,所以google又新增了一个InCallState用于标识InCallActivity的状态。InCallState的状态总共有4种,分别是NO_CALLS、INCOMING、INCALL、OUTGOING。

通过查找我们可以在packages/apps/InCallUI/src/com/android/incallui/InCallPresenter.java中找到InCallState的定义,如下:

    /**
     * All the main states of InCallActivity.
     */
    public enum InCallState {
       // 没有电话
        NO_CALLS,
       // 来电
        INCOMING,
       // 正在通话中
        INCALL,
       //等待用户输入然后再呼出
        WAITING_FOR_ACCOUNT,
       // 预备拨出界面(UI is starting up but no call has been initiated yet.The UI is waiting for Telecomm to respond)
        PENDING_OUTGOING,
       // 拨出电话
        OUTGOING;
        public boolean isIncoming() {
            return (this == INCOMING);
        }
        public boolean isConnectingOrConnected() {
            return (this == INCOMING ||
                    this == OUTGOING ||
                    this == INCALL);
        }
    }

该用于表示InCallActivity当前所处的状态,那么这些状态与Call.State(Internal)以及Call.State(TeleService)之间的对应关系又是什么呢?我们回到MT流程中,在来电状态变更之后,经过了Telephony Framework和TeleService的处理之后,会将相关信息传递到InCallUI中。此时,CallList便会处理这些信息并更新InCallState的状态。

无论当前通话是来电或者挂断或者是呼叫保持,这些都能够在CallList中找到与之对应的处理方法,如:onIncoming、onDisconnect、onUpdate。通过这些方法可以完成对状态信息的处理以及更新,它们的共通特点是都会调用updateCallInMap()方法。在该方法中完成对Call(TeleService)对象的创建以及和GsmConnection对象的关联,关键代码如下:

    private boolean updateCallInMap(Call call) {
        boolean updated = false;
        if (call.getState() == Call.State.DISCONNECTED) {
            // update existing (but do not add!!) disconnected calls
            if (mCallById.containsKey(call.getId())) {
                // For disconnected calls, we want to keep them alive for a few seconds so that the
                // UI has a chance to display anything it needs when a call is disconnected.
                // Set up a timer to destroy the call after X seconds.
                final Message msg = mHandler.obtainMessage(EVENT_DISCONNECTED_TIMEOUT, call);
                mHandler.sendMessageDelayed(msg, getDelayForDisconnect(call));
                mPendingDisconnectCalls.add(call);
                mCallById.put(call.getId(), call);
                /// M:add for plug in. @{
                mCallMap.put(call.getId(), call.getTelecommCall());
                /// @}
                mCallByTelecommCall.put(call.getTelecommCall(), call);
                updated = true;
            }
        } else if (!isCallDead(call)) {
            mCallById.put(call.getId(), call);
            /// M:add for plug in. @{
            mCallMap.put(call.getId(), call.getTelecommCall());
            /// @}
            mCallByTelecommCall.put(call.getTelecommCall(), call);
            updated = true;
        } 
        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()方法,如下:

    /**
     * Given the call list, return the state in which the in-call screen should be.
     */
    public InCallState getPotentialStateFromCallList(CallList callList) {
        InCallState newState = InCallState.NO_CALLS;
        if (callList == null) {
            return newState;
        }
        //INCOMING/OUTGOING/INCALL的对应  
        if (callList.getIncomingCall() != null) {
            newState = InCallState.INCOMING;
        } else if (callList.getWaitingForAccountCall() != null) {
            newState = InCallState.WAITING_FOR_ACCOUNT;
        } else if (callList.getPendingOutgoingCall() != null) {
            newState = InCallState.PENDING_OUTGOING;
        } 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;
        }
        if (newState == InCallState.NO_CALLS) {
            if (mBoundAndWaitingForOutgoingCall) {
                return InCallState.OUTGOING;
            }
        }
        return newState;
    }

这里我们主要看下INCOMING、OUTGOING以及INCALL这几个状态是如何与Call.State(TeleService)对应的。

2、InCallState. INCOMING

根据前面的代码,首先查看CallList中的getInComingCall()方法,可以看到:

    public Call getIncomingCall() {
        Call call = getFirstCallWithState(Call.State.INCOMING);
        if (call == null) {
            call = getFirstCallWithState(Call.State.CALL_WAITING);
        }
        return call;
    }

    /**
     * Returns first call found in the call map with the specified state.
     */
    public Call getFirstCallWithState(int state) {
        return getCallWithState(state, 0);
    }

    //在HashMap<Integer, Call> mCallMap中查找valuses  
    //是否有与对应state匹配的Call(TeleService)  
    public Call getCallWithState(int state, int positionToFind) {
        Call retval = null;
        int position = 0;
        for (Call call : mCallById.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。

3、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对应。

4、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。

5、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来说,会有自己单独的处理,因此也不属于NO_CALLS。

六、 Phone状态总结

1. Telephony中关于Call、Phone的状态有如下几6种:

(1). DriverCall.State;
将Modem返回的通话状态转换成最基本的Call状态,DriverCall.State包含ACTIVE、HOLDING、INCOMING、WAITING 、DIALING、ALERTING、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种类型。

(3). PhoneConstants.State;
在Android 4.0以及之前叫做Phone.State,用于描述手机在通话过程中的状态,其状态更新来源于Call.State(internal)。根据Call.State(internal)的状态划分为三类:IDLE、RINGING、OFFHOOK。这些状态供系统以及系统级APP使用。

(4). TelephonyManager.CALL_STATE_XX;

(5). Call.State(TeleService);
在Android 4.4中,Phone模块被划分为InCallUI和TeleService两部分,而这里的Call.State(TeleService)正是通话状态在TeleService中的表现,同时该状态也将为后面的InCallState提供参考基准。Call.State(TeleService)包含了基本的13种类型:ACTIVE、ONHOLD、DIALING、REDIALING、INCOMING、CALL_WAITING、DISCONNECTED、DISCONNECTING、IDLE、CONFERENCE、INVALID、PENDING_OUTGOING(预备拨出)、WAITING_FOR_ACCOUNT(等待用户输入)。
我们知道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所处状态,其包含类型为6种:NO_CALLS、INCALL、OUTGOING、INCOMING、PENDING_OUTGOING(预备拨出)、WAITING_FOR_ACCOUNT(等待用户输入)。该类型是首次出现在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状态也会随之而变化,不同的状态具有不同的作用。

1)、Android 6.0 中 Telephony架构中各种State的映射关系
这里写图片描述

2)、Android 6.0 中,Telephony Framework Call.State 之间的转换关系如下图所示

3、Android 设计Phone状态的原因分析(参考网址:http://blog.csdn.net/a34140974/article/details/50156193

1)、一个Phone可以有多个Call。而每一个Call都对应着9种状态,分别是IDLE,ACTIVE,HOLDING,DIALING,ALERTING,INCOMING,WAITING,DISCONNECTED,DISCONNECTING。一般Phone启动时创建了3个Call,根据状态的不同,3个Call可以成为RingCall,ForegroundCall以及BackgroundCall和IdleCall。其中RingCall,ForegroundCall唯一,BackgroundCall你自己算下就知道最多2个,当一个电话都没有时3个Call都为Idle状态成为IdleCall。每个Call,最多有5个connections,但同时3个Call合起来不能超过7个connections。
这里就存在着三类状态,Connection的状态、CallState和PhoneState。Connection的状态更新CallState,而CallState更新PhoneState。

2)、从Call的9种状态来看,同一时刻三个Call只能有一个RingCall(INCOMING或WAITING状态),一个ForegroundCall(ACTIVE状态)一个BackgroundCall(HOLDING状态),可以同时有多个IDLE状态。
这里需要联想到实际的电话:假设有一个超级大忙人,
a)首先,有一通电话Call_1进入,Ring~~Ring,接之,此时这个Call_1由Ring变为Active状态,成为ForegroundCall;
b)接Call_1的过程中,又来了一通电话Call_2,接之,此时Call_1状态转化为HOLD成为BackgroundCall,而Call_2由Ring变为Active成为ForegroundCall
c)两通电话同时存在时应该还是可以切换的(多方通话),也就是说ForegroundCall和BackgroundCall可以互相转换,CallManager也确实提供了这样的一个函数;
d)此时又来第三通电话,再接之,BackgroundCall数量加1,ForegroundCall变为BackgroundCall。
以上分析可以得出结论就是:一个Phone对应多个Call。

    private CallManager() {
        mPhones = new ArrayList<Phone>();
        mRingingCalls = new ArrayList<Call>();
        mBackgroundCalls = new ArrayList<Call>();
        mForegroundCalls = new ArrayList<Call>();
        mDefaultPhone = null;
    }

参考博文:
http://blog.csdn.net/yihongyuelan/article/details/29187281

  • 5
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值