您的位置:首页 > 移动开发 > Android开发

Android 5.1 Phone 挂断电话流程分析

2015-08-29 20:36 525 查看

写在前面的话

本文主要分析Android挂断电话的流程,研究的代码是Android 5.1的,以CDMA为例,GSM同理。

挂断电话主要分两种情况:本地主动挂断电话和远程断开通话

一、 本地主动挂断电话



(如果图片看不清的话,可以右键选择在新标签中打开图片,或者把图片另存到自己电脑再查看。)

这里说的本地主动挂断电话,是指通过点击UI界面上的挂断按钮来挂断电话,而不是通过物理键来挂断电话;至于通过物理键挂断电话,第二节再分析。

挂断电话的按钮
floating_end_call_action_button
是在
call_card_content.xml
布局文件里的,该布局文件是在CallCardFragment.java的onCreateView方法里被加载,因此,本地主动挂断电话的流程就从CallCardFragment.java开始。

本文来自http://blog.csdn.net/linyongan ,转载请务必注明出处。

步骤1:在CallCardFragment.java的onViewCreated()方法里可以找到,挂断电话的按钮
floating_end_call_action_button
所绑定的监听器,当挂断按钮被点击时就会执行监听器里的方法。

@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
...
mFloatingActionButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
getPresenter().endCallClicked();
}
});
...
}


步骤2: CallCardPresenter.java的CallCardPresenter()方法

public void endCallClicked() {
if (mPrimary == null) {
return;
}

Log.i(this, "Disconnecting call: " + mPrimary);
mPrimary.setState(Call.State.DISCONNECTING);
Call temp = mPrimary;
CallList.getInstance().onUpdate(mPrimary);
TelecomAdapter.getInstance().disconnectCall(temp.getId());
}


在这里做了3件事:

1、把Call的状态设置成
DISCONNECTING
。(步骤3)

2、更新UI界面,显示正在挂断的界面。(步骤4,5,7,8)

3、继续走挂断电话的流程。(步骤6)

步骤6: TelecomAdapter.java的disconnectCall()方法

void disconnectCall(String callId) {
if (mPhone != null) {
getTelecommCallById(callId).disconnect();
} else {
Log.e(this, "error disconnectCall, mPhone is null");
}
}

private android.telecom.Call getTelecommCallById(String callId) {
final Call call = CallList.getInstance().getCallById(callId);
return call == null ? null : call.getTelecommCall();
}


在getTelecommCallById()方法里通过Package目录下InCallUI Call来间接获取到framework目录下的Telecomm Call,Telecomm Call是在InCallUI Call实例创建时传递进来的。

步骤9: (frameworks\base\telecomm\java\android\telecom目录下)Call.java的disconnect()方法

/**
* Instructs this {@code Call} to disconnect.
*/
public void disconnect() {
mInCallAdapter.disconnectCall(mTelecomCallId);
}


mInCallAdapter是InCallAdapter类型的。

步骤10: (frameworks\base\telecomm\java\android\telecom目录下)InCallAdapter.java的disconnectCall()方法

/**
* Instructs Telecom to disconnect the specified call.
*
* @param callId The identifier of the call to disconnect.
*/
public void disconnectCall(String callId) {
try {
mAdapter.disconnectCall(callId);
} catch (RemoteException e) {
}
}


mAdapter是IInCallAdapter类型的。

步骤11,12和13: (packages\services\telecomm\src\com\android\server\telecom目录下)InCallAdapter.java的disconnectCall()方法

@Override
public void disconnectCall(String callId) {
Log.v(this, "disconnectCall: %s", callId);
if (mCallIdMapper.isValidCallId(callId)) {
mHandler.obtainMessage(MSG_DISCONNECT_CALL, callId).sendToTarget();
}
}


在这里通过obtainMessage()方法创建了一个消息类型为
MSG_DISCONNECT_CALL
的Message,再调用sendToTarget()方法把Message发送出去。

