电话挂断分为本地挂断和远程挂断,针对这两种情况各做分析
先来看下本地挂断电话的时序图:

步骤1:点击通话界面的挂断按钮,会调用到CallCardPresenter的endCallClicked方法,请看CallCardFragment里挂断按钮的监听事件

com.android.incallui.CallCardFragment
publicvoid onViewCreated(View view, Bundle savedInstanceState) {
......
......
mFloatingActionButton.setOnClickListener(new View.OnClickListener() {
@Override
publicvoid onClick(View v) {
getPresenter().endCallClicked();
}
});
}


com.android.incallui.CallCardPresenter
publicvoid 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
publicvoid disconnect() {
mInCallAdapter.disconnectCall(mTelecomCallId);
}
这里mInCallAdapter是android.telecom.InCallAdapter类,与telecom进程通信的代理类
步骤4:InCallAdapter的disconnectCall方法

android.telecom.InCallAdapter
publicvoid disconnectCall(String callId) {
try {
mAdapter.disconnectCall(callId);
} catch (RemoteException e) {
}
}

mAdapter就是incallui与telecom通信的AIDL接口
步骤5:跨进程调用进入telecom进程,该AIDL接口具体实现类是InCallAdapter,相同的类名不一样的包名

com.android.server.telecom.InCallAdapter
publicvoid 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
publicvoid 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
publicvoid disconnect() {
disconnect(false);
}

publicvoid 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);
} elseif (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
privatefinal IBinder mBinder = new IConnectionService.Stub() {
......
......
publicvoid disconnect(String callId) {
mHandler.obtainMessage(MSG_DISCONNECT, callId).sendToTarget();
}
}

步骤10~13:发送MSG_DISCONNECT消息到队列里处理

privatevoid 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
publicvoid onDisconnect() {
Log.v(this, "onDisconnect");
hangup(android.telephony.DisconnectCause.LOCAL);
}

protectedvoid 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
publicvoid hangup() throws CallStateException {
if (!mDisconnected) {
mOwner.hangup(this);
} else {
thrownew CallStateException ("disconnected");
}
}

这里的mOwner对想是GsmCdmaCallTracker类型
步骤17:GsmCdmaCallTracker的hangup方法

com.android.internal.telephony.GsmCdmaCallTracker
publicvoid hangup(GsmCdmaConnection conn) throws CallStateException {
if (conn.mOwner != this) {
thrownew 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 yetif (Phone.DEBUG_PHONE) log("hangup: set hangupPendingMO to true");
mHangupPendingMO = true;
} elseif (!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 {
thrownew 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
publicvoid 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
privatevoid operationComplete() {
mPendingOperations--;
if (DBG_POLL) log("operationComplete: pendingOperations=" +
mPendingOperations + ", needsPoll=" + mNeedsPoll);
if (mPendingOperations == 0 && mNeedsPoll) {
mLastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT);
mCi.getCurrentCalls(mLastRelevantPoll);
} elseif (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
protectedsynchronizedvoid handlePollCalls(AsyncResult ar) {
List polledCalls;
if (VDBG) log("handlePollCalls");
if (ar.exception == null) {
polledCalls = (List)ar.result;
} elseif (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 sparseif (curDC < dcSize) {
dc = (DriverCall) polledCalls.get(curDC);
if (dc.index == i+1) {
curDC++;
} else {
dc = null;
}
}
…………
} elseif (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);
//CDMAboolean wasDisconnected = false;
if (conn.isIncoming() && conn.getConnectTime() == 0) {
// Missed or rejected callint 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;
} elseif (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 causeif (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
publicboolean 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方法设置断开时间以及通话时长

privatevoid 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
protectedvoid 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
protectedsynchronizedvoid 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
publicvoid 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 normalif (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;
} elseif (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;
} elseif (causeCode == CallFailCause.ERROR_UNSPECIFIED) {
if (phone.mSST.mRestrictedState.isCsRestricted()) {
return DisconnectCause.CS_RESTRICTED;
} elseif (phone.mSST.mRestrictedState.isCsEmergencyRestricted()) {
return DisconnectCause.CS_RESTRICTED_EMERGENCY;
} elseif (phone.mSST.mRestrictedState.isCsNormalRestricted()) {
return DisconnectCause.CS_RESTRICTED_NORMAL;
} else {
return DisconnectCause.ERROR_UNSPECIFIED;
}
} elseif (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;
} elseif (causeCode==CallFailCause.NORMAL_CLEARING) {
return DisconnectCause.NORMAL;
} else {
return DisconnectCause.ERROR_UNSPECIFIED;
}
}
}
}
}

由于causeCode值是NORMAL_CLEARING,所以得到的DisconnectCause值是DisconnectCause.NORMAL
接着调用onDisconnect方法

publicboolean 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打印会对整个流程的理解更加深刻