Android(安卓)O版本Call对象解析
在Android8.0中的Call.java有三个地方
frameworks\opt\telephony\src\java\com\android\internal\telephonytelephoney frameworks frameworks\base\telecomm\java\android\telecomtelecomm frameworkspackages\services\Telecomm\src\com\android\server\telecomtelecom 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 newUnknownConnectionsGsm = new ArrayList(); 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 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 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 componentsUpdated = new ArrayList<>(); for (Map.Entry 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进行逐层更新状态
更多相关文章
- Android高手进阶教程(二十五)之---Android(安卓)中的AIDL!!!
- 一种绕过Android(安卓)P对非SDK接口限制的简单方法
- Android的消息机制,用Android线程间通信的Message机制,Android中Ha
- Android状态栏和虚拟导航栏的适配总结
- Android(安卓)内存浅析【管理、机制、分析】
- 图解 Android(安卓)Handler 线程消息机制
- Android(安卓)内存浅析【一】【管理、机制、分析】
- Android中Handler异步线程
- Android[中级教程] 深入剖析Android消息机制