通话挂断流程

电话挂断分为本地挂断和远程挂断,针对这两种情况各做分析

先来看下本地挂断电话的时序图:

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值