Android7.0 Phone應用源碼分析(四) phone掛斷流程分析

技術 科技優家 2017-03-25

今天我們再來分析一下電話掛斷流程

電話掛斷分為本地掛斷和遠程掛斷,針對這兩種情況各做分析

先來看下本地掛斷電話的時序圖:

步驟1:點擊通話界面的掛斷按鈕,會調用到CallCardPresenter的endCallClicked方法,請看CallCardFragment裡掛斷按鈕的監聽事件

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流程

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)

步驟3:調用到framework裡Call的disconnect方法

android.telecom.Call
 public void disconnect {
        mInCallAdapter.disconnectCall(mTelecomCallId);
 }

這裡mInCallAdapter是android.telecom.InCallAdapter類,與telecom進程通信的代理類

步驟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拒接流程一樣,這裡就不重複描述了

講完本地掛斷電話的流程,再講一下遠程掛斷的流程,先看一下相關的時序圖

Android7.0 Phone應用源碼分析(四) phone掛斷流程分析

步驟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拒接流程一樣,這裡就不重複描述了

至此,一個電話掛斷的流程就分析完了,結合log打印會對整個流程的理解更加深刻