步骤14: InCallAdapter.java内部类InCallAdapterHandler的handleMessage()方法

private final class InCallAdapterHandler extends Handler {
@Override
public void handleMessage(Message msg) {
Call call;
switch (msg.what) {
...
case MSG_DISCONNECT_CALL:
call = mCallIdMapper.getCall(msg.obj);
if (call != null) {
mCallsManager.disconnectCall(call);
} else {
Log.w(this, "disconnectCall, unknown call id: %s", msg.obj);
}
break;
...
}


步骤15: CallsManager.java的disconnectCall()方法

/**
* Instructs Telecom to disconnect the specified call. Intended to be invoked by the
* in-call app through {@link InCallAdapter} for an ongoing call. This is usually triggered by
* the user hitting the end-call button.
*/
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();
}
}


步骤16和17: (packages\services\telecomm\src\com\android\server\telecom目录下)Call.java的disconnect()方法

/**
* Attempts to disconnect the call through the connection service.
*/
void disconnect() {
disconnectWithReason(DisconnectCause.LOCAL);
}

void disconnectWithReason(int disconnectCause) {
// Track that the call is now locally disconnecting.
Log.d(this, "disconnectWithReason dicsonnectcause %d", disconnectCause);
setLocallyDisconnecting(true);

if (mState == CallState.NEW || mState == CallState.PRE_DIAL_WAIT ||
mState == CallState.CONNECTING) {
Log.d(this, "Aborting call %s", this);
abort();
} else if (mState != CallState.ABORTED && mState != CallState.DISCONNECTED) {
Log.d(this, "disconnectWithReason %s", mConnectionService);
if (mConnectionService == null) {
Log.e(this, new Exception(), "disconnect() request on a call without a"
+ " connection service.");
} else {
Log.d(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.disconnectWithReason(this, disconnectCause);
}
}
}


disconnect()
方法里把挂断电话的原因设置成
DisconnectCause.LOCAL
。还有就是mConnectionService是ConnectionServiceWrapper类型的。

步骤18: ConnectionServiceWrapper.java的disconnectWithReason()方法

void disconnectWithReason(Call call, int disconnectCause) {
final String callId = mCallIdMapper.getCallId(call);
if (callId != null && isServiceValid("disconnect")) {
try {
logOutgoing("disconnect %s", callId);
mServiceInterface.disconnectWithReason(callId, disconnectCause);
} catch (RemoteException e) {
}
}
}


mServiceInterface是IConnectionService类型的。

步骤19,20和21: ConnectionService.java里mBinder的disconnectWithReason()方法

private final IBinder mBinder = new IConnectionService.Stub() {
...
@Override
public void disconnectWithReason(String callId, int disconnectCause) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = callId;
args.argi1 = disconnectCause;
mHandler.obtainMessage(MSG_DISCONNECT_REASON, args).sendToTarget();
}
...
}


在这里通过obtainMessage()方法创建了一个消息类型为
MSG_DISCONNECT_REASON
的Message,并且通过sendToTarget()方法把Message发送出去。

步骤22: ConnectionService.java里mHandler的handleMessage()方法

private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
...
case MSG_DISCONNECT_REASON: {
SomeArgs args = (SomeArgs) msg.obj;
try {
String callId = (String) args.arg1;
int disconnectCause = args.argi1;
disconnectWithReason(callId, disconnectCause);
} finally {
args.recycle();
}
break;
}
...
}


步骤23: ConnectionService.java的disconnectWithReason()方法

private void disconnectWithReason(String callId, int disconnectCause) {
Log.d(this, "disconnectWithReason %s %d DisconnectCause", callId, disconnectCause);
if (mConnectionById.containsKey(callId)) {
findConnectionForAction(callId, "disconnect").onDisconnectWithReason(disconnectCause);
} else {
findConferenceForAction(callId, "disconnect").onDisconnectWithReason(disconnectCause);
}
}

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();
}

static synchronized Connection getNullConnection() {
if (sNullConnection == null) {
sNullConnection = new Connection() {};
}
return sNullConnection;
}


