每次收到底层上报的呼叫状态变化时(UNSOL_RESPONSE_CALL_STATE_CHANGED ),都会去查询变化了的具体内容给modem发的信息是RIL_REQUEST_GET_CALL_LIST.handlePollCalls是用来处理查询之后的结果,分析如下:
handlePollCalls(AsyncResult ar)
List polledCalls;
log("****handlepollcalls.....");
if (ar.exception == null) {
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(bIsvoice);
return;
}
Connection newRinging = null; //or waiting
boolean hasNonHangupStateChanged = false; // Any change besides
// a dropped connection
boolean needsPollDelay = false;
boolean unknownConnectionAppeared = false;
boolean isSeparateCallConnection = false;
for (int i = 0, curDC = 0, dcSize = polledCalls.size()
; i < connections.length; i++) {
GsmConnection conn = connections[i];//一个个查询gsmcalltracker里面的连接
DriverCall dc = null;
// polledCall list is sparse
if (curDC < dcSize) {
dc = (DriverCall) polledCalls.get(curDC);
if (dc.index == i+1) {
curDC++;
} else if(isSeparateCallConnection) {
curDC++;
isSeparateCallConnection = false;
} else {
dc = null;
}
}
if (DBG_POLL) log("poll: conn[i=" + i + "]=" +
conn+", dc=" + dc);
log("****poll: conn[i=" + i + "]=" +
conn+", dc=" + dc);
if (conn == null && dc != null) {//进来一个新的呼叫
// Connection appeared in CLCC response that we don't know about
if (pendingMO != null && pendingMOpareTo(dc)) {
if (DBG_POLL) log("poll: pendingMO=" + pendingMO);
// It's our pending mobile originating call
connections[i] = pendingMO;
pendingMO.index = i;
pendingMO.update(dc);
pendingMO = null;
// Someone has already asked to hangup this call
if (hangupPendingMO) {
hangupPendingMO = false;
try {
if (Phone.DEBUG_PHONE) log(
"poll: hangupPendingMO, hangup conn " + i);
hangup(connections[i]);
} catch (CallStateException ex) {
Log.e(LOG_TAG, "unexpected error on hangup");
}
// Do not continue processing this poll
// Wait for hangup and repoll
return;
}
} else {
connections[i] = new GsmConnection(phone.getContext(), dc, this, i);
log("****setting connection");
// it's a ringing call
if (connections[i].getCall() == ringingCall) {
log("****new ringing connection");
newRinging = connections[i];
} else {
// Something strange happened: a call appeared
// which is neither a ringing call or one we created.
// Either we've crashed and re-attached to an existing
// call, or something else (eg, SIM) initiated the call.
Log.i(LOG_TAG,"Phantom call appeared " + dc);
// If it's a connected call, set the connect time so that
// it's non-zero. It may not be accurate, but at least
// it won't appear as a Missed Call.
if (dc.state != DriverCall.State.ALERTING
&& dc.state != DriverCall.State.DIALING) {
connections[i].connectTime = System.currentTimeMillis();
}
unknownConnectionAppeared = true;
}
}
<span style="color:#000099;"><strong>hasNonHangupStateChanged = true;</strong></span>
} else if (conn != null && dc == null) {//modem呼叫断开时走这
// Connection missing in CLCC response that we were
// tracking.
log("****no connection let's drop");
if(conn.isVoice != bIsVoice) {
log("Different call came 1");
continue;
}
droppedDuringPoll.add(conn);
// Dropped connections are removed from the CallTracker
// list but kept in the GsmCall list
connections[i] = null;
} else if (conn != null && dc != null && !connpareTo(dc)) {
// Connection in CLCC response does not match what
// we were tracking. Assume dropped call and new call
log("****connection not matching let's drop");
if(conn.isVoice != dc.isVoice) {
log("Different call came 2");
curDC--;
isSeparateCallConnection = true;
continue;
}
droppedDuringPoll.add(conn);
connections[i] = new GsmConnection (phone.getContext(), dc, this, i);
if (connections[i].getCall() == ringingCall) {
newRinging = connections[i];
} // else something strange happened
hasNonHangupStateChanged = true;
} else if (conn != null && dc != null) { /* implicit connpareTo(dc)有连接且查询到的call也存在,正常外呼走这 */
boolean changed;
log("****conn dc both has value");
changed = conn.update(dc); //尝试更新连接,如果返回的值位changed,则表明查询前后call的状态有变化
hasNonHangupStateChanged = hasNonHangupStateChanged || changed;
log("****hasNonHangupStateChanged = "+hasNonHangupStateChanged);
}
if (REPEAT_POLLING) {
if (dc != null) {
// FIXME with RIL, we should not need this anymore
if ((dc.state == DriverCall.State.DIALING
/*&& cm.getOption(cm.OPTION_POLL_DIALING)*/)
|| (dc.state == DriverCall.State.ALERTING
/*&& cm.getOption(cm.OPTION_POLL_ALERTING)*/)
|| (dc.state == DriverCall.State.INCOMING
/*&& cm.getOption(cm.OPTION_POLL_INCOMING)*/)
|| (dc.state == DriverCall.State.WAITING
/*&& cm.getOption(cm.OPTION_POLL_WAITING)*/)
) {
// Sometimes there's no unsolicited notification
// for state transitions
bIsVoice = dc.isVoice;
needsPollDelay = true;
}
}
}
}
// This is the first poll after an ATD.
// We expect the pending call to appear in the list
// If it does not, we land here
if (pendingMO != null) {
Log.d(LOG_TAG,"Pending MO dropped before poll fg state:"
+ foregroundCall.getState());
droppedDuringPoll.add(pendingMO);
pendingMO = null;
hangupPendingMO = false;
}
if (newRinging != null) {//如果是新进来的呼叫
phone.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 = droppedDuringPoll.size() - 1; i >= 0 ; i--) { //本地原因引起的挂断</span>
GsmConnection conn = droppedDuringPoll.get(i);
log("****welcome to drop call");
if (conn.isIncoming() && conn.getConnectTime() == 0) {
// Missed or rejected call
Connection.DisconnectCause cause;
if (conn.cause == Connection.DisconnectCause.LOCAL) {//进来的call不接听
cause = Connection.DisconnectCause.INCOMING_REJECTED;
} else {
cause = Connection.DisconnectCause.INCOMING_MISSED;
}
if (Phone.DEBUG_PHONE) {
log("missed/rejected call, conn.cause=" + conn.cause);
log("setting cause to " + cause);
}
droppedDuringPoll.remove(i);
conn.onDisconnect(cause);
} else if (conn.cause == Connection.DisconnectCause.LOCAL) {//主动挂断
// Local hangup
log("****local hangup");
droppedDuringPoll.remove(i);
conn.onDisconnect(Connection.DisconnectCause.LOCAL);
} else if (conn.cause ==
Connection.DisconnectCause.INVALID_NUMBER) {//拨出的号码无效
log("****invalid hangup");
droppedDuringPoll.remove(i);
conn.onDisconnect(Connection.DisconnectCause.INVALID_NUMBER);
}
}
// Any non-local disconnects: determine cause如果有非本地原因要清除的连接
if (droppedDuringPoll.size() > 0) {
//bIsVoice = droppedDuringPoll.get(0).isVoice();
log("****dropping call bIsVoice = "+bIsVoice);
if(bIsVoice) {
cm.getLastCallFailCause(//先查找上次失败的原因
obtainNoPollCompleteMessage(EVENT_GET_LAST_CALL_FAIL_CAUSE));
} else {
cm.getLastVTCallFailCause(
obtainNoPollCompleteMessage(EVENT_GET_LAST_CALL_FAIL_CAUSE));
}
}
if (needsPollDelay) {
log("****needspolldelay");
pollCallsAfterDelay(bIsVoice);
}
// 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) {
log("****newringing and hasnonhangupstatechanged");
internalClearDisconnected();
}
updatePhoneState();//跟新gsmphone的状态
if (unknownConnectionAppeared) {
log("****unknown connection appeared");
phone.notifyUnknownConnection();
}
if (hasNonHangupStateChanged || newRinging != null) {
log("****hasnonhangupstatechanged and newringing");
phone.notifyPreciseCallStateChanged();
}
//dumpState();
}
GsmCallTracker.java里面维护了三个call对象
GsmCall ringingCall = new GsmCall(this);
// A call that is ringing or (call) waiting
GsmCall foregroundCall = new GsmCall(this);
GsmCall backgroundCall = new GsmCall(this);
call可以有如下几种状态:
IDLE, ACTIVE, HOLDING, DIALING, ALERTING, INCOMING, WAITING, DISCONNECTED, DISCONNECTING
的但是不是每一个call都可以拥有上面所有的状态.具体一个call对象可以拥有哪些状态可以查看parentFromDcstate函数
private GsmCall parentFromDCState (DriverCall.State state) {
664 switch (state) {
665 case ACTIVE:
666 case DIALING:
667 case ALERTING:
668 return owner.foregroundCall;
//foreground call can have ACTIVE/DIALING/ALERTING three states
669 //break;
670
671 case HOLDING:
672 return owner.backgroundCall;
673 //break;
674
675 case INCOMING:
676 case WAITING:
677 return owner.ringingCall;
678 //break;
679
680 default:
681 throw new RuntimeException("illegal call state: " + state);
682 }
683 }
三个call对象的介绍如下:
ringingCall:(用来管理处于INCOMING和WAITING状态的通话)//刚进来的电话初始化gsmconnection 时,call的状态会变成INCOMING
foregroundCall:(用来管理处于DAILING、ALERTING、ACTIVE状态的通话)
backgroundCall:(用来管理HOLD的通话)。
往外打的电话可以有DAILING,ALERTING,ACTIVE 三种状态
打进来的电话可以有INCOMING,WAITING,ACTIVE 三种状态
往modem发了挂断命令时call的状态变为DISCONNECTING(正在断开)。
手机的状态只有下面三种:IDLE, RINGING, OFFHOOK;
/**
* The phone state. One of the following:<p>
* <ul>
* <li>IDLE = no phone activity</li>
* <li>RINGING = a phone call is ringing or call waiting.
* In the later case, another call is active as well 有电话在在打进来或者等待接听</li>
* <li>OFFHOOK = The phone is off hook. At least one call
* exists that is dialing, active or holding and no calls are
* ringing or waiting. 有正在打出去的电话 or 已经接通了的call or 处于hold状态的call</li>
* </ul>
*/
一个call对象里面有一个连接数组
public ArrayList<Connection> connections = new ArrayList<Connection>();
GsmCallTracker里面也有一个connections数组,to record the call list and in GsmConnection ,it have a member GsmCall parent.
every time poll current call, the connections arry will updata it's state .根据返回的DriverCall.state来选择parent应该是ringing or foreground or background call,如果call的类型没切换,可以是变化了call 的状态.或者DriverCall.state和查询前一样,返回false
所以一个backgroundCall里面其实可以保存多个通话连接,只是暂时挂起。再比如,在开始呼叫时,gsmcalltracker会初始化一个gsmconnection
isIncoming = false;
isVoice = ct.isMOVoice;
createTime = System.currentTimeMillis();
this.parent = parent;//这里的parent就是call。且是foregroundCall。相反呼进来的电话是ringincall
parent.attachFake(this, GsmCall.State.DIALING);//将这个connection加入到call的connection数组成员中,且将parent的状态设置为DIALING
gsmcalltracker的构造函数里有
cm.registerForCallStateChanged(this, EVENT_CALL_STATE_CHANGE, null);
所以当ril.java每次接到底层上报的UNSOL_RESPONSE_CALL_STATE_CHANGED 时,就会给gsmcalltracker 发EVENT_CALL_STATE_CHANGE事件
这个事件的处理就是初始化查询当前的call的状态
protected void pollCallsWhenSafe() {
needsPoll = true;
if (checkNoOperationsPending()) {
lastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT);//当查询完call list的状态后返回到calltracker的事件
cm.getCurrentCalls(lastRelevantPoll);//调用ril实例查询call的状态
}
}
查询完后给gsmcalltracker发的事件EVENT_POLL_CALLS_RESULT,其处理就是 handlePollCalls函数。
挂断电话时分不同的call对象,如果电话已经接通了则RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND
如果是挂断正在打进来的电话则用RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND
对于MT voice call
在PhoneBase.java文件里有
mCM.setOnCallRing(this, EVENT_CALL_RING, null);
当ril接到UNSOL_CALL_RING 时,就会报告EVENT_CALL_RING这个事件给GsmPhone 。其处理为notifyIncomingRing
private void notifyIncomingRing() {
if (!mIsVoiceCapable)
return;
AsyncResult ar = new AsyncResult(null, this, null);
mIncomingRingRegistrants.notifyRegistrants(ar);//通知注册过次事件的模块
}
在CallManager.java里有
phone.registerForIncomingRing(mHandler, EVENT_INCOMING_RING, null);
在callmanager里的处理也是通知注册次事件的模块
case EVENT_INCOMING_RING:
if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_INCOMING_RING)");
// The event may come from RIL who's not aware of an ongoing fg call
if (!hasActiveFgCall()) {
mIncomingRingRegistrants.notifyRegistrants((AsyncResult) msg.obj);
}
InCallScreen.java里有
mCM.registerForIncomingRing(mHandler, PHONE_INCOMING_RING, null);
mCM是CallManager的实例
上层如果接听了,则会调用gsmphone里的acceptCall-->gamcalltracker.acceptCall函数来发RIL_REQUEST_ANSWER给modem
更多推荐
voice call relevant
发布评论