当调用getCurrentCalls方法查询Call List当前所有的通话连接,查询的结果是交给handlePollCalls来处理的。handlePollCalls方法比较长,我们把它分解成几部分来分析:
@Override
protected void handlePollCalls(AsyncResult ar) {
1.初始化操作,获取Call List。
2.更新通话的相关信息。
2.1 获取conn对象和dc对象。
2.2 通话状态变化。
2.2.1 出现新的通话连接。
2.2.2 通话连接断开。
2.2.3 通话状态发生变化。
3.收尾处理,根据最新的通话状态发出消息。
4.更新手机的状态。
}
1.初始化操作,获取Call List。
//声明Call List对象,它将保存RIL查询出的当前Call对象列表
List polledCalls;
//RIL返回的消息无异常,即getCurrentCalls查询当前Call List成功
if (ar.exception == null) {
//将RIL返回的消息结果对象强制转换成List对象
polledCalls = (List)ar.result;
} else if (isCommandExceptionRadioNotAvailable(ar.exception)) {
// just a dummy empty ArrayList to cause the loop
// to hang up all the calls
polledCalls = new ArrayList();
} else {
// Radio probably wasn't ready--try again in a bit
// But don't keep polling if the channel is closed
pollCallsAfterDelay();
return;
}
Connection newRinging = null; //创建通话连接对象
Connection newUnknown = null;
//除了通话连接断开的其他任何通话状态改变的标志
boolean hasNonHangupStateChanged = false; // Any change besides
// a dropped connection
boolean hasAnyCallDisconnected = false;
//是否需要延迟查询Call List的标志
boolean needsPollDelay = false;
//是否出现未知通话连接的标志
boolean unknownConnectionAppeared = false;
上面代码中把RILJ发给CDMACallTracker的Handler消息中的result通过强制转换成List对象,然后赋值给polledCalls对象的。
本文来自 http://blog.csdn.net/linyongan ,转载请务必注明出处。
2.更新通话的相关信息。
CDMACallTracker通过Handler消息获取最新的DriverCall List对象列表,然后遍历mConnections保存的通话连接对象列表,通过DriverCall List中对应的DriverCall对象更新CDMACallTracker中的通话相关信息。
2.1 获取conn对象和dc对象。
for (int i = 0, curDC = 0, dcSize = polledCalls.size()
; i < mConnections.length; i++) {
//依次获取connections中的对象
CdmaConnection conn = mConnections[i];
DriverCall dc = null;
// polledCall list is sparse
if (curDC < dcSize) {
dc = (DriverCall) polledCalls.get(curDC);
//DriverCall对象与connections中对象的关系,通过下标对应
if (dc.index == i+1) {
//conn与dc对象匹配成功,下次循环则获取下一个dc对象
curDC++;
} else {
//conn与dc对象未匹配成功,则不取出dc对象
dc = null;
}
}
if (DBG_POLL) log("poll: conn[i=" + i + "]=" + conn+", dc=" + dc);
根据下标i的值获取mConnections数组中的CdmaConnection对象conn,并且通过curDC的值从polledCalls列表中获取DriverCall对象dc,这两个对象通过dc.index == i+1完成匹配。(因为index 从1开始,而i从0开始的)
2.2 通话状态变化。
2.2.1 出现新的通话连接。
//conn代表旧的通话连接的基本信息,dc代表新的。出现新的通话连接
if (conn == null && dc != null) {
//主动发起拨号请求后,第一次查询到Call List后,进入这里
// Connection appeared in CLCC response that we don't know about
if (mPendingMO != null && mPendingMO.compareTo(dc)) {
if (DBG_POLL) log("poll: pendingMO=" + mPendingMO);
// It's our pending mobile originating call
mConnections[i] = mPendingMO;
//把i的值赋值给index
mPendingMO.mIndex = i;
mPendingMO.update(dc);
mPendingMO = null;
// Someone has already asked to hangup this call
if (mHangupPendingMO) {
mHangupPendingMO = false;
// Re-start Ecm timer when an uncompleted emergency call ends
if (mIsEcmTimerCanceled) {
handleEcmTimer(CDMAPhone.RESTART_ECM_TIMER);
}
try {
if (Phone.DEBUG_PHONE) log(
"poll: hangupPendingMO, hangup conn " + i);
hangup(mConnections[i]);
} catch (CallStateException ex) {
Rlog.e(LOG_TAG, "unexpected error on hangup");
}
// Do not continue processing this poll
// Wait for hangup and repoll
return;
}
} else {//接收到新来电
if (Phone.DEBUG_PHONE) {
log("pendingMo=" + mPendingMO + ", dc=" + dc);
}
//通过dc创建新的Connection对象
mConnections[i] = new CdmaConnection(mPhone.getContext(), dc, this, i);
Connection hoConnection = getHoConnection(dc);
if (hoConnection != null) {
// Single Radio Voice Call Continuity (SRVCC) completed
mConnections[i].migrateFrom(hoConnection);
// Updating connect time for silent redial cases (ex: Calls are transferred
// from DIALING/ALERTING/INCOMING/WAITING to ACTIVE)
if (hoConnection.mPreHandoverState != CdmaCall.State.ACTIVE &&
hoConnection.mPreHandoverState != CdmaCall.State.HOLDING) {
mConnections[i].onConnectedInOrOut();
}
mHandoverConnections.remove(hoConnection);
mPhone.notifyHandoverStateChanged(mConnections[i]);
} else {
// find if the MT call is a new ring or unknown connection
//把mConnections对象赋值给newRinging
newRinging = checkMtFindNewRinging(dc,i);
if (newRinging == null) {
unknownConnectionAppeared = true;
newUnknown = mConnections[i];
}
}
checkAndEnableDataCallAfterEmergencyCallDropped();
}
hasNonHangupStateChanged = true;
}
2.2.2 通话连接断开。
else if (conn != null && dc == null) {
//通话连接断开
// This case means the RIL has no more active call anymore and
// we need to clean up the foregroundCall and ringingCall.
// Loop through foreground call connections as
// it contains the known logical connections.
int count = mForegroundCall.mConnections.size();
for (int n = 0; n < count; n++) {
if (Phone.DEBUG_PHONE) log("adding fgCall cn " + n + " to droppedDuringPoll");
CdmaConnection cn = (CdmaConnection)mForegroundCall.mConnections.get(n);
//把旧的conn对象添加到删除通话连接列表中
mDroppedDuringPoll.add(cn);
}
count = mRingingCall.mConnections.size();
// Loop through ringing call connections as
// it may contain the known logical connections.
for (int n = 0; n < count; n++) {
if (Phone.DEBUG_PHONE) log("adding rgCall cn " + n + " to droppedDuringPoll");
CdmaConnection cn = (CdmaConnection)mRingingCall.mConnections.get(n);
mDroppedDuringPoll.add(cn);
}
mForegroundCall.setGeneric(false);
mRingingCall.setGeneric(false);
// Re-start Ecm timer when the connected emergency call ends
if (mIsEcmTimerCanceled) {
handleEcmTimer(CDMAPhone.RESTART_ECM_TIMER);
}
// If emergency call is not going through while dialing
checkAndEnableDataCallAfterEmergencyCallDropped();
// Dropped connections are removed from the CallTracker
// list but kept in the Call list
//设置旧的通话连接为null
mConnections[i] = null;
}
2.2.3 通话状态发生变化。
else if (conn != null && dc != null) { /* implicit conn.compareTo(dc) */
//通话状态发生变化
// Call collision case
if (conn.isIncoming() != dc.isMT) {
if (dc.isMT == true){
// Mt call takes precedence than Mo,drops Mo
mDroppedDuringPoll.add(conn);
// find if the MT call is a new ring or unknown connection
newRinging = checkMtFindNewRinging(dc,i);
if (newRinging == null) {
unknownConnectionAppeared = true;
newUnknown = conn;
}
checkAndEnableDataCallAfterEmergencyCallDropped();
} else {
// Call info stored in conn is not consistent with the call info from dc.
// We should follow the rule of MT calls taking precedence over MO calls
// when there is conflict, so here we drop the call info from dc and
// continue to use the call info from conn, and only take a log.
Rlog.e(LOG_TAG,"Error in RIL, Phantom call appeared " + dc);
}
} else {
boolean changed;
//通过dc对象更新conn对象,返回通话状态是否已经更新的标志
changed = conn.update(dc);
hasNonHangupStateChanged = hasNonHangupStateChanged || changed;
}
}
3.收尾处理,根据最新的通话状态发出消息。
//如果接收到来电请求
if (newRinging != null) {
mPhone.notifyNewRingingConnection(newRinging);
}
// clear the "local hangup" and "missed/rejected call"
// cases from the "dropped during poll" list
// These cases need no "last call fail" reason
for (int i = mDroppedDuringPoll.size() - 1; i >= 0 ; i--) {
CdmaConnection conn = mDroppedDuringPoll.get(i);
if (conn.isIncoming() && conn.getConnectTime() == 0) {
// Missed or rejected call
int cause;
if (conn.mCause == DisconnectCause.LOCAL) {
cause = DisconnectCause.INCOMING_REJECTED;
} else {
cause = DisconnectCause.INCOMING_MISSED;
}
if (Phone.DEBUG_PHONE) {
log("missed/rejected call, conn.cause=" + conn.mCause);
log("setting cause to " + cause);
}
mDroppedDuringPoll.remove(i);
hasAnyCallDisconnected |= conn.onDisconnect(cause);
} else if (conn.mCause == DisconnectCause.LOCAL
|| conn.mCause == DisconnectCause.INVALID_NUMBER) {
mDroppedDuringPoll.remove(i);
hasAnyCallDisconnected |= conn.onDisconnect(conn.mCause);
}
}
/* Disconnect any pending Handover connections */
for (Iterator<Connection> it = mHandoverConnections.iterator();
it.hasNext();) {
Connection hoConnection = it.next();
log("handlePollCalls - disconnect hoConn= " + hoConnection);
((ImsPhoneConnection)hoConnection).onDisconnect(DisconnectCause.NOT_VALID);
it.remove();
}
//(mDroppedDuringPoll中仍然存在通话连接消失的对象,也就是还有远程断开通话连接对象
// Any non-local disconnects: determine cause
if (mDroppedDuringPoll.size() > 0) {
//向RIL层查询最后一次通话连接断开的原因
mCi.getLastCallFailCause(
obtainNoPollCompleteMessage(EVENT_GET_LAST_CALL_FAIL_CAUSE));
}
if (needsPollDelay) {
pollCallsAfterDelay();
}
// Cases when we can no longer keep disconnected Connection's
// with their previous calls
// 1) the phone has started to ring
// 2) A Call/Connection object has changed state...
// we may have switched or held or answered (but not hung up)
if (newRinging != null || hasNonHangupStateChanged || hasAnyCallDisconnected) {
internalClearDisconnected();
}
//同步更新Phone.State手机状态
updatePhoneState();
//出现未知的通话连接
if (unknownConnectionAppeared) {
mPhone.notifyUnknownConnection(newUnknown);
}
//非通话连接断开的情况或者是接收到新的来电请求
if (hasNonHangupStateChanged || newRinging != null || hasAnyCallDisconnected) {
mPhone.notifyPreciseCallStateChanged();
}
4.更新手机的状态。
private void updatePhoneState() {
//首先记录之前的状态
PhoneConstants.State oldState = mState;
//根据三个mState状态,更新Phone.State手机状态
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) {
mVoiceCallEndedRegistrants.notifyRegistrants(
new AsyncResult(null, null, null));
}
//如果通话状态转变为非待机状态,则发起通知,设置数据连接不可用
else if (oldState == PhoneConstants.State.IDLE && oldState != mState) {
mVoiceCallStartedRegistrants.notifyRegistrants (
new AsyncResult(null, null, null));
}
if (Phone.DEBUG_PHONE) {
log("update phone state, old=" + oldState + " new="+ mState);
}
//手机状态已经更新,通过Phone对象发出状态变化通知
if (mState != oldState) {
mPhone.notifyPhoneStateChanged();
}
}
通过Call对象获取其State状态从而更新Phone.State状态,它们之间的状态是保持同步的。
Call.State和Phone.State之间的状态转换过程如下:
可以看到Call.State共有9个状态,Phone.State共有3个状态;Call.State中,除去IDLE和INCOMING之外的7个状态,提炼成Phone.State的OFFHOOK。