通过findConnectionForAction获得一个Connection实例,在这里获得的是TelephonyConnection。

步骤24和25: TelephonyConnection.java的disconnectWithReason()方法

@Override
public void onDisconnectWithReason(int disconnectCause) {
Log.v(this, "onDisconnect");
hangup(DisconnectCauseUtil.toTelephonyDisconnectCauseCode(disconnectCause));
}

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.hangupWithReason(telephonyDisconnectCode);
} 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.hangupWithReason(telephonyDisconnectCode);
}
} catch (CallStateException e) {
Log.e(this, e, "Call to Connection.hangup failed with exception");
}
}
}


步骤26和27: CDMAConnection.java的hangupWithReason()方法

@Override
public void hangupWithReason(int disconnectCause) throws CallStateException {
//TODO not passing disconnect cause in CS.
hangup();
}

@Override
public void hangup() throws CallStateException {
if (!mDisconnected) {
mOwner.hangup(this);
} else {
throw new CallStateException ("disconnected");
}
}


注意,此时
mOwner.hangup(this);
传递的参数是this,也就是CdmaConnection类型的。

步骤28和29: CdmaCallTracker.java的hangup()方法

//***** Called from CdmaConnection
/*package*/ void
hangup (CdmaConnection conn) throws CallStateException {
if (conn.mOwner != this) {
throw new CallStateException ("CdmaConnection " + conn
+ "does not belong to CdmaCallTracker " + this);
}

if (conn == mPendingMO) {
// We're hanging up an outgoing call that doesn't have it's
// GSM index assigned yet

if (Phone.DEBUG_PHONE) log("hangup: set hangupPendingMO to true");
mHangupPendingMO = true;
} else if ((conn.getCall() == mRingingCall)
&& (mRingingCall.getState() == CdmaCall.State.WAITING)) {
// Handle call waiting hang up case.
//
// The ringingCall state will change to IDLE in CdmaCall.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.getCDMAIndex(), obtainCompleteMessage());
} catch (CallStateException ex) {
// Ignore "connection not found"
// Call may have hung up already
Rlog.w(LOG_TAG,"CdmaCallTracker WARN: hangup() on absent connection "
+ conn);
}
}

conn.onHangupLocal();
}

/**
* Obtain a message to use for signalling "invoke getCurrentCalls() when
* this operation and all other pending operations are complete
*/
private Message
obtainCompleteMessage() {
return obtainCompleteMessage(EVENT_OPERATION_COMPLETE);
}


通过obtainCompleteMessage()创建了一个消息类型为
EVENT_OPERATION_COMPLETE
的Message作为
mCi.hangupConnection
的一个参数,其中mCi是RIL.java的实例。

步骤30: RIL.java的hangupConnection()方法

@Override
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);

rr.mParcel.writeInt(1);
rr.mParcel.writeInt(gsmIndex);

send(rr);
}


RILJ封装了一个
RIL_REQUEST_HANGUP
类型的消息,并且通过
send(rr);
发送给RILD,RILD继续处理。

步骤31和32: 当RILJ接收到RILD的回应时,在它的processResponse()方法有
RIL_REQUEST_HANGUP
相关的逻辑处理,最后将消息发送给rr. result对应的handler处理(rr. result就是hangupConnection传递进来的Message参数),即由CdmaCallTracker中的handleMessage()方法进行处理。

步骤33~35: CdmaCallTracker.java的handleMessage()方法

public void
handleMessage (Message msg) {
...
switch (msg.what) {
case EVENT_OPERATION_COMPLETE:
operationComplete();
break;
...
}

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,"CdmaCallTracker.pendingOperations < 0");
mPendingOperations = 0;
}
}


通过obtainMessage()创建了一个消息类型为
EVENT_POLL_CALLS_RESULT
的Message,然后传递给
mCi.getCurrentCalls()


