Android7.0 phone拒接流程分析--- 本文為原創文章,轉載請註明出處,http://www.cnblogs.com/lance2016/p/6391096.html ...
接上篇博文:Android7.0 Phone應用源碼分析(二) phone來電流程分析
今天我們再來分析下Android7.0 的phone的拒接流程
下麵先來看一下拒接電話流程時序圖
步驟1:滑動按鈕到拒接圖標,會調用到AnswerFragment的onDecline方法
com.android.incallui.AnswerFragment
public void onDecline(Context context) { getPresenter().onDecline(context); }
最後是調用到AnswerPresenteronDecline方法
com.android.incallui.AnswerPresenter public void onDecline(Context context) { Log.d(this, "onDecline " + mCallId); if (mCall.getSessionModificationState() == Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST) { InCallPresenter.getInstance().declineUpgradeRequest(context); } else { TelecomAdapter.getInstance().rejectCall(mCall.getId(), false, null); } }
步驟2:進入TelecomAdapter的rejectCall方法
com.android.incallui.TelecomAdapter void rejectCall(String callId, boolean rejectWithMessage, String message) { android.telecom.Call call = getTelecomCallById(callId); if (call != null) { call.reject(rejectWithMessage, message); } else { Log.e(this, "error rejectCall, call not in call list: " + callId); } }
TelecomAdapter是incallui與telecom通信的代理類,這裡通過callid取出對應的Call對象(android.telecom.Call)
步驟3:調用到framework里Call的reject方法
android.telecom.Call public void reject(boolean rejectWithMessage, String textMessage) { mInCallAdapter.rejectCall(mTelecomCallId, rejectWithMessage, textMessage); }
這裡mInCallAdapter是android.telecom.InCallAdapter類,是在Call對象創建的時候由外部傳入的參數
在telecom綁定InCallService服務的時候,會傳遞一個AIDL介面對象,InCallService會生成InCallAdapter對象來保存這個介面對象
步驟4:InCallAdapter的rejectCall方法
android.telecom.InCallAdapter
public void rejectCall(String callId, boolean rejectWithMessage, String textMessage) { try { mAdapter.rejectCall(callId, rejectWithMessage, textMessage); } catch (RemoteException e) { } }
mAdapter就是incallui與telecom通信的AIDL介面
步驟5:跨進程調用進入telecom進程,該AIDL介面具體實現類是InCallAdapter,雖然類名一樣但是不同的包名,這裡需要註意一下
com.android.server.telecom.InCallAdapter public void rejectCall(String callId, boolean rejectWithMessage, String textMessage) { try { Log.startSession("ICA.rC", mOwnerComponentName); long token = Binder.clearCallingIdentity(); try { synchronized (mLock) { Log.d(this, "rejectCall(%s,%b,%s)", callId, rejectWithMessage, textMessage); Call call = mCallIdMapper.getCall(callId); if (call != null) { mCallsManager.rejectCall(call, rejectWithMessage, textMessage); } else { Log.w(this, "setRingback, unknown call id: %s", callId); } } } finally { Binder.restoreCallingIdentity(token); } } finally { Log.endSession(); } }
這裡同樣是根據callid取出對應Call(com.android.server.telecom.Call),最後調用CallsManager的rejectCall方法傳入call
步驟6:CallsManager的rejectCall方法
com.android.server.telecom.CallsManager public void rejectCall(Call call, boolean rejectWithMessage, String textMessage) { if (!mCalls.contains(call)) { Log.i(this, "Request to reject a non-existent call %s", call); } else { for (CallsManagerListener listener : mListeners) { listener.onIncomingCallRejected(call, rejectWithMessage, textMessage); } call.reject(rejectWithMessage, textMessage); } }
這裡先通知觀察者來電拒接事件,比如CallAudioManager對該事件感興趣,它的處理是停止播放來電鈴聲和來電等待聲
com.android.server.telecom.CallAudioManager public void onIncomingCallRejected(Call call, boolean rejectWithMessage, String message) { maybeStopRingingAndCallWaitingForAnsweredOrRejectedCall(call); } private void maybeStopRingingAndCallWaitingForAnsweredOrRejectedCall(Call call) { // Check to see if the call being answered/rejected is the only ringing call, since this // will be called before the connection service acknowledges the state change. if (mRingingCalls.size() == 0 || (mRingingCalls.size() == 1 && call == mRingingCalls.iterator().next())) { mRinger.stopRinging(); mRinger.stopCallWaiting(); } }
最後再調用前面傳進來的call對象的reject方法
步驟7:Call的reject方法
com.android.server.telecom.Call public void reject(boolean rejectWithMessage, String textMessage) { Preconditions.checkNotNull(mConnectionService); // Check to verify that the call is still in the ringing state. A call can change states // between the time the user hits 'reject' and Telecomm receives the command. if (isRinging("reject")) { // Ensure video state history tracks video state at time of rejection. mVideoStateHistory |= mVideoState; mConnectionService.reject(this, rejectWithMessage, textMessage); Log.event(this, Log.Events.REQUEST_REJECT); } }
這裡的mConnectionService是ConnectionServiceWrapper類,是telecom與telephony通信的代理類
步驟8:ConnectionServiceWrapper的reject方法
com.android.server.telecom.ConnectionServiceWrapper void reject(Call call, boolean rejectWithMessage, String message) { final String callId = mCallIdMapper.getCallId(call); if (callId != null && isServiceValid("reject")) { try { logOutgoing("reject %s", callId); if (rejectWithMessage && call.can( Connection.CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION)) { mServiceInterface.rejectWithMessage(callId, message); } else { mServiceInterface.reject(callId); } } catch (RemoteException e) { } } }
這裡mServiceInterface就是telephony提供給telecom調用的AIDL介面
步驟9:跨進程調用進入telephony進程,telephony進程實際服務類是TelephonyConnectionService繼承於ConnectionService類在manifest聲明如下:
<service android:singleUser="true" android:name="com.android.services.telephony.TelephonyConnectionService" android:label="@string/pstn_connection_service_label" android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE" > <intent-filter> <action android:name="android.telecom.ConnectionService" /> </intent-filter> </service>
而AIDL介面具體實現是其父類ConnectionService的mBinder成員變數
android.telecom.ConnectionService private final IBinder mBinder = new IConnectionService.Stub() { @Override public void reject(String callId) { mHandler.obtainMessage(MSG_REJECT, callId).sendToTarget(); } }
步驟10~13:發送MSG_REJECT消息到隊列里處理
private void reject(String callId) { Log.d(this, "reject %s", callId); findConnectionForAction(callId, "reject").onReject(); } private Connection findConnectionForAction(String callId, String action) { if (mConnectionById.containsKey(callId)) { return mConnectionById.get(callId); } Log.w(this, "%s - Cannot find Connection %s", action, callId); return getNullConnection(); }
根據callid找到對應的connection對象(android.telecom.Connection),調用onReject方法
步驟14:TelephonyConnection繼承於connection
com.android.services.telephony.TelephonyConnection public void onReject() { Log.v(this, "onReject"); if (isValidRingingCall()) { hangup(android.telephony.DisconnectCause.INCOMING_REJECTED); } super.onReject(); }
protected void hangup(int telephonyDisconnectCode) { if (mOriginalConnection != null) { try { // Hanging up a ringing call requires that we invoke call.hangup() as opposed to // connection.hangup(). Without this change, the party originating the call will not // get sent to voicemail if the user opts to reject the call. if (isValidRingingCall()) { Call call = getCall(); if (call != null) { call.hangup(); } else { Log.w(this, "Attempting to hangup a connection without backing call."); } } else { // We still prefer to call connection.hangup() for non-ringing calls in order // to support hanging-up specific calls within a conference call. If we invoked // call.hangup() while in a conference, we would end up hanging up the entire // conference call instead of the specific connection. mOriginalConnection.hangup(); } } catch (CallStateException e) { Log.e(this, e, "Call to Connection.hangup failed with exception"); } } }
步驟15,16:這獲取mOriginalConnection的call(com.android.internal.telephony.Call)對象,並調用hangup方法
protected Call getCall() { if (mOriginalConnection != null) { return mOriginalConnection.getCall(); } return null; }
Call是抽象類,具體子類是GsmCdmaCall
com.android.internal.telephony.GsmCdmaCall public void hangup() throws CallStateException { mOwner.hangup(this); }
mOwner是GsmCdmaCallTracker對象
步驟17:GsmCdmaCallTracker的hangup方法
com.android.internal.telephony.GsmCdmaCallTracker public void hangup(GsmCdmaCall call) throws CallStateException { if (call.getConnections().size() == 0) { throw new CallStateException("no connections in call"); } if (call == mRingingCall) { if (Phone.DEBUG_PHONE) log("(ringing) hangup waiting or background"); mCi.hangupWaitingOrBackground(obtainCompleteMessage()); } else if (call == mForegroundCall) { if (call.isDialingOrAlerting()) { if (Phone.DEBUG_PHONE) { log("(foregnd) hangup dialing or alerting..."); } hangup((GsmCdmaConnection)(call.getConnections().get(0))); } else if (isPhoneTypeGsm() && mRingingCall.isRinging()) { // Do not auto-answer ringing on CHUP, instead just end active calls log("hangup all conns in active/background call, without affecting ringing call"); hangupAllConnections(call); } else { hangupForegroundResumeBackground(); } } else if (call == mBackgroundCall) { if (mRingingCall.isRinging()) { if (Phone.DEBUG_PHONE) { log("hangup all conns in background call"); } hangupAllConnections(call); } else { hangupWaitingOrBackground(); } } else { throw new RuntimeException ("GsmCdmaCall " + call + "does not belong to GsmCdmaCallTracker " + this); } call.onHangupLocal(); mPhone.notifyPreciseCallStateChanged(); }
由於是ringcall,這裡調用mCi.hangupWaitingOrBackground(obtainCompleteMessage());
mCi是CommandsInterface即RILJ介面,包裝了一個EVENT_OPERATION_COMPLETE回調消息,發送給RIL
步驟18:RIL的hangupWaitingOrBackground方法
com.android.internal.telephony.RIL
hangupWaitingOrBackground (Message result) { RILRequest rr = RILRequest.obtain(RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND, result); if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); mEventLog.writeRilHangup(rr.mSerial, RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND, -1); send(rr); }
給RIL層發送RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND消息
步驟19:mPhone.notifyPreciseCallStateChanged通知Phone狀態監聽事件
步驟20~24:收到RIL層的回應消息並處理,最後發送回調消息EVENT_OPERATION_COMPLETE給GsmCdmaCallTracker
步驟25:GsmCdmaCallTracker處理回調消息EVENT_OPERATION_COMPLETE
com.android.internal.telephony.GsmCdmaCallTracker private void operationComplete() { mPendingOperations--; if (DBG_POLL) log("operationComplete: pendingOperations=" + mPendingOperations + ", needsPoll=" + mNeedsPoll); if (mPendingOperations == 0 && mNeedsPoll) { mLastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT); mCi.getCurrentCalls(mLastRelevantPoll); } else if (mPendingOperations < 0) { // this should never happen Rlog.e(LOG_TAG,"GsmCdmaCallTracker.pendingOperations < 0"); mPendingOperations = 0; } }
這裡再次向RIL發送消息主動獲取當前Call狀態,包裝的回調消息為EVENT_POLL_CALLS_RESULT
步驟26~32:RIL返回消息,GsmCdmaCallTracker接收EVENT_POLL_CALLS_RESULT消息並處理
protected synchronized void handlePollCalls(AsyncResult ar) { ................... 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; } else if (conn.mCause == DisconnectCause.LOCAL || conn.mCause == DisconnectCause.INVALID_NUMBER) { mDroppedDuringPoll.remove(i); hasAnyCallDisconnected |= conn.onDisconnect(conn.mCause); wasDisconnected = true; } if (!isPhoneTypeGsm() && wasDisconnected && unknownConnectionAppeared && conn == newUnknownConnectionCdma) { unknownConnectionAppeared = false; newUnknownConnectionCdma = null; } ................... ................... updatePhoneState(); if (unknownConnectionAppeared) { if (isPhoneTypeGsm()) { for (Connection c : newUnknownConnectionsGsm) { log("Notify unknown for " + c); mPhone.notifyUnknownConnection(c); } } else { mPhone.notifyUnknownConnection(newUnknownConnectionCdma); } } if (hasNonHangupStateChanged || newRinging != null || hasAnyCallDisconnected) { mPhone.notifyPreciseCallStateChanged(); } }
這裡設置DisconnectCause.INCOMING_REJECTED為連接斷開的cause並調用GsmCdmaConnection的onDisconnect方法
步驟33:GsmCdmaConnection的onDisconnect方法
com.android.internal.telephony.GsmCdmaConnection public boolean onDisconnect(int cause) { boolean changed = false; mCause = cause; if (!mDisconnected) { doDisconnect(); if (DBG) Rlog.d(LOG_TAG, "onDisconnect: cause=" + cause); mOwner.getPhone().notifyDisconnect(this); if (mParent != null) { changed = mParent.connectionDisconnected(this); } mOrigConnection = null; } clearPostDialListeners(); releaseWakeLock(); return changed; }
doDisconnect方法設置斷開時間以及通話時長
private void doDisconnect() { mIndex = -1; mDisconnectTime = System.currentTimeMillis(); mDuration = SystemClock.elapsedRealtime() - mConnectTimeReal; mDisconnected = true; clearPostDialListeners(); }
最後通知註冊者斷開事件mOwner.getPhone().notifyDisconnect(this);
步驟34,36:通知phone狀態變化事件給相關監聽者
步驟35:GsmCdmaPhone通知通話斷開事件
com.android.internal.telephony.GsmCdmaPhone public void notifyDisconnect(Connection cn) { mDisconnectRegistrants.notifyResult(cn); mNotifier.notifyDisconnectCause(cn.getDisconnectCause(), cn.getPreciseDisconnectCause()); }
步驟37~40:TelephonyConnection註冊了斷開事件監聽,接收並處理斷開消息
com.android.services.telephony.TelephonyConnection void setOriginalConnection(com.android.internal.telephony.Connection originalConnection) { Log.v(this, "new TelephonyConnection, originalConnection: " + originalConnection); ...... getPhone().registerForDisconnect(mHandler, MSG_DISCONNECT, null); ...... }
void updateState() { if (mOriginalConnection == null) { return; } updateStateInternal(); updateStatusHints(); updateConnectionCapabilities(); updateConnectionProperties(); updateAddress(); updateMultiparty(); }
void updateStateInternal() { if (mOriginalConnection == null) { return; } Call.State newState; // If the state is overridden and the state of the original connection hasn't changed since, // then we continue in the overridden state, else we go to the original connection's state. if (mIsStateOverridden && mOriginalConnectionState == mOriginalConnection.getState()) { newState = mConnectionOverriddenState; } else { newState = mOriginalConnection.getState(); } Log.v(this, "Update state from %s to %s for %s", mConnectionState, newState, this); if (mConnectionState != newState) { mConnectionState = newState; switch (newState) { case IDLE: break; case ACTIVE: setActiveInternal(); break; case HOLDING: setOnHold(); break; case DIALING: case ALERTING: setDialing(); break; case INCOMING: case WAITING: setRinging(); break; case DISCONNECTED: setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause( mOriginalConnection.getDisconnectCause(), mOriginalConnection.getVendorDisconnectCause())); close(); break; case DISCONNECTING: break; } } }
通過DisconnectCauseUtil的toTelecomDisconnectCause方法生成DisconnectCause(android.telecom.DisconnectCause)對象
包含code, label, description, reason,toneToPlay信息
步驟41,42:通知外部監聽者斷開事件mNotifier.notifyDisconnectCause
步驟43:調用父類Connection的setDisconnected方法
public final void setDisconnected(DisconnectCause disconnectCause) { checkImmutable(); mDisconnectCause = disconnectCause; setState(STATE_DISCONNECTED); Log.d(this, "Disconnected with cause %s", disconnectCause); for (Listener l : mListeners) { l.onDisconnected(this, disconnectCause); } }
回調通知觀察者ConnectionService註冊了該事件,mConnectionListener接收處理
步驟44:mConnectionListener處理onDisconnected事件
android.telecom.ConnectionService private final Connection.Listener mConnectionListener = new Connection.Listener() { ...... @Override public void onDisconnected(Connection c, DisconnectCause disconnectCause) { String id = mIdByConnection.get(c); Log.d(this, "Adapter set disconnected %s", disconnectCause); mAdapter.setDisconnected(id, disconnectCause); } }
根據connection對象取出對應的callid
步驟45:TelephonyConnection的updateAddress方法更新connection信息
步驟46:ConnectionServiceAdapter的setDisconnected方法
android.telecom.ConnectionServiceAdapter void setDisconnected(String callId, DisconnectCause disconnectCause) { for (IConnectionServiceAdapter adapter : mAdapters) { try { adapter.setDisconnected(callId, disconnectCause); } catch (RemoteException e) { } } }
telecom在綁定TelephonyConnectionService的時候,會設置AIDL回調介面對象給telephony即ConnectionServiceWrapper的Adapter成員變數
步驟47:跨進程調用到telecom進程,ConnectionServiceWrapper的Adapter處理setDisconnected
com.android.server.telecom.ConnectionServiceWrapper private final class Adapter extends IConnectionServiceAdapter.Stub { ...... @Override public void setDisconnected(String callId, DisconnectCause disconnectCause) { Log.startSession("CSW.sD"); long token = Binder.clearCallingIdentity(); try { synchronized (mLock) { logIncoming("setDisconnected %s %s", callId, disconnectCause); Call call = mCallIdMapper.getCall(callId); Log.d(this, "disconnect call %s %s", disconnectCause, call); if (call != null) { mCallsManager.markCallAsDisconnected(call, disconnectCause); } else { // Log.w(this, "setDisconnected, unknown call id: %s", args.arg1); } } } finally { Binder.restoreCallingIdentity(token); Log.endSession(); } } ...... }
根據callid取出Call(com.android.server.telecom.Call)對象,給CallsManager傳遞Call和disconnectCause
步驟48,49,50:CallsManager的markCallAsDisconnected方法
com.android.server.telecom.CallsManager void markCallAsDisconnected(Call call, DisconnectCause disconnectCause) { call.setDisconnectCause(disconnectCause); setCallState(call, CallState.DISCONNECTED, "disconnected set explicitly"); }
給Call設置disconnectCause,同時設置callstate
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. call.setState(newState, tag); maybeShowErrorDialogOnDisconnect(call); Trace.beginSection("onCallStateChanged"); // Only broadcast state change for calls that are being tracked. if (mCalls.contains(call)) { updateCallsManagerState(); for (CallsManagerListener listener : mListeners) { if (Log.SYSTRACE_DEBUG) { Trace.beginSection(listener.getClass().toString() + " onCallStateChanged"); } listener.onCallStateChanged(call, oldState, newState); if (Log.SYSTRACE_DEBUG) { Trace.endSection(); } } } Trace.endSection(); } }
最後回調onCallStateChanged方法通知監聽者,這裡監聽call狀態變化的對象有很多,我們看下InCallController的處理
步驟51,52:InCallController的onCallStateChanged方法
com.android.server.telecom.InCallController @Override public void onCallStateChanged(Call call, int oldState, int newState) { updateCall(call); }
private void updateCall(Call call, boolean videoProviderChanged) { if (!mInCallServices.isEmpty()) { ParcelableCall parcelableCall = ParcelableCallUtils.toParcelableCall( call, videoProviderChanged /* includeVideoProvider */, mCallsManager.getPhoneAccountRegistrar()); Log.i(this, "Sending updateCall %s ==> %s", call, parcelableCall); List<ComponentName> componentsUpdated = new ArrayList<>(); for (Map.Entry<ComponentName, IInCallService> entry : mInCallServices.entrySet()) { ComponentName componentName = entry.getKey(); IInCallService inCallService = entry.getValue(); componentsUpdated.add(componentName); try { inCallService.updateCall(parcelableCall); } catch (RemoteException ignored) { } } Log.i(this, "Components updated: %s", componentsUpdated); } } }
根據call信息生成ParcelableCall對象,給incallservice傳遞ParcelableCall
步驟53,54:InCallService的updateCall方法
android.telecom.InCallService @Override public void updateCall(ParcelableCall call) { mHandler.obtainMessage(MSG_UPDATE_CALL, call).sendToTarget(); }
private final Handler mHandler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { ...... case MSG_UPDATE_CALL: mPhone.internalUpdateCall((ParcelableCall) msg.obj); break; }
步驟55:Phone的internalUpdateCall方法
android.telecom.Phone final void internalUpdateCall(ParcelableCall parcelableCall) { Call call = mCallByTelecomCallId.get(parcelableCall.getId()); if (call != null) { checkCallTree(parcelableCall); call.internalUpdate(parcelableCall, mCallByTelecomCallId); } }
這裡的Phone對象只是一個管理類,保存call列表信息和與telecom通信的AIDL介面對象,通過callid取出Call(android.telecom.Call)對象
步驟56:Call的internalUpdate方法
android.telecom.Call final void internalUpdate(ParcelableCall parcelableCall, Map<String, Call> callIdMap) { Details details = Details.createFromParcelableCall(parcelableCall); ...... ...... // Now we fire updates, ensuring that any client who listens to any of these notifications // gets the most up-to-date state. if (stateChanged) { fireStateChanged(mState); } if (detailsChanged) { fireDetailsChanged(mDetails); } if (cannedTextResponsesChanged) { fireCannedTextResponsesLoaded(mCannedTextResponses); } if (videoCallChanged) { fireVideoCallChanged(mVideoCallImpl); } if (parentChanged) { fireParentChanged(getParent()); } if (childrenChanged) { fireChildrenChanged(getChildren()); } // If we have transitioned to DISCONNECTED, that means we need to notify clients and // remove ourselves from the Phone. Note that we do this after completing all state updates // so a client can cleanly transition all their UI to the state appropriate for a // DISCONNECTED Call while still relying on the existence of that Call in the Phone's list. if (mState == STATE_DISCONNECTED) { fireCallDestroyed(); } }
步驟57:轉化ParcelableCall 信息為Detail信息,判斷call狀態是否有變化,有則進入fireStateChanged
private void fireStateChanged(final int newState) { for (CallbackRecord<Callback> record : mCallbackRecords) { final Call call = this; final Callback callback = record.getCallback(); record.getHandler().post(new Runnable() { @Override public void run() { callback.onStateChanged(call, newState); } }); } }
步驟58:這裡遍歷Call(android.telecom.Call)對象里的回調監聽者
private final List<CallbackRecord<Callback>> mCallbackRecords = new CopyOnWriteArrayList<>();
也就是每次InCallPresenter添加Call(android.telecom.Call)時添加的註冊回調事件
com.android.incallui.InCallPresenter public void onCallAdded(final android.telecom.Call call) { if (shouldAttemptBlocking(call)) { maybeBlockCall(call); } else { mCallList.onCallAdded(call); } // Since a call has been added we are no longer waiting for Telecom to send us a call. setBoundAndWaitingForOutgoingCall(false, null); call.registerCallback(mCallCallback); }
這裡有兩個地方註冊了事件回調,一個是在CallList的onCallAdd方法里轉化Call(android.telecom.Call)創建了Call(com.android.incallui.Call)對象時註冊的
com.android.incallui.CallList public void onCallAdded(final android.telecom.Call telecomCall) { Trace.beginSection("onCallAdded"); final Call call = new Call(telecomCall); Log.d(this, "onCallAdded: callState=" + call.getState()); if (call.getState() == Call.State.INCOMING || call.getState() == Call.State.CALL_WAITING) { onIncoming(call, call.getCannedSmsResponses()); } else { onUpdate(call); } call.logCallInitiationType(); Trace.endSection(); }
com.android.incallui.Call public Call(android.telecom.Call telecomCall) { mTelecomCall = telecomCall; mId = ID_PREFIX + Integer.toString(sIdCounter++); updateFromTelecomCall(); mTelecomCall.registerCallback(mTelecomCallCallback); mTimeAddedMs = System.currentTimeMillis(); }
還有就是InCallPresenter的成員變數mCallCallback的註冊
這裡onStateChange只有Call(com.android.incallui.Call)的成員變數mTelecomCallCallback有處理