接上篇博文:Android7.0 Phone應用源碼分析(三) phone拒接流程分析html
今天咱們再來分析一下電話掛斷流程android
電話掛斷分爲本地掛斷和遠程掛斷,針對這兩種狀況各作分析app
先來看下本地掛斷電話的時序圖:ide
步驟1:點擊通話界面的掛斷按鈕,會調用到CallCardPresenter的endCallClicked方法,請看CallCardFragment裏掛斷按鈕的監聽事件oop
com.android.incallui.CallCardFragment public void onViewCreated(View view, Bundle savedInstanceState) { ......
...... mFloatingActionButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { getPresenter().endCallClicked(); } }); }
com.android.incallui.CallCardPresenter public void endCallClicked() { if (mPrimary == null) { return; } Log.i(this, "Disconnecting call: " + mPrimary); final String callId = mPrimary.getId(); mPrimary.setState(Call.State.DISCONNECTING); CallList.getInstance().onUpdate(mPrimary); TelecomAdapter.getInstance().disconnectCall(callId); }
這裏先把Call的狀態設置成DISCONNECTING,而後經過CallList更新UI界面,最後繼續掛斷流程
源碼分析
步驟2:TelecomAdapter的disconnectCall流程
post
com.android.incallui.TelecomAdapter void disconnectCall(String callId) { android.telecom.Call call = getTelecomCallById(callId); if (call != null) { call.disconnect(); } else { Log.e(this, "error disconnectCall, call not in call list " + callId); } }
經過callid找到對應的Call對象(android.telecom.Call)ui
步驟3:調用到framework裏Call的disconnect方法this
android.telecom.Call public void disconnect() { mInCallAdapter.disconnectCall(mTelecomCallId); }
這裏mInCallAdapter是android.telecom.InCallAdapter類,與telecom進程通訊的代理類url
步驟4:InCallAdapter的disconnectCall方法
android.telecom.InCallAdapter public void disconnectCall(String callId) { try { mAdapter.disconnectCall(callId); } catch (RemoteException e) { } }
mAdapter就是incallui與telecom通訊的AIDL接口
步驟5:跨進程調用進入telecom進程,該AIDL接口具體實現類是InCallAdapter,相同的類名不同的包名
com.android.server.telecom.InCallAdapter public void disconnectCall(String callId) { try { Log.startSession("ICA.dC", mOwnerComponentName); long token = Binder.clearCallingIdentity(); try { synchronized (mLock) { Log.v(this, "disconnectCall: %s", callId); Call call = mCallIdMapper.getCall(callId); if (call != null) { mCallsManager.disconnectCall(call); } else { Log.w(this, "disconnectCall, unknown call id: %s", callId); } } } finally { Binder.restoreCallingIdentity(token); } } finally { Log.endSession(); } }
這裏一樣是根據callid取出對應Call(com.android.server.telecom.Call),最後調用CallsManager的disconnectCall方法傳入call
步驟6:CallsManager的disconnectCall方法
com.android.server.telecom.CallsManager public void disconnectCall(Call call) { Log.v(this, "disconnectCall %s", call); if (!mCalls.contains(call)) { Log.w(this, "Unknown call (%s) asked to disconnect", call); } else { mLocallyDisconnectingCalls.add(call); call.disconnect(); } }
將該Call對象加入本地斷開Call列表,以後進入Call的disconnect方法
步驟7:Call的disconnect方法
com.android.server.telecom.Call public void disconnect() { disconnect(false); }
public void disconnect(boolean wasViaNewOutgoingCallBroadcaster) { Log.event(this, Log.Events.REQUEST_DISCONNECT); // Track that the call is now locally disconnecting. setLocallyDisconnecting(true); if (mState == CallState.NEW || mState == CallState.SELECT_PHONE_ACCOUNT || mState == CallState.CONNECTING) { Log.v(this, "Aborting call %s", this); abort(wasViaNewOutgoingCallBroadcaster); } else if (mState != CallState.ABORTED && mState != CallState.DISCONNECTED) { if (mConnectionService == null) { Log.e(this, new Exception(), "disconnect() request on a call without a" + " connection service."); } else { Log.i(this, "Send disconnect to connection service for call: %s", this); // The call isn't officially disconnected until the connection service // confirms that the call was actually disconnected. Only then is the // association between call and connection service severed, see // {@link CallsManager#markCallAsDisconnected}. mConnectionService.disconnect(this); } } }
setLocallyDisconnecting(true); 先設置是否爲本地掛斷標誌爲true
因爲mState這時候是CallState.ACTIVE狀態,進入mConnectionService的disconnect方法
這裏的mConnectionService是ConnectionServiceWrapper類,是telecom與telephony通訊的代理類
步驟8:ConnectionServiceWrapper的disconnect方法
com.android.server.telecom.ConnectionServiceWrapper void disconnect(Call call) { final String callId = mCallIdMapper.getCallId(call); if (callId != null && isServiceValid("disconnect")) { try { logOutgoing("disconnect %s", callId); mServiceInterface.disconnect(callId); } catch (RemoteException e) { } } }
這裏mServiceInterface就是telephony提供給telecom調用的AIDL接口
步驟9:跨進程調用進入telephony進程,AIDL接口具體實現是其父類ConnectionService的mBinder成員變量
android.telecom.ConnectionService private final IBinder mBinder = new IConnectionService.Stub() {
......
......
public void disconnect(String callId) { mHandler.obtainMessage(MSG_DISCONNECT, callId).sendToTarget(); } }
步驟10~13:發送MSG_DISCONNECT消息到隊列裏處理
private void disconnect(String callId) { Log.d(this, "disconnect %s", callId); if (mConnectionById.containsKey(callId)) { findConnectionForAction(callId, "disconnect").onDisconnect(); } else { findConferenceForAction(callId, "disconnect").onDisconnect(); } }
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),調用onDisconnect方法
步驟14,15:TelephonyConnection繼承於connection
com.android.services.telephony.TelephonyConnection public void onDisconnect() { Log.v(this, "onDisconnect"); hangup(android.telephony.DisconnectCause.LOCAL); }
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"); } } }
設置斷開鏈接類型爲DisconnectCause.LOCAL,並調用mOriginalConnection的hangup方法, 實際Connection類是GsmCdmaConnection
步驟16:GsmCdmaConnection的hangup方法
com.android.internal.telephony.GsmCdmaConnection public void hangup() throws CallStateException { if (!mDisconnected) { mOwner.hangup(this); } else { throw new CallStateException ("disconnected"); } }
這裏的mOwner對想是GsmCdmaCallTracker類型
步驟17:GsmCdmaCallTracker的hangup方法
com.android.internal.telephony.GsmCdmaCallTracker public void hangup(GsmCdmaConnection conn) throws CallStateException { if (conn.mOwner != this) { throw new CallStateException ("GsmCdmaConnection " + conn + "does not belong to GsmCdmaCallTracker " + this); } if (conn == mPendingMO) { // We're hanging up an outgoing call that doesn't have it's // GsmCdma index assigned yet if (Phone.DEBUG_PHONE) log("hangup: set hangupPendingMO to true"); mHangupPendingMO = true; } else if (!isPhoneTypeGsm() && conn.getCall() == mRingingCall && mRingingCall.getState() == GsmCdmaCall.State.WAITING) { // Handle call waiting hang up case. // // The ringingCall state will change to IDLE in GsmCdmaCall.detach // if the ringing call connection size is 0. We don't specifically // set the ringing call state to IDLE here to avoid a race condition // where a new call waiting could get a hang up from an old call // waiting ringingCall. // // PhoneApp does the call log itself since only PhoneApp knows // the hangup reason is user ignoring or timing out. So conn.onDisconnect() // is not called here. Instead, conn.onLocalDisconnect() is called. conn.onLocalDisconnect(); updatePhoneState(); mPhone.notifyPreciseCallStateChanged(); return; } else { try { mCi.hangupConnection (conn.getGsmCdmaIndex(), obtainCompleteMessage()); } catch (CallStateException ex) { // Ignore "connection not found" // Call may have hung up already Rlog.w(LOG_TAG,"GsmCdmaCallTracker WARN: hangup() on absent connection " + conn); } } conn.onHangupLocal(); }
因爲是通話中掛斷,這裏調用 mCi.hangupConnection (conn.getGsmCdmaIndex(), obtainCompleteMessage());
這裏調用conn的getGsmCdmaIndex方法先獲取索引
com.android.internal.telephony.GsmCdmaConnection getGsmCdmaIndex() throws CallStateException { if (mIndex >= 0) { return mIndex + 1; } else { throw new CallStateException ("GsmCdma index not yet assigned"); } }
這個索引對應的就是DriveCall裏的index值,匹配modem裏CallList裏對於Call對象
mCi是CommandsInterface即RILJ接口,包裝了一個EVENT_OPERATION_COMPLETE回調消息,發送給RIL
步驟18:RIL的hangupConnection方法
com.android.internal.telephony.RIL public void hangupConnection (int gsmIndex, Message result) { if (RILJ_LOGD) riljLog("hangupConnection: gsmIndex=" + gsmIndex); RILRequest rr = RILRequest.obtain(RIL_REQUEST_HANGUP, result); if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " " + gsmIndex); mEventLog.writeRilHangup(rr.mSerial, RIL_REQUEST_HANGUP, gsmIndex); rr.mParcel.writeInt(1); rr.mParcel.writeInt(gsmIndex); send(rr); }
給RIL層發送RIL_REQUEST_HANGUP消息並附帶index參數
步驟19~23:收到RIL層的迴應消息並處理,最後發送回調消息EVENT_OPERATION_COMPLETE給GsmCdmaCallTracker
步驟24: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
步驟25~31:RIL返回消息,GsmCdmaCallTracker接收EVENT_POLL_CALLS_RESULT消息並處理
com.android.internal.telephony.GsmCdmaCallTracker protected synchronized void handlePollCalls(AsyncResult ar) { List polledCalls; if (VDBG) 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(); return; } …………
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); if (dc.index == i+1) { curDC++; } else { dc = null; } } ………… } else if (conn != null && dc == null) { if (isPhoneTypeGsm()) { // Connection missing in CLCC response that we were // tracking. mDroppedDuringPoll.add(conn); // Dropped connections are removed from the CallTracker // list but kept in the GsmCdmaCall list mConnections[i] = null; } else { ………… 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; } } /* Disconnect any pending Handover connections */ for (Iterator<Connection> it = mHandoverConnections.iterator(); it.hasNext();) { Connection hoConnection = it.next(); log("handlePollCalls - disconnect hoConn= " + hoConnection + " hoConn.State= " + hoConnection.getState()); if (hoConnection.getState().isRinging()) { hoConnection.onDisconnect(DisconnectCause.INCOMING_MISSED); } else { hoConnection.onDisconnect(DisconnectCause.NOT_VALID); } it.remove(); } // Any non-local disconnects: determine cause if (mDroppedDuringPoll.size() > 0) { mCi.getLastCallFailCause( obtainNoPollCompleteMessage(EVENT_GET_LAST_CALL_FAIL_CAUSE)); } ………… if (VDBG) log("handlePollCalls calling updatePhoneState()"); 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(); } //dumpState(); } }
這裏是通話斷開事件,將connection放入mDroppedDuringPoll列表,因爲斷開類型是DisconnectCause.LOCAL
直接調用GsmCdmaConnection的onDisconnect方法傳入cause參數
步驟32: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);
步驟33以後的流程就跟上篇講解phone拒接流程同樣,這裏就不重複描述了
詳見http://www.cnblogs.com/lance2016/p/6391096.html
講完本地掛斷電話的流程,再講一下遠程掛斷的流程,先看一下相關的時序圖
步驟1~4:modem層上報RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED事件,RIL通知EVENT_CALL_STATE_CHANGE消息
步驟5,6,7:GsmCdmaCallTracker處理消息,向RIL發送消息主動獲取當前Call狀態,包裝的回調消息爲EVENT_POLL_CALLS_RESULT
com.android.internal.telephony.CallTracker protected void pollCallsWhenSafe() { mNeedsPoll = true; if (checkNoOperationsPending()) { mLastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT); mCi.getCurrentCalls(mLastRelevantPoll); } }
步驟8~13:RIL返回消息,GsmCdmaCallTracker接收EVENT_POLL_CALLS_RESULT消息並處理
com.android.internal.telephony.GsmCdmaCallTracker protected synchronized void handlePollCalls(AsyncResult ar) { List polledCalls; …………
if (mDroppedDuringPoll.size() > 0) { mCi.getLastCallFailCause( obtainNoPollCompleteMessage(EVENT_GET_LAST_CALL_FAIL_CAUSE)); } …………
}
這裏跟前面本地掛斷的流程差很少,但區別是因爲不是本地掛斷的cause類型,會主動再向RIL發送消息獲取通話斷開的cause
包裝的回調消息爲EVENT_GET_LAST_CALL_FAIL_CAUSE
步驟14:RIL的getLastCallFailCause方法
com.android.internal.telephony.RIL public void getLastCallFailCause (Message result) { RILRequest rr = RILRequest.obtain(RIL_REQUEST_LAST_CALL_FAIL_CAUSE, result); if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); send(rr); }
向MODEM發送RIL_REQUEST_LAST_CALL_FAIL_CAUSE消息
步驟15~20:modem迴應RIL_REQUEST_LAST_CALL_FAIL_CAUSE消息,RIL發送回調消息EVENT_GET_LAST_CALL_FAIL_CAUSE
步驟21:GsmCdmaCallTracker處理消息
com.android.internal.telephony.GsmCdmaCallTracker case EVENT_GET_LAST_CALL_FAIL_CAUSE: int causeCode; String vendorCause = null; ar = (AsyncResult)msg.obj; operationComplete(); if (ar.exception != null) { // An exception occurred...just treat the disconnect // cause as "normal" causeCode = CallFailCause.NORMAL_CLEARING; Rlog.i(LOG_TAG, "Exception during getLastCallFailCause, assuming normal disconnect"); } else { LastCallFailCause failCause = (LastCallFailCause)ar.result; causeCode = failCause.causeCode; vendorCause = failCause.vendorCause; } // Log the causeCode if its not normal if (causeCode == CallFailCause.NO_CIRCUIT_AVAIL || causeCode == CallFailCause.TEMPORARY_FAILURE || causeCode == CallFailCause.SWITCHING_CONGESTION || causeCode == CallFailCause.CHANNEL_NOT_AVAIL || causeCode == CallFailCause.QOS_NOT_AVAIL || causeCode == CallFailCause.BEARER_NOT_AVAIL || causeCode == CallFailCause.ERROR_UNSPECIFIED) { CellLocation loc = mPhone.getCellLocation(); int cid = -1; if (loc != null) { if (isPhoneTypeGsm()) { cid = ((GsmCellLocation)loc).getCid(); } else { cid = ((CdmaCellLocation)loc).getBaseStationId(); } } EventLog.writeEvent(EventLogTags.CALL_DROP, causeCode, cid, TelephonyManager.getDefault().getNetworkType()); } for (int i = 0, s = mDroppedDuringPoll.size(); i < s ; i++) { GsmCdmaConnection conn = mDroppedDuringPoll.get(i); conn.onRemoteDisconnect(causeCode, vendorCause); } updatePhoneState(); mPhone.notifyPreciseCallStateChanged(); mDroppedDuringPoll.clear(); break;
RIL在處理RIL_REQUEST_LAST_CALL_FAIL_CAUSE消息時將結果放入LastCallFailCause對象,傳遞給GsmCdmaCallTracker
GsmCdmaCallTracker獲得結果,其中causeCode值就是斷開緣由,接着調用GsmCdmaConnection的onRemoteDisconnect方法
步驟22:GsmCdmaConnection的onRemoteDisconnect方法
com.android.internal.telephony.GsmCdmaConnection void onRemoteDisconnect(int causeCode, String vendorCause) { this.mPreciseCause = causeCode; this.mVendorCause = vendorCause; onDisconnect(disconnectCauseFromCode(causeCode)); }
這裏經過disconnectCauseFromCode方法將causeCode值轉化爲DisconnectCause值
int disconnectCauseFromCode(int causeCode) { /** * See 22.001 Annex F.4 for mapping of cause codes * to local tones */ switch (causeCode) { case CallFailCause.USER_BUSY: return DisconnectCause.BUSY; case CallFailCause.NO_CIRCUIT_AVAIL: case CallFailCause.TEMPORARY_FAILURE: case CallFailCause.SWITCHING_CONGESTION: case CallFailCause.CHANNEL_NOT_AVAIL: case CallFailCause.QOS_NOT_AVAIL: case CallFailCause.BEARER_NOT_AVAIL: return DisconnectCause.CONGESTION; case CallFailCause.ACM_LIMIT_EXCEEDED: return DisconnectCause.LIMIT_EXCEEDED; case CallFailCause.CALL_BARRED: return DisconnectCause.CALL_BARRED; case CallFailCause.FDN_BLOCKED: return DisconnectCause.FDN_BLOCKED; case CallFailCause.UNOBTAINABLE_NUMBER: return DisconnectCause.UNOBTAINABLE_NUMBER; case CallFailCause.DIAL_MODIFIED_TO_USSD: return DisconnectCause.DIAL_MODIFIED_TO_USSD; case CallFailCause.DIAL_MODIFIED_TO_SS: return DisconnectCause.DIAL_MODIFIED_TO_SS; case CallFailCause.DIAL_MODIFIED_TO_DIAL: return DisconnectCause.DIAL_MODIFIED_TO_DIAL; case CallFailCause.CDMA_LOCKED_UNTIL_POWER_CYCLE: return DisconnectCause.CDMA_LOCKED_UNTIL_POWER_CYCLE; case CallFailCause.CDMA_DROP: return DisconnectCause.CDMA_DROP; case CallFailCause.CDMA_INTERCEPT: return DisconnectCause.CDMA_INTERCEPT; case CallFailCause.CDMA_REORDER: return DisconnectCause.CDMA_REORDER; case CallFailCause.CDMA_SO_REJECT: return DisconnectCause.CDMA_SO_REJECT; case CallFailCause.CDMA_RETRY_ORDER: return DisconnectCause.CDMA_RETRY_ORDER; case CallFailCause.CDMA_ACCESS_FAILURE: return DisconnectCause.CDMA_ACCESS_FAILURE; case CallFailCause.CDMA_PREEMPTED: return DisconnectCause.CDMA_PREEMPTED; case CallFailCause.CDMA_NOT_EMERGENCY: return DisconnectCause.CDMA_NOT_EMERGENCY; case CallFailCause.CDMA_ACCESS_BLOCKED: return DisconnectCause.CDMA_ACCESS_BLOCKED; case CallFailCause.ERROR_UNSPECIFIED: case CallFailCause.NORMAL_CLEARING: default: GsmCdmaPhone phone = mOwner.getPhone(); int serviceState = phone.getServiceState().getState(); UiccCardApplication cardApp = phone.getUiccCardApplication(); AppState uiccAppState = (cardApp != null) ? cardApp.getState() : AppState.APPSTATE_UNKNOWN; if (serviceState == ServiceState.STATE_POWER_OFF) { return DisconnectCause.POWER_OFF; } else if (serviceState == ServiceState.STATE_OUT_OF_SERVICE || serviceState == ServiceState.STATE_EMERGENCY_ONLY ) { return DisconnectCause.OUT_OF_SERVICE; } else { if (isPhoneTypeGsm()) { if (uiccAppState != AppState.APPSTATE_READY) { return DisconnectCause.ICC_ERROR; } else if (causeCode == CallFailCause.ERROR_UNSPECIFIED) { if (phone.mSST.mRestrictedState.isCsRestricted()) { return DisconnectCause.CS_RESTRICTED; } else if (phone.mSST.mRestrictedState.isCsEmergencyRestricted()) { return DisconnectCause.CS_RESTRICTED_EMERGENCY; } else if (phone.mSST.mRestrictedState.isCsNormalRestricted()) { return DisconnectCause.CS_RESTRICTED_NORMAL; } else { return DisconnectCause.ERROR_UNSPECIFIED; } } else if (causeCode == CallFailCause.NORMAL_CLEARING) { return DisconnectCause.NORMAL; } else { // If nothing else matches, report unknown call drop reason // to app, not NORMAL call end. return DisconnectCause.ERROR_UNSPECIFIED; } } else { if (phone.mCdmaSubscriptionSource == CdmaSubscriptionSourceManager.SUBSCRIPTION_FROM_RUIM && uiccAppState != AppState.APPSTATE_READY) { return DisconnectCause.ICC_ERROR; } else if (causeCode==CallFailCause.NORMAL_CLEARING) { return DisconnectCause.NORMAL; } else { return DisconnectCause.ERROR_UNSPECIFIED; } } } } }
因爲causeCode值是NORMAL_CLEARING,因此獲得的DisconnectCause值是DisconnectCause.NORMAL
接着調用onDisconnect方法
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; }
步驟23以後的流程就跟上篇講解phone拒接流程同樣,這裏就不重複描述了
詳見http://www.cnblogs.com/lance2016/p/6391096.html
至此,一個電話掛斷的流程就分析完了,結合log打印會對整個流程的理解更加深入