步骤36,37和38:调用RIL.java的getCurrentCalls()方法发出查询Call List状态列表的请求,底层查询完之后返回的结果还是交给CdmaCallTracker中的handleMessage()方法进行处理。

步骤39和40: CdmaCallTracker.java的handleMessage()方法

public void
handleMessage (Message msg) {
...
switch (msg.what) {
case EVENT_POLL_CALLS_RESULT:{
Rlog.d(LOG_TAG, "Event EVENT_POLL_CALLS_RESULT Received");
ar = (AsyncResult)msg.obj;

if(msg == mLastRelevantPoll) {
if(DBG_POLL) log(
"handle EVENT_POLL_CALL_RESULT: set needsPoll=F");
mNeedsPoll = false;
mLastRelevantPoll = null;
handlePollCalls((AsyncResult)msg.obj);
}
}
break;
...
}

@Override
protected void
handlePollCalls(AsyncResult ar) {
// clear the "local hangup" and "missed/rejected call"
// cases from the "dropped during poll" list
// These cases need no "last call fail" reason
for (int i = mDroppedDuringPoll.size() - 1; i >= 0 ; i--) {
CdmaConnection conn = mDroppedDuringPoll.get(i);

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);
} else if (conn.mCause == DisconnectCause.LOCAL
|| conn.mCause == DisconnectCause.INVALID_NUMBER) {
mDroppedDuringPoll.remove(i);
hasAnyCallDisconnected |= conn.onDisconnect(conn.mCause);
}
}
}


关于handlePollCalls()方法更详细的讲解请看《handlePollCalls方法详解

在步骤16就可以知道电话挂断的原因是DisconnectCause.LOCAL,因此下一步会调用
conn.onDisconnect(conn.mCause)


步骤41: CDMAConnection.java的onDisconnect()方法

/**
* Called when the radio indicates the connection has been disconnected.
* @param cause call disconnect cause; values are defined in {@link DisconnectCause}
*/
/*package*/ boolean
onDisconnect(int cause) {
boolean changed = false;
mCause = cause;
if (!mDisconnected) {
doDisconnect();
if (VDBG) Rlog.d(LOG_TAG, "onDisconnect: cause=" + cause);
mOwner.mPhone.notifyDisconnect(this);
if (mParent != null) {
changed = mParent.connectionDisconnected(this);
}
}
releaseWakeLock();
return changed;
}


步骤42和43:这里主要通过
mPhone.notifyDisconnect(this);
通知监听了这个事件的人。

最后发觉是注册TelephonyConnection.java监听了
MSG_DISCONNECT
事件

getPhone().registerForDisconnect(mHandler, MSG_DISCONNECT, null);


因此,在TelephonyConnection.java里会有
MSG_DISCONNECT
相应的逻辑处理。

步骤44和45: TelephonyConnection.java里mHandler的handleMessage()方法

private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
...
case MSG_DISCONNECT:
updateState();
break;
...
}
}
void updateState() {
...
if (mOriginalConnectionState != newState) {
mOriginalConnectionState = 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:
if (mSsNotification != null) {
setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
mOriginalConnection.getDisconnectCause(),
mSsNotification.notificationType,
mSsNotification.code));
mSsNotification = null;
DisconnectCauseUtil.mNotificationCode = 0xFF;
DisconnectCauseUtil.mNotificationType = 0xFF;
}
resetDisconnectCause();
close();
break;
...
}
}
updateConnectionCapabilities();
updateAddress();
}


当前的State是
DISCONNECTED
,所以会执行Connection.java的setDisconnected方法;
close();
也会继续走一段流程,就是把当前Call对象从Call集合里remove掉,这段流程暂且不分析了。

步骤46: TelephonyConnection.java里mHandler的handleMessage()方法

/**
* Sets state to disconnected.
*
* @param disconnectCause The reason for the disconnection, as specified by
*         {@link DisconnectCause}.
*/
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);
}
}


在这里做了两件事

1、把状态设置成
STATE_DISCONNECTED
。(步骤47)

