在Android8.0中的Call.java有三个地方
frameworks\opt\telephony\src\java\com\android\internal\telephony
telephoney frameworks
frameworks\base\telecomm\java\android\telecom
telecomm frameworks
packages\services\Telecomm\src\com\android\server\telecom
telecom services
在Android8.0上取消了InCallUI 中的Call
这里我们从telephoney frameworks开始分析
1.telephoney frameworks Call的初始化
在Phone的初始化过程中,会对GsmCdmaCallTraker进行初始化。
GsmCdmaCallTraker过程中,会创建三个GsmCdmaCall对象,并监听CallStateChanged消息
GsmCdmaCall对象继承自telephoney frameworks Call对象
//Call对象
public GsmCdmaCall mRingingCall = new GsmCdmaCall(this);
// A call that is ringing or (call) waiting
public GsmCdmaCall mForegroundCall = new GsmCdmaCall(this);
public GsmCdmaCall mBackgroundCall = new GsmCdmaCall(this);
//Connection对象
public GsmCdmaConnection[] mConnections;
2.telephoney frameworks Call的状态
九种状态,又分为三类
frameworks\opt\telephony\src\java\com\android\internal\telephony
public enum State {
IDLE, ACTIVE, HOLDING, DIALING, ALERTING, INCOMING, WAITING, DISCONNECTED, DISCONNECTING;
public boolean isAlive() {
return !(this == IDLE || this == DISCONNECTED || this == DISCONNECTING);
}
public boolean isRinging() {
return this == INCOMING || this == WAITING;
}
public boolean isDialing() {
return this == DIALING || this == ALERTING;
}
}
3.telephoney frameworks Call 状态的获取及更新
当modem的Call状态发生变化后会发送CallStateChanged通知到RIL,继续上报到GsmCdmaCallTracker,之后GsmCdmaCallTracker的hangdleMessage方法处理此消息,
继续调用PollCallsWhenSafe方法,然后下方请求到RIL在到Modem查询GetCurrentCalls,查询的结果主要是DriverCall返回到GsmCdmaCallTracker,接着调用 handlePollCalls 方法进行处理。
具体详细的代码跟踪流程可以见Android8.0 来电分析流程一的1-9步骤
这里我们主要分析handlePollCalls是如何利用查询返回的DriverCall集合更新Call的状态
@Override
protected synchronized void handlePollCalls(AsyncResult ar) {
List polledCalls;
if (ar.exception == null) {
//获取返回的数据
polledCalls = (List)ar.result;
}
Connection newRinging = null; //or waiting
ArrayList<Connection> newUnknownConnectionsGsm = new ArrayList<Connection>();
Connection newUnknownConnectionCdma = null;
boolean hasNonHangupStateChanged = false; // Any change besides
// a dropped connection
boolean hasAnyCallDisconnected = false;
boolean needsPollDelay = false;
boolean unknownConnectionAppeared = false;
int handoverConnectionsSize = mHandoverConnections.size();
//CDMA
boolean noConnectionExists = true;
for (int i = 0, curDC = 0, dcSize = polledCalls.size()
; i < mConnections.length; i++) {
GsmCdmaConnection conn = mConnections[i];
DriverCall dc = null;
// polledCall list is sparse
if (curDC < dcSize) {
//解析返回的数据
dc = (DriverCall) polledCalls.get(curDC);
//返回数据dc的下标比conn的下标大一,可以搜索AT< +CLCC其通话下标是从1开始计数,而conn则是从零开始
//只要满足dc.index == i+1 的条件,则当前conn的对象是由dc对象创建的,只要dc发生了变化,conn也需要做对应的调整
if (dc.index == i+1) {
curDC++;
} else {
dc = null;
}
}
//conn可以看作是老的通话连接的基本信息,dc可以看作是新的通话连接的基本信息
//出现新的通话连接
//老通话conn不存在,出现新通话dc信息
if (conn == null && dc != null) {
// Connection appeared in CLCC response that we don't know about
//MO
if (mPendingMO != null && mPendingMO.compareTo(dc)) {
if (DBG_POLL) log("poll: pendingMO=" + mPendingMO);
// It's our pending mobile originating call
mConnections[i] = mPendingMO;
mPendingMO.mIndex = i;
mPendingMO.update(dc);
mPendingMO = null;
// Someone has already asked to hangup this call
if (mHangupPendingMO) {
...
//其他操作导致已经挂断了此通话
...
}
} else {
//MT
if (Phone.DEBUG_PHONE) {
log("pendingMo=" + mPendingMO + ", dc=" + dc);
}
//通过dc对象创建connection
mConnections[i] = new GsmCdmaConnection(mPhone, dc, this, i);
Connection hoConnection = getHoConnection(dc);
if (hoConnection != null) {
//handover切换
// 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 != GsmCdmaCall.State.ACTIVE &&
hoConnection.mPreHandoverState != GsmCdmaCall.State.HOLDING &&
dc.state == DriverCall.State.ACTIVE) {
mConnections[i].onConnectedInOrOut();
}
mHandoverConnections.remove(hoConnection);
if (isPhoneTypeGsm()) {
for (Iterator<Connection> it = mHandoverConnections.iterator();
it.hasNext(); ) {
Connection c = it.next();
Rlog.i(LOG_TAG, "HO Conn state is " + c.mPreHandoverState);
if (c.mPreHandoverState == mConnections[i].getState()) {
it.remove();
}
}
}
mPhone.notifyHandoverStateChanged(mConnections[i]);
} else {
//创建来电Connection
// find if the MT call is a new ring or unknown connection
newRinging = checkMtFindNewRinging(dc,i);
if (newRinging == null) {
unknownConnectionAppeared = true;
if (isPhoneTypeGsm()) {
newUnknownConnectionsGsm.add(mConnections[i]);
} else {
newUnknownConnectionCdma = mConnections[i];
}
}
}
}
//标志置为非挂断状态
hasNonHangupStateChanged = true;
}
//通话连接断开
//1.通过mDroppedDuringPoll处理
//如果掉话了则将其加入到删除通话连接列表中mDroppedDuringPoll.add(cn);
//2.设置旧的通话连接为null,与dc匹配
//mConnections[i] = null;
else if (conn != null && dc == null) {
if (isPhoneTypeGsm()) {
// Connection missing in CLCC response that we were
// tracking.
mDroppedDuringPoll.add(conn);
} else {
// 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");
GsmCdmaConnection cn = (GsmCdmaConnection)mForegroundCall.mConnections.get(n);
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");
GsmCdmaConnection cn = (GsmCdmaConnection)mRingingCall.mConnections.get(n);
mDroppedDuringPoll.add(cn);
}
// Re-start Ecm timer when the connected emergency call ends
if (mIsEcmTimerCanceled) {
handleEcmTimer(GsmCdmaPhone.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
mConnections[i] = null;
}
//通话连接断开并有新的来电
//
else if (conn != null && dc != null && !conn.compareTo(dc) && isPhoneTypeGsm()) {
// Connection in CLCC response does not match what
// we were tracking. Assume dropped call and new call
//通话连接断开处理
mDroppedDuringPoll.add(conn);
mConnections[i] = new GsmCdmaConnection (mPhone, dc, this, i);
if (mConnections[i].getCall() == mRingingCall) {
//新的来电,初始化newRinging连接
newRinging = mConnections[i];
} // else something strange happened
hasNonHangupStateChanged = true;
}
//通话状态发生变化
else if (conn != null && dc != null) { /* implicit conn.compareTo(dc) */
// Call collision case
if (!isPhoneTypeGsm() && 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;
newUnknownConnectionCdma = 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.
if(Build.IS_SHOW_LOG){
Rlog.e(LOG_TAG,"Error in RIL, Phantom call appeared " + dc);
}
}
} else {
boolean changed;
//通过dc对象更新conn对象,返回状态标志是否已经更新
changed = conn.update(dc);
hasNonHangupStateChanged = hasNonHangupStateChanged || changed;
}
}
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
needsPollDelay = true;
}
}
}
}
//收尾工作
// Safety check so that obj is not stuck with mIsInEmergencyCall set to true (and data
// disabled). This should never happen though.
if (!isPhoneTypeGsm() && noConnectionExists) {
checkAndEnableDataCallAfterEmergencyCallDropped();
}
// 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 (mPendingMO != null) {
Rlog.d(LOG_TAG, "Pending MO dropped before poll fg state:"
+ mForegroundCall.getState());
mDroppedDuringPoll.add(mPendingMO);
mPendingMO = null;
mHangupPendingMO = false;
if (!isPhoneTypeGsm()) {
if( mPendingCallInEcm) {
mPendingCallInEcm = false;
}
checkAndEnableDataCallAfterEmergencyCallDropped();
}
}
//响铃通知
if (newRinging != null) {
mPhone.notifyNewRingingConnection(newRinging);
}
//根据mDroppedDuringPoll进行挂断处理
// clear the "local hangup" and "missed/rejected call"
// cases from the "dropped during poll" list
// These cases need no "last call fail" reason
ArrayList<GsmCdmaConnection> locallyDisconnectedConnections = new ArrayList<>();
for (int i = mDroppedDuringPoll.size() - 1; i >= 0 ; i--) {
GsmCdmaConnection conn = mDroppedDuringPoll.get(i);
//CDMA
boolean wasDisconnected = false;
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);
wasDisconnected = true;
locallyDisconnectedConnections.add(conn);
} else if (conn.mCause == DisconnectCause.LOCAL
|| conn.mCause == DisconnectCause.INVALID_NUMBER) {
mDroppedDuringPoll.remove(i);
hasAnyCallDisconnected |= conn.onDisconnect(conn.mCause);
wasDisconnected = true;
locallyDisconnectedConnections.add(conn);
}
if (!isPhoneTypeGsm() && wasDisconnected && unknownConnectionAppeared
&& conn == newUnknownConnectionCdma) {
unknownConnectionAppeared = false;
newUnknownConnectionCdma = null;
}
}
...
//更新phone状态
if (VDBG) log("handlePollCalls calling updatePhoneState()");
updatePhoneState();
...
}
这里我们可以看到其通过底层返回的 DriverCall 和 Connection进行对比以判断通话状态的更改,然后更改Call的State,最后更改Phone的state,并发出通知.
这里我们分别跟进 GsmCdmaConnection 和 GsmCdmaCall 中。
可以看到GsmCdmaCall中有一组connection对象,并且有attch及相关管理方法
GsmCdmaConnection中有一个GsmCdmaCall mParent 实例。
观察其构造方法,可以看到其在创建的时候会先从dc状态中获取parent对象,在将自身和dc添加到mParent中。
mParent = parentFromDCState(dc.state);
mParent.attach(this, dc);
继续跟进parentFromDCState
protected GsmCdmaCall
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);
}
}
可以看到其根据dcState的不同返回不同的GsmCdmaCall实例。从这里也可以看到GsmCdmaCallTracker中三个Call对象对应的状态。
所以状态发生的流程总结如下:
获取底层返回的Dc对象,解析,对比Connection,之后更新Connection对应的parent的Call对象,最后更新Phone状态。
Telecom Services的Call对象
在之前的MO,MT流程的分析中提到过,会先创建一个Call,然后调用startCreateConnection去创建Connection具体可以查看去电流程分析二部分
跟进CallsManager中的createCallForExistingConnection方法
Call createCallForExistingConnection(String callId, ParcelableConnection connection) {
boolean isDowngradedConference = (connection.getConnectionProperties()
& Connection.PROPERTY_IS_DOWNGRADED_CONFERENCE) != 0;
...
setCallState(call, Call.getStateFromConnectionState(connection.getState()),
"existing connection");
...
}
观察其先调用connection.getState方法,在调用getStateFromConnection,之后进行setCallState完成CallState的设置。在之前来电和去电的分析中我们知道,
此Connection其实是远程调用telephony frameworks的Connection
1.从telephony frameworks的Connection中获取CallState.
public Call.State getState() {
Call c;
c = getCall();
if (c == null) {
return Call.State.IDLE;
} else {
return c.getState();
}
}
这就获取到了telephony的Call是State
2.继续查看Call.getStateFromConnectionState
static int getStateFromConnectionState(int state) {
switch (state) {
case Connection.STATE_INITIALIZING:
return CallState.CONNECTING;
case Connection.STATE_ACTIVE:
return CallState.ACTIVE;
case Connection.STATE_DIALING:
return CallState.DIALING;
case Connection.STATE_PULLING_CALL:
return CallState.PULLING;
case Connection.STATE_DISCONNECTED:
return CallState.DISCONNECTED;
case Connection.STATE_HOLDING:
return CallState.ON_HOLD;
case Connection.STATE_NEW:
return CallState.NEW;
case Connection.STATE_RINGING:
return CallState.RINGING;
}
return CallState.DISCONNECTED;
}
通过转换获取telecomm services中Call对应的State
3.继续跟进CallsManager中的setCallState方法
private void setCallState(Call call, int newState, String tag) {
if (call == null) {
return;
}
int oldState = call.getState();
Log.i(this, "setCallState %s -> %s, call: %s", CallState.toString(oldState),
CallState.toString(newState), call);
if (newState != oldState) {
// Unfortunately, in the telephony world the radio is king. So if the call notifies
// us that the call is in a particular state, we allow it even if it doesn't make
// sense (e.g., STATE_ACTIVE -> STATE_RINGING).
// TODO: Consider putting a stop to the above and turning CallState
// into a well-defined state machine.
// TODO: Define expected state transitions here, and log when an
// unexpected transition occurs.
//更新Telecomm Service Call对象的State
call.setState(newState, tag);
...
}
CallsManager如何管理Call对象?
参考自liyonggang的图原文链接
Telecom frameworks的Call对象
在Telecomm frameworks层InCallController来控制Call的更新
public class InCallController extends CallsManagerListenerBase
监听CallsManager消息
跟进onCallStateChanged
public void onCallStateChanged(Call call, int oldState, int newState) {
updateCall(call);
}
跟进updateCall
private void updateCall(Call call, boolean videoProviderChanged, boolean rttInfoChanged) {
if (!mInCallServices.isEmpty()) {
Log.i(this, "Sending updateCall %s", call);
List<ComponentName> componentsUpdated = new ArrayList<>();
for (Map.Entry<InCallServiceInfo, IInCallService> entry : mInCallServices.entrySet()) {
...
//获取parcelableCall
ParcelableCall parcelableCall = ParcelableCallUtils.toParcelableCall(
call,
videoProviderChanged /* includeVideoProvider */,
mCallsManager.getPhoneAccountRegistrar(),
info.isExternalCallsSupported(),
rttInfoChanged && info.equals(mInCallServiceConnection.getInfo()));
ComponentName componentName = info.getComponentName();
IInCallService inCallService = entry.getValue();
componentsUpdated.add(componentName);
try {
//继续调用
inCallService.updateCall(parcelableCall);
} catch (RemoteException ignored) {
}
}
Log.i(this, "Components updated: %s", componentsUpdated);
}
}
跟进到inCallService
public void updateCall(ParcelableCall call) {
mHandler.obtainMessage(MSG_UPDATE_CALL, call).sendToTarget();
}
查看mHandler的初始化,发现其是当前类,继续跟进到当前类的handleMessage中
public void handleMessage(Message msg) {
if (mPhone == null && msg.what != MSG_SET_IN_CALL_ADAPTER) {
return;
}
switch (msg.what) {
...
case MSG_ADD_CALL:
mPhone.internalAddCall((ParcelableCall) msg.obj);
break;
case MSG_UPDATE_CALL:
mPhone.internalUpdateCall((ParcelableCall) msg.obj);
break;
发现其调用Phone的internalUpdateCall方法进行更新
final void internalUpdateCall(ParcelableCall parcelableCall) {
Call call = mCallByTelecomCallId.get(parcelableCall.getId());
if (call != null) {
checkCallTree(parcelableCall);
call.internalUpdate(parcelableCall, mCallByTelecomCallId);
}
}
继续跟进就到了Telecomm Frameworks的Call中了,其利用parcelableCall进行更新
InCallUI 的Call
在O上InCallUI下面已经取消了Call.java
不过我们可以搜索到DialerCall.java,其本质上就是之前的Call.java
private final Call.Callback mTelecomCallCallback =
new Call.Callback() {
@Override
public void onStateChanged(Call call, int newState) {
LogUtil.v("TelecomCallCallback.onStateChanged", "call=" + call + " newState=" + newState);
update();
}
可以看到其监听了TeleComm 的Call
当其接收到onStateChange后调用update方法
private void update() {
Trace.beginSection("Update");
int oldState = getState();
// We want to potentially register a video call callback here.
updateFromTelecomCall();
..
}
所有Call的状态就从Telephony framework->Telecomm Service->TelecommFramework->dialerCall进行逐层更新状态