Android 4.4 Kitkat Phone工作流程浅析(八)__Phone状态分析


本文来自http://blog.csdn.net/yihongyuelan 转载请务必注明出处

本文代码以MTK平台Android 4.4为分析对象。与Google原生AOSP有些许差异。请读者知悉。

前置文章:

《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:<call_id>,<msg_type>,<is_ibt>,<is_tch>,<dir>,<call_mode>,[<number>,<type>],[<disc_cause>]
相应的信息例如以下图:


当中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;}elsedc.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.DIALINGif (msgType == 130){log("1.1. it is a MO call");mConnections[i] = mPendingMO;mPendingMO.mIndex = i;//GsmConnection依据DriverCall更新GsmCallmPendingMO.update(dc);mPendingMO = null;//... ...省略}//... ...省略}//DriverCall.State.INCOMING/DriverCall.State.WAITINGelse 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.Stateif (newParent != mParent) {if (mParent != null) {//移除先前连接。并将State设置为Call.State.IDLEmParent.detach(this);}//添加当前连接,并更新StatenewParent.attach(this, dc);mParent = newParent;changed = true;} else {boolean parentStateChange;//更新StateparentStateChange = 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获取GsmCallmParent = parentFromDCState (dc.state);//添加GsmConnection并更新Call.StatemParent.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_STATEmRegistry.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.Statereturn 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.Callfinal List<Call> 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) {//将状态改变向后继续传递。终于到达InCallUImListeners.get(i).onUpdate(updatedCalls);}}}//... ...省略
}
在该方法中完毕了Call.State(Internal)和Call.State(TeleService)的转换,重点关注doUpdate()方法,关键代码例如以下:
private void doUpdate(boolean fullUpdate, List<Call> 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.CONFERENCEDif (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// 主动呼叫即MTOUTGOING;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)转换成InCallStateInCallState newState = getPotentialStateFromCallList(callList);newState = startOrFinishUi(newState);//... ...省略
}
继续查看getPotentialStateFromCallList()方法,例如以下:
public static InCallState getPotentialStateFromCallList(CallList callList) {//InCallState默认状态位NO_CALLSInCallState newState = InCallState.NO_CALLS;//假设calllist为null返回默认状态NO_CALLSif (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<Integer, Call> 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

本文涉及图片以及各资源,免积分下载。点这里。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/459309.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

米家对讲机_对前面两代产品不断总结和完善的产物,米家对讲机2代开箱体验...

4月1日&#xff0c;小米终于发布了旗下对讲机产品的2代&#xff1a;米家对讲机2。憋了两年后&#xff0c;可见米家对讲机团队针对前期米家对讲机一代以及1S很多用户反应的问题还是非常重视的&#xff0c;所以这次的米家对讲机2代基本上改进了前期存在的不足&#xff0c;补齐了前…

【MFC】vs2013_MFC使用文件之15.mfc 按钮CBitmapButton的使用

本文是基于对话框的 博文基于 无幻 的博文为基础写的 http://blog.csdn.net/akof1314/article/details/4951836 笔者使用mfc撑死2个星期&#xff0c;不过这是有c基础的前提下接触mfc&#xff0c;昨天看到了网上对qt creator的评论&#xff0c;感觉好高大上&#xff0c;回去试了…

C语言存储空间布局以及static解析

本文我将采用Linux环境测试C语言存储空间布局&#xff0c;以及采用VC6.0来测试static的常见用法。采用linux环境来测试c语言存储空间布局&#xff0c;是因为Linux很容易利用shell命令中的size命令查看到进程存储区各段的大小。采用VC6.0来测试static的常见用法&#xff0c;是因…

老李推荐:第6章6节《MonkeyRunner源码剖析》Monkey原理分析-事件源-事件源概览-命令队列...

老李推荐&#xff1a;第6章6节《MonkeyRunner源码剖析》Monkey原理分析-事件源-事件源概览-命令队列事件源在获得字串命令并把它翻译成对应的MonkeyEvent事件后&#xff0c;会把这些事件排队放入一个由事件源维护的队列&#xff0c;然后其他地方如Monkey类的runMonkeyCycles方法…

华为手机怎么设置应用不全屏显示_手机投屏智能电视画面比例不合适怎么办?...

手机投屏到电视的比例不对怎么办知乎上有网友私下&#xff1a;“苹果手机屏幕镜像投屏到电视上&#xff0c;画面不能全屏&#xff0c;然后在网上查了一下是因为显示比例的问题&#xff0c;请问怎么解决&#xff1f;”这个问题说简单也简单&#xff0c;说难也难。说简单是因为想…

手把手教你使用CocoaPods管理你的iOS第三方开源类库

手把手教你使用CocoaPods管理你的iOS第三方开源类库 本文转载自&#xff1a;http://kittenyang.com/cocoapods 鉴于我开这个博客的初衷是记录自己平时的技术积累&#xff0c;而我平时又属研究iOS最多&#xff0c;因此这个博客在一定程度上可以说是以iOS技术为主的博客。既然研究…

alt复制选区就会卡 ps_PS入门视频教程笔记整理(二)工具栏介绍一

这几期会慢慢的更&#xff0c;工具栏的相关介绍还有一些简单有趣的应用~1、移动工具和画板工具 (1)移动工具选择相应的图层进行拖拽移动的操作■自动选择&#xff1a;不勾选的话——只有一个图层被选中(移动当前所选择的图层里的内容)勾选的话——无论你点击哪一个地方进行拖拽…

测试Markdown

一级标题 二级标题 四级标题 这是高阶标题&#xff08;和一级标题效果一样&#xff09; 这是次阶标题&#xff08;等同二阶标题&#xff09; 无序列表 *1 *2 *3 无序列表 -1 -2 -3 有序列表 1.你大爷 2.你大伯 3.你叔 4、你哥 这是一个引用 第二个引用 第三个…

MyBatis collection的两种形式——MyBatis学习笔记之九

与association一样&#xff0c;collection元素也有两种形式&#xff0c;现介绍如下&#xff1a; 一、嵌套的resultMap 实际上以前的示例使用的就是这种方法&#xff0c;今天介绍它的另一种写法。还是以教师映射为例&#xff0c;修改映射文件TeacherMapper.xml如下&#xff08;点…

int linux 原子操作_linux c++编程之多线程:原子操作如何解决线程冲突

在多线程中操作全局变量一般都会引起线程冲突&#xff0c;为了解决线程冲突&#xff0c;引入原子操作。1.线程冲突#include #include #include #include int g_count 0;void count(void *p){Sleep(100); //do some work//每个线程把g_count加1共10次for (int i 0; i < …

python查询斐波那契数列通项公式_分享一个神奇的操作系统——斐波那契+MACD,每一波都有20%以上的收益!...

斐波那契数列&#xff0c;又称兔子数列&#xff0c;或者黄金分割数列。指的是这样一个数列&#xff1a;0、1、1、2、3、5、8、13、21……从第三项起&#xff0c;它的每一项都等于前两项的和。为什么是兔子数列?我们假设兔子在出生两个月后&#xff0c;就有繁殖能力&#xff0c…

FPGA 状态机设计

数字系统有两大类有限状态机&#xff08;Finite State Machine&#xff0c;FSM&#xff09;&#xff1a;Moore状态机和Mealy状态机。 Moore状态机 其最大特点是输出只由当前状态确定&#xff0c;与输入无关。Moore状态机的状态图中的每一个状态都包含一个输出信号。这是一个典型…

WCF 初识(一)

WCF的前世今生 在.NETFramework 2.0以及前版本中&#xff0c;微软发展了Web Service&#xff08;SOAP with HTTP communication&#xff09;&#xff0c;.NET Remoting&#xff08;TCP/HTTP/Pipeline communication&#xff09;以及基础的Winsock等通信支持。 由于各个通信方法…

C/C++关键字解析

2、C/C分别有多少个关键字&#xff1f; 假如别人问某一个关键字是否属于C/C&#xff0c;要能正确的答出来。 1&#xff09;由ANSI标准定义的C语言关键字共32个 auto double int struct break else long switch case enum register typedef char extern return union const flo…

Linux系统:软链接与硬链接的原理分析

1、相关概念 1、索引节点inode(index node)&#xff1a;inode就是索引节点&#xff0c;它用来存放档案及目录的基本信息&#xff0c;包含时间、档名、使用者及群组等。 inode 是 UNIX/Linux 操作系统中的一种数据结构&#xff0c;其本质是结构体它包含了与文件系统中各个文件相…

操作系统:虚拟页式存储管理(缺页中断、页面置换算法)

1、基本工作原理 1、基本工作原理 在进程开始运行之前&#xff0c;不是全部装入页面&#xff0c;而是装入一个或者零个页面&#xff0c;之后根据进程运行的需要&#xff0c;动态装入其他页面&#xff1b;当内存已满&#xff0c;而又需要装入 新的页面时&#xff0c;则根据某种…

小程序 长按api_高质量的微信小程序样式模板应该长什么样?

现在不懂技术的小白若想快速制作自己的小程序&#xff0c;一般是通过小程序模板来实现。通过在模板上添加自己的图片、文字、商品等等&#xff0c;可以很简单地生成一个小程序。不过要想把小程序做得好看&#xff0c;你得找高质量的小程序样式模板才行。那么高质量的微信小程序…

web安全测试-AppScan使用分享

这里主要分享如何使用AppScan对一大项目的部分功能进行安全扫描。 ------------------------------------------------------------------------ 其实&#xff0c;对于安全方面的测试知道的甚少。因为那公司每个月要求对产品进行安全扫描。掌握了一人点使用技巧&#xff0c;所…

ros 开源物体检测_ROS kinetic + Realsens D435i + ORK + LINEMOD 物体识别

1. ORKORK (Object Recognition Kitchen) 是 ROS 集成的物体识别库&#xff0c;当前 Kinetic 版本的 ROS 只集成了部分功能包的二进制安装文件&#xff0c;所以需通过源码编译安装。安装依赖库sudo apt-get installmeshlabsudo apt-get install libosmesa6-devsudo apt-get ins…

华为荣耀6 H60-L02/L12(联通版)救砖包【适用于无限重启】

本帖最后由 HOT米粒 于 2014-11-16 20:43 编辑 华为荣耀6 H60-L02/L12&#xff08;联通版&#xff09;救砖包【适用于无限重启】说明&#xff1a; 1、本工具包用于华为荣耀6 H60-L02&#xff08;联通版&#xff09;&#xff1b; 2、本工具适用于在Honor Logo 无限重启的童鞋恢复…