2、通知绑定了监听器的人电话已挂断。(步骤48,49和50,最后通知了ConnectionServiceWrapper.java)

步骤50~53: ConnectionServiceWrapper.java的setDisconnected方法

@Override
public void setDisconnected(String callId, DisconnectCause disconnectCause) {
logIncoming("setDisconnected %s %s", callId, disconnectCause);
if (mCallIdMapper.isValidCallId(callId) || mCallIdMapper.isValidConferenceId(callId)) {
Log.d(this, "disconnect call %s", callId);
SomeArgs args = SomeArgs.obtain();
args.arg1 = callId;
args.arg2 = disconnectCause;
mHandler.obtainMessage(MSG_SET_DISCONNECTED, args).sendToTarget();
}
}


通过obtainMessage()创建了一个消息类型为
MSG_SET_DISCONNECTED
的Message,

在mHandler的handleMessage()方法里有相应的处理

步骤54: CallsManager.java的markCallAsDisconnected()方法

/**
* Marks the specified call as STATE_DISCONNECTED and notifies the in-call app. If this was the
* last live call, then also disconnect from the in-call controller.
*
* @param disconnectCause The disconnect cause, see {@link android.telecomm.DisconnectCause}.
*/
void markCallAsDisconnected(Call call, DisconnectCause disconnectCause) {
call.setDisconnectCause(disconnectCause);
int prevState = call.getState();
setCallState(call, CallState.DISCONNECTED);
String activeSub = getActiveSubscription();
String conversationSub = getConversationSub();
String lchSub = IsAnySubInLch();

PhoneAccount phAcc =
getPhoneAccountRegistrar().getPhoneAccount(call.getTargetPhoneAccount());
if ((call.getTargetPhoneAccount() != null &&
call.getTargetPhoneAccount().getId().equals(activeSub)) &&
(phAcc != null) && (phAcc.isSet(PhoneAccount.LCH)) &&
(conversationSub != null) &&
(!conversationSub.equals(activeSub))) {
Log.d(this,"Set active sub to conversation sub");
setActiveSubscription(conversationSub);
} else if ((conversationSub == null) && (lchSub != null) &&
((prevState == CallState.CONNECTING) || (prevState == CallState.PRE_DIAL_WAIT)) &&
(call.getState() == CallState.DISCONNECTED)) {
Log.d(this,"remove sub with call from LCH");
updateLchStatus(lchSub);
setActiveSubscription(lchSub);
manageMSimInCallTones(false);
}

if ((call.getTargetPhoneAccount() != null) && (phAcc != null) &&
(phAcc.isSet(PhoneAccount.LCH))) {
Call activecall = getFirstCallWithStateUsingSubId(call.getTargetPhoneAccount().getId(),
CallState.RINGING, CallState.DIALING, CallState.ACTIVE, CallState.ON_HOLD);
Log.d(this,"activecall: " + activecall);
if (activecall == null) {
phAcc.unSetBit(PhoneAccount.LCH);
manageMSimInCallTones(false);
}
}
}


步骤55:把状态设置成了
DISCONNECTED


步骤56~66:最后通知监听者Call的状态已改变,一直传递到UI界面。最后由CallCardFragment的setCallState方法更新界面,显示已挂断的界面。

步骤67: 底层上报Call的状态已改变的消息。

步骤68: 发出查询Call list状态列表的请求。

步骤69: 底层返回Call list,由CdmaCallTracker进行处理。

主动挂断电话的流程就这么多了。

二、按电源键物理键挂断通话



步骤1: 在PhoneWindowManager.java (frameworks\base\policy\src\com \android\internal\policy\impl)的interceptKeyBeforeQueueing()里有对物理键的相关事件处理。

case KeyEvent.KEYCODE_POWER: {
result &= ~ACTION_PASS_TO_USER;
isWakeKey = false; // wake-up will be handled separately
if (down) {
boolean panic = mImmersiveModeConfirmation.onPowerKeyDown(interactive,
event.getDownTime(), isImmersiveMode(mLastSystemUiFlags));
if (panic) {
mHandler.post(mRequestTransientNav);
}
if (interactive && !mPowerKeyTriggered
&& (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
mPowerKeyTriggered = true;
mPowerKeyTime = event.getDownTime();
interceptScreenshotChord();
interceptScreenshotLog();

if (mButtonLightEnabled) {
try {
mLight.setButtonLightEnabled(false);
} catch(RemoteException e) {
Slog.e(TAG, "remote call for turn off button light failed.");
}
}
}

TelecomManager telecomManager = getTelecommService();
boolean hungUp = false;
if (telecomManager != null) {
if (telecomManager.isRinging()) {
// Pressing Power while there's a ringing incoming
// call should silence the ringer.
telecomManager.silenceRinger();
} else if ((mIncallPowerBehavior
& Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0
&& telecomManager.isInCall() && interactive) {
// Otherwise, if "Power button ends call" is enabled,
// the Power button will hang up any current active call.
hungUp = telecomManager.endCall();
}  else {
mContext.sendBroadcast(new Intent(POWER_KEYDOWN_MUTE));
}
}
interceptPowerKeyDown(event, interactive);
} else {
interceptPowerKeyUp(event, interactive, canceled);
}
break;
}


如果正在通话中,电源键被点击,则说明用户想挂断电话(用户开了辅助功能的话),则会调用telecomManager.endCall()来挂断电话。

步骤2: TelecomManager.java的endCall()方法

/**
* Ends an ongoing call.
* TODO: L-release - need to convert all invocations of ITelecomService#endCall to use this
* method (clockwork & gearhead).
* @hide
*/
@SystemApi
public boolean endCall() {
try {
if (isServiceConnected()) {
return getTelecomService().endCall();
}
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelecomService#endCall", e);
}
return false;
}


在这里通过getTelecomService()方法获取到ITelecomService接口,远程调用TelecomService.java的endCall()方法

步骤3~5: TelecomService.java的endCall()方法

/**
* @see android.telecom.TelecomManager#endCall
*/
@Override
public boolean endCall() {
enforceModifyPermission();
return (boolean) sendRequest(MSG_END_CALL);
}

/**
* Posts the specified command to be executed on the main thread, waits for the request to
* complete, and returns the result.
*/
private Object sendRequest(int command) {
if (Looper.myLooper() == mMainThreadHandler.getLooper()) {
MainThreadRequest request = new MainThreadRequest();
mMainThreadHandler.handleMessage(mMainThreadHandler.obtainMessage(command, request));
return request.result;
} else {
MainThreadRequest request = sendRequestAsync(command, 0);

// Wait for the request to complete
synchronized (request) {
while (request.result == null) {
try {
request.wait();
} catch (InterruptedException e) {
// Do nothing, go back and wait until the request is complete
}
}
}
return request.result;
}
}


在这里通过obtainMessage()方法创建了一个消息类型为
MSG_END_CALL
的Message,然后传递给mMainThreadHandler.handleMessage()方法,让它处理这个Message。

步骤6~8: TelecomService.java的endCall()方法

private final class MainThreadHandler extends Handler {
@Override
public void handleMessage(Message msg) {
if (msg.obj instanceof MainThreadRequest) {
MainThreadRequest request = (MainThreadRequest) msg.obj;
Object result = null;
switch (msg.what) {
...
case MSG_END_CALL:
result = endCallInternal();
break;
...
}

private boolean endCallInternal() {
// Always operate on the foreground call if one exists, otherwise get the first call in
// priority order by call-state.
Call call = mCallsManager.getForegroundCall();
if (call == null) {
call = mCallsManager.getFirstCallWithState(
CallState.ACTIVE,
CallState.DIALING,
CallState.RINGING,
CallState.ON_HOLD);
}

if (call != null) {
if (call.getState() == CallState.RINGING) {
call.reject(false /* rejectWithMessage */, null);
} else {
call.disconnect();
}
return true;
}

return false;
}


MSG_END_CALL消息是由endCallInternal()来处理,如果当前正在响铃的话就执行拒接电话的流程;否则的话,就调用call.disconnect()执行挂断电话的流程。这里的Call.java是在packages\services\telecomm\src\com\android\server\telecom目录下的。

步骤9:之后的流程跟点击挂断按钮方式的挂断流程步骤16~69相同,在这里就不重复了

三、远程挂断通话



RILJ收到Call状态变化通知RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED,接着通知CdmaCallTracker,CdmaCallTracker则在pollCallsWhenSafe方法里调用RILJ的getCurrentCalls方法请求查询Call状态列表,底层处理完之后发出通知,CdmaCallTracker在handleMessage里进行响应,然后进入handlePollCalls方法里

1. CdmaCallTracker.java的handlePollCalls()方法

上面的流程跟接电话的流程都是有很多相似的,所以这里一笔带过,重点关注CdmaCallTracker的handlePollCalls方法

protected void
handlePollCalls(AsyncResult ar){
...
if (mDroppedDuringPoll.size() > 0) {
mCi.getLastCallFailCause(
obtainNoPollCompleteMessage(
EVENT_GET_LAST_CALL_FAIL_CAUSE));
}
...
}


2. RIL.java的getLastCallFailCause()方法

在这里会先调用RIL的getLastCallFailCause方法向RIL查询通话断开的原因,注意这里传进来了一个消息类型为EVENT_GET_LAST_CALL_FAIL_CAUSE的Message

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);
}


对应的log是

08-04 13:22:45.842 D/RILJ    ( 3034): [5549]> LAST_CALL_FAIL_CAUSE


3. RIL.java的processSolicited()方法

RIL向底层发送了请求,底层处理完成之后,就会把处理的结果返回给RIL,RIL.java在processSolicited方法里处理

private RILRequest processSolicited (Parcel p) {
...
case RIL_REQUEST_LAST_CALL_FAIL_CAUSE:
ret =  responseInts(p); break;
...
//打印log日志
if (RILJ_LOGD) riljLog(rr.serialString() + "< " +
requestToString(rr.mRequest)
+ " " + retToString(rr.mRequest, ret));
if (rr.mResult != null) {
AsyncResult.forMessage(rr.mResult, null, tr);
rr.mResult.sendToTarget();//发出handler消息通知
}


对应的log是

08-04 13:22:45.847 D/RILJ    ( 3034): [5549]< LAST_CALL_FAIL_CAUSE {16}


4. CdmaCallTracker.java的handleMessage()方法

rr.mResult.sendToTarget()发出handler消息通知后,会在CdmaCallTracker中的handleMessage方法中响应。

public void
handleMessage (Message msg) {
...
switch (msg.what) {
case EVENT_GET_LAST_CALL_FAIL_CAUSE:
int causeCode;
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 {
causeCode = ((int[])ar.result)[0];
}

for (int i = 0, s =  mDroppedDuringPoll.size()
; i < s ; i++
) {
CdmaConnection conn = mDroppedDuringPoll.get(i);

conn.onRemoteDisconnect(causeCode);
}
//更新phone状态
updatePhoneState();

mPhone.notifyPreciseCallStateChanged();
//清除断开的通话连接
mDroppedDuringPoll.clear();
break;
...
}


conn.onRemoteDisconnect(causeCode);通话断开的原因就是causeCode,conn是CdmaConnection类型的,下一步进入CdmaConnection.java

5. CdmaConnection.java的onRemoteDisconnect()方法

void onRemoteDisconnect(int causeCode) {
onDisconnect(disconnectCauseFromCode(causeCode));
}

/** Called when the radio indicates the connection has been disconnected */
boolean onDisconnect(DisconnectCause cause) {
boolean changed = false;
mCause = cause;
if (!mDisconnected) {
doDisconnect();
if (VDBG) Rlog.d(LOG_TAG, "onDisconnect: cause=" + cause);
mOwner.mPhone.notifyDisconnect(this);
if (mParent != null) {
changed = mParent.connectionDisconnected(this);
}
}
releaseWakeLock();
return changed;
}


看到这一行mPhone.notifyDisconnect(this); 因为我们这里研究的是CDMA,所以mPhone就是PhoneBase的其中一个子类CDMAPhone

6. CDMAPhone.java的notifyDisconnect()方法

void notifyDisconnect(Connection cn) {
mDisconnectRegistrants.notifyResult(cn);
}


我们要找出mDisconnectRegistrants被调用的地方,我们先来到CallManager.java的registerForDisconnect方法里,继续找,我们要找的是“phone.registerForDisconnect”,在CallManager.java找到下面这行:

phone.registerForDisconnect(mHandler, EVENT_DISCONNECT, null);


可以知道,是CallManager.java监听了EVENT_DISCONNECT事件,所以会在它的handleMessage方法里响应这个消息

7. CallManager.java的handleMessage方法

public void handleMessage(Message msg) {
switch (msg.what) {
case EVENT_DISCONNECT:
if (VDBG) Rlog.d(LOG_TAG, " handleMessage (EVENT_DISCONNECT)");
mDisconnectRegistrants.notifyRegistrants((
AsyncResult) msg.obj);
break;
...
}


又是mDisconnectRegistrants,还是要找它的被调用的地方,不过这次要找的是“callManager.registerForDisconnect”,在CallStateMonitor.java里找到这行:

callManager.registerForDisconnect(this, PHONE_DISCONNECT, null);


这里有个要注意的地方,CallStateMonitor并不是Handler的子类,所以它没有handleMessage方法,那么它监听了PHONE_DISCONNECT事件,在哪里响应并且处理它呢,我们要找PHONE_DISCONNECT被调用的地方,在CallNotifier.java的handleMessage方法里找到了

public void handleMessage(Message msg) {
switch (msg.what) {
...
case CallStateMonitor.PHONE_DISCONNECT:
if (DBG) log("DISCONNECT");
onDisconnect((AsyncResult) msg.obj);
break;
...
}


所以CallNotifier会响应PHONE_DISCONNECT事件。

最后,贴出上面分析的远程挂断通话流程的log

08-04 13:22:45.816 D/RILJ    ( 3034):[UNSL]< UNSOL_RESPONSE_CALL_STATE_CHANGED
08-04 13:22:45.816 D/RILJ    ( 3034): [5548]> GET_CURRENT_CALLS
08-04 13:22:45.832 D/RILJ    ( 3034): [5548]< GET_CURRENT_CALLS
08-04 13:22:45.842 D/RILJ    ( 3034): [5549]> LAST_CALL_FAIL_CAUSE
08-04 13:22:45.845 D/CdmaCallTracker( 3034):
[CdmaCallTracker] update phone state, old=OFFHOOK new=OFFHOOK
08-04 13:22:45.847 D/RILJ    ( 3034): [5549]< LAST_CALL_FAIL_CAUSE {16}
08-04 13:22:45.857 D/CdmaCallTracker( 3034):
[CdmaCallTracker] update phone state, old=OFFHOOK new=IDLE
08-04 13:22:46.361 D/CdmaCallTracker( 3034):
[CdmaCallTracker] update phone state, old=IDLE new=IDLE
08-04 13:22:46.469 D/CallNotifier( 3034): DISCONNECT
08-04 13:22:46.469 D/CallNotifier( 3034): onDisconnect()...  CallManager state: IDLE
08-04 13:22:46.470 D/CallNotifier( 3034):
onDisconnect: cause = NORMAL, incoming = true, date = 1438665738497
08-04 13:22:46.502 D/CallNotifier( 3034):
stopSignalInfoTone: Stopping SignalInfo tone player
08-04 13:22:46.504 D/CallNotifier( 3034): stopRing()... (onDisconnect)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息