Android7.0 Phone应用源码分析 phone拒接流程分析

大道之行,天下为公。这篇文章主要讲述Android7.0 Phone应用源码分析 phone拒接流程分析相关的知识,希望能为你提供帮助。

Android7.0 Phone应用源码分析 phone拒接流程分析

文章图片
android7.0 phone拒接流程分析--- 本文为原创文章,转载请注明出处,http://www.cnblogs.com/lance2016/p/6391096.html接上篇博文:Android7.0 Phone应用源码分析(二) phone来电流程分析
今天我们再来分析下Android7.0 的phone的拒接流程
Android7.0 Phone应用源码分析 phone拒接流程分析

文章图片

下面先来看一下拒接电话流程时序图
Android7.0 Phone应用源码分析 phone拒接流程分析

文章图片

步骤1:滑动按钮到拒接图标,会调用到AnswerFragment的onDecline方法
com.android.incallui.AnswerFragment

public void onDecline(Context context) { getPresenter().onDecline(context); }

最后是调用到AnswerPresenteronDecline方法
com.android.incallui.AnswerPresenter public void onDecline(Context context) { Log.d(this, "onDecline " + mCallId); if (mCall.getSessionModificationState() == Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST) { InCallPresenter.getInstance().declineUpgradeRequest(context); } else { TelecomAdapter.getInstance().rejectCall(mCall.getId(), false, null); } }

步骤2:进入TelecomAdapter的rejectCall方法
com.android.incallui.TelecomAdapter void rejectCall(String callId, boolean rejectWithMessage, String message) { android.telecom.Call call = getTelecomCallById(callId); if (call != null) { call.reject(rejectWithMessage, message); } else { Log.e(this, "error rejectCall, call not in call list: " + callId); } }

TelecomAdapter是incallui与telecom通信的代理类,这里通过callid取出对应的Call对象(android.telecom.Call)
步骤3:调用到framework里Call的reject方法
android.telecom.Call public void reject(boolean rejectWithMessage, String textMessage) { mInCallAdapter.rejectCall(mTelecomCallId, rejectWithMessage, textMessage); }

这里mInCallAdapter是android.telecom.InCallAdapter类,是在Call对象创建的时候由外部传入的参数
在telecom绑定InCallService服务的时候,会传递一个AIDL接口对象,InCallService会生成InCallAdapter对象来保存这个接口对象
步骤4:InCallAdapter的rejectCall方法
android.telecom.InCallAdapter

public void rejectCall(String callId, boolean rejectWithMessage, String textMessage) { try { mAdapter.rejectCall(callId, rejectWithMessage, textMessage); } catch (RemoteException e) { } }

mAdapter就是incallui与telecom通信的AIDL接口
步骤5:跨进程调用进入telecom进程,该AIDL接口具体实现类是InCallAdapter,虽然类名一样但是不同的包名,这里需要注意一下
com.android.server.telecom.InCallAdapter public void rejectCall(String callId, boolean rejectWithMessage, String textMessage) { try { Log.startSession("ICA.rC", mOwnerComponentName); long token = Binder.clearCallingIdentity(); try { synchronized (mLock) { Log.d(this, "rejectCall(%s,%b,%s)", callId, rejectWithMessage, textMessage); Call call = mCallIdMapper.getCall(callId); if (call != null) { mCallsManager.rejectCall(call, rejectWithMessage, textMessage); } else { Log.w(this, "setRingback, unknown call id: %s", callId); } } } finally { Binder.restoreCallingIdentity(token); } } finally { Log.endSession(); } }

这里同样是根据callid取出对应Call(com.android.server.telecom.Call),最后调用CallsManager的rejectCall方法传入call
步骤6:CallsManager的rejectCall方法
com.android.server.telecom.CallsManager public void rejectCall(Call call, boolean rejectWithMessage, String textMessage) { if (!mCalls.contains(call)) { Log.i(this, "Request to reject a non-existent call %s", call); } else { for (CallsManagerListener listener : mListeners) { listener.onIncomingCallRejected(call, rejectWithMessage, textMessage); } call.reject(rejectWithMessage, textMessage); } }

这里先通知观察者来电拒接事件,比如CallAudioManager对该事件感兴趣,它的处理是停止播放来电铃声和来电等待声
com.android.server.telecom.CallAudioManager public void onIncomingCallRejected(Call call, boolean rejectWithMessage, String message) { maybeStopRingingAndCallWaitingForAnsweredOrRejectedCall(call); } private void maybeStopRingingAndCallWaitingForAnsweredOrRejectedCall(Call call) { // Check to see if the call being answered/rejected is the only ringing call, since this // will be called before the connection service acknowledges the state change. if (mRingingCalls.size() == 0 || (mRingingCalls.size() == 1 & & call == mRingingCalls.iterator().next())) { mRinger.stopRinging(); mRinger.stopCallWaiting(); } }

最后再调用前面传进来的call对象的reject方法
步骤7:Call的reject方法
com.android.server.telecom.Call public void reject(boolean rejectWithMessage, String textMessage) { Preconditions.checkNotNull(mConnectionService); // Check to verify that the call is still in the ringing state. A call can change states // between the time the user hits \'reject\' and Telecomm receives the command. if (isRinging("reject")) { // Ensure video state history tracks video state at time of rejection. mVideoStateHistory |= mVideoState; mConnectionService.reject(this, rejectWithMessage, textMessage); Log.event(this, Log.Events.REQUEST_REJECT); } }

这里的mConnectionService是ConnectionServiceWrapper类,是telecom与telephony通信的代理类
步骤8:ConnectionServiceWrapper的reject方法
com.android.server.telecom.ConnectionServiceWrapper void reject(Call call, boolean rejectWithMessage, String message) { final String callId = mCallIdMapper.getCallId(call); if (callId != null & & isServiceValid("reject")) { try { logOutgoing("reject %s", callId); if (rejectWithMessage & & call.can( Connection.CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION)) { mServiceInterface.rejectWithMessage(callId, message); } else { mServiceInterface.reject(callId); } } catch (RemoteException e) { } } }

这里mServiceInterface就是telephony提供给telecom调用的AIDL接口
步骤9:跨进程调用进入telephony进程,telephony进程实际服务类是TelephonyConnectionService继承于ConnectionService类在manifest声明如下:
< service android:singleUser="true" android:name="com.android.services.telephony.TelephonyConnectionService" android:label="@string/pstn_connection_service_label" android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE" > < intent-filter> < action android:name="android.telecom.ConnectionService" /> < /intent-filter> < /service>

而AIDL接口具体实现是其父类ConnectionService的mBinder成员变量
android.telecom.ConnectionService private final IBinder mBinder = new IConnectionService.Stub() {@Override public void reject(String callId) { mHandler.obtainMessage(MSG_REJECT, callId).sendToTarget(); } }

步骤10~13:发送MSG_REJECT消息到队列里处理
private void reject(String callId) { Log.d(this, "reject %s", callId); findConnectionForAction(callId, "reject").onReject(); } 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),调用onReject方法
步骤14:TelephonyConnection继承于connection
com.android.services.telephony.TelephonyConnection public void onReject() { Log.v(this, "onReject"); if (isValidRingingCall()) { hangup(android.telephony.DisconnectCause.INCOMING_REJECTED); } super.onReject(); }

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

步骤15,16:这获取mOriginalConnection的call(com.android.internal.telephony.Call)对象,并调用hangup方法
protected Call getCall() { if (mOriginalConnection != null) { return mOriginalConnection.getCall(); } return null; }

Call是抽象类,具体子类是GsmCdmaCall
com.android.internal.telephony.GsmCdmaCall public void hangup() throws CallStateException { mOwner.hangup(this); }

mOwner是GsmCdmaCallTracker对象
步骤17:GsmCdmaCallTracker的hangup方法
com.android.internal.telephony.GsmCdmaCallTracker public void hangup(GsmCdmaCall call) throws CallStateException { if (call.getConnections().size() == 0) { throw new CallStateException("no connections in call"); }if (call == mRingingCall) { if (Phone.DEBUG_PHONE) log("(ringing) hangup waiting or background"); mCi.hangupWaitingOrBackground(obtainCompleteMessage()); } else if (call == mForegroundCall) { if (call.isDialingOrAlerting()) { if (Phone.DEBUG_PHONE) { log("(foregnd) hangup dialing or alerting..."); } hangup((GsmCdmaConnection)(call.getConnections().get(0))); } else if (isPhoneTypeGsm() & & mRingingCall.isRinging()) { // Do not auto-answer ringing on CHUP, instead just end active calls log("hangup all conns in active/background call, without affecting ringing call"); hangupAllConnections(call); } else { hangupForegroundResumeBackground(); } } else if (call == mBackgroundCall) { if (mRingingCall.isRinging()) { if (Phone.DEBUG_PHONE) { log("hangup all conns in background call"); } hangupAllConnections(call); } else { hangupWaitingOrBackground(); } } else { throw new RuntimeException ("GsmCdmaCall " + call + "does not belong to GsmCdmaCallTracker " + this); }call.onHangupLocal(); mPhone.notifyPreciseCallStateChanged(); }

由于是ringcall,这里调用mCi.hangupWaitingOrBackground(obtainCompleteMessage());
mCi是CommandsInterface即RILJ接口,包装了一个EVENT_OPERATION_COMPLETE回调消息,发送给RIL
步骤18:RIL的hangupWaitingOrBackground方法
com.android.internal.telephony.RIL

hangupWaitingOrBackground (Message result) { RILRequest rr = RILRequest.obtain(RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND, result); if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); mEventLog.writeRilHangup(rr.mSerial, RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND, -1); send(rr); }

给RIL层发送RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND消息
步骤19:mPhone.notifyPreciseCallStateChanged通知Phone状态监听事件
步骤20~24:收到RIL层的回应消息并处理,最后发送回调消息EVENT_OPERATION_COMPLETE给GsmCdmaCallTracker
步骤25: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
步骤26~32:RIL返回消息,GsmCdmaCallTracker接收EVENT_POLL_CALLS_RESULT消息并处理
protected synchronized void handlePollCalls(AsyncResult ar) { ...................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; }................... ...................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(); }}

这里设置DisconnectCause.INCOMING_REJECTED为连接断开的cause并调用GsmCdmaConnection的onDisconnect方法
步骤33: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 voiddoDisconnect() { mIndex = -1; mDisconnectTime = System.currentTimeMillis(); mDuration = SystemClock.elapsedRealtime() - mConnectTimeReal; mDisconnected = true; clearPostDialListeners(); }

最后通知注册者断开事件mOwner.getPhone().notifyDisconnect(this);
步骤34,36:通知phone状态变化事件给相关监听者
步骤35:GsmCdmaPhone通知通话断开事件
com.android.internal.telephony.GsmCdmaPhone public void notifyDisconnect(Connection cn) { mDisconnectRegistrants.notifyResult(cn); mNotifier.notifyDisconnectCause(cn.getDisconnectCause(), cn.getPreciseDisconnectCause()); }

步骤37~40:TelephonyConnection注册了断开事件监听,接收并处理断开消息
com.android.services.telephony.TelephonyConnection void setOriginalConnection(com.android.internal.telephony.Connection originalConnection) { Log.v(this, "new TelephonyConnection, originalConnection: " + originalConnection); ...... getPhone().registerForDisconnect(mHandler, MSG_DISCONNECT, null); ...... }

void updateState() { if (mOriginalConnection == null) { return; }updateStateInternal(); updateStatusHints(); updateConnectionCapabilities(); updateConnectionProperties(); updateAddress(); updateMultiparty(); }

void updateStateInternal() { if (mOriginalConnection == null) { return; } Call.State newState; // If the state is overridden and the state of the original connection hasn\'t changed since, // then we continue in the overridden state, else we go to the original connection\'s state. if (mIsStateOverridden & & mOriginalConnectionState == mOriginalConnection.getState()) { newState = mConnectionOverriddenState; } else { newState = mOriginalConnection.getState(); } Log.v(this, "Update state from %s to %s for %s", mConnectionState, newState, this); if (mConnectionState != newState) { mConnectionState = 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: setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause( mOriginalConnection.getDisconnectCause(), mOriginalConnection.getVendorDisconnectCause())); close(); break; case DISCONNECTING: break; } } }

通过DisconnectCauseUtil的toTelecomDisconnectCause方法生成DisconnectCause(android.telecom.DisconnectCause)对象
包含code, label, description, reason,toneToPlay信息
步骤41,42:通知外部监听者断开事件mNotifier.notifyDisconnectCause
步骤43:调用父类Connection的setDisconnected方法
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); } }

回调通知观察者ConnectionService注册了该事件,mConnectionListener接收处理
步骤44:mConnectionListener处理onDisconnected事件
android.telecom.ConnectionService private final Connection.Listener mConnectionListener = new Connection.Listener() { ...... @Override public void onDisconnected(Connection c, DisconnectCause disconnectCause) { String id = mIdByConnection.get(c); Log.d(this, "Adapter set disconnected %s", disconnectCause); mAdapter.setDisconnected(id, disconnectCause); }}

根据connection对象取出对应的callid
步骤45:TelephonyConnection的updateAddress方法更新connection信息
步骤46:ConnectionServiceAdapter的setDisconnected方法
android.telecom.ConnectionServiceAdapter void setDisconnected(String callId, DisconnectCause disconnectCause) { for (IConnectionServiceAdapter adapter : mAdapters) { try { adapter.setDisconnected(callId, disconnectCause); } catch (RemoteException e) { } } }

telecom在绑定TelephonyConnectionService的时候,会设置AIDL回调接口对象给telephony即ConnectionServiceWrapper的Adapter成员变量
步骤47:跨进程调用到telecom进程,ConnectionServiceWrapper的Adapter处理setDisconnected
com.android.server.telecom.ConnectionServiceWrapper private final class Adapter extends IConnectionServiceAdapter.Stub { ...... @Override public void setDisconnected(String callId, DisconnectCause disconnectCause) { Log.startSession("CSW.sD"); long token = Binder.clearCallingIdentity(); try { synchronized (mLock) { logIncoming("setDisconnected %s %s", callId, disconnectCause); Call call = mCallIdMapper.getCall(callId); Log.d(this, "disconnect call %s %s", disconnectCause, call); if (call != null) { mCallsManager.markCallAsDisconnected(call, disconnectCause); } else { // Log.w(this, "setDisconnected, unknown call id: %s", args.arg1); } } } finally { Binder.restoreCallingIdentity(token); Log.endSession(); } } ...... }

根据callid取出Call(com.android.server.telecom.Call)对象,给CallsManager传递Call和disconnectCause
步骤48,49,50:CallsManager的markCallAsDisconnected方法
com.android.server.telecom.CallsManager void markCallAsDisconnected(Call call, DisconnectCause disconnectCause) { call.setDisconnectCause(disconnectCause); setCallState(call, CallState.DISCONNECTED, "disconnected set explicitly"); }

给Call设置disconnectCause,同时设置callstate
private void setCallState(Call call, int newState, String tag) { if (call == null) { return; } int oldState = call.getState(); Log.i(this, "setCallState %s -> %s, call: %s", CallState.toString(oldState), CallState.toString(newState), call); if (newState != oldState) { // Unfortunately, in the telephony world the radio is king. So if the call notifies // us that the call is in a particular state, we allow it even if it doesn\'t make // sense (e.g., STATE_ACTIVE -> STATE_RINGING). // TODO: Consider putting a stop to the above and turning CallState // into a well-defined state machine. // TODO: Define expected state transitions here, and log when an // unexpected transition occurs. call.setState(newState, tag); maybeShowErrorDialogOnDisconnect(call); Trace.beginSection("onCallStateChanged"); // Only broadcast state change for calls that are being tracked. if (mCalls.contains(call)) { updateCallsManagerState(); for (CallsManagerListener listener : mListeners) { if (Log.SYSTRACE_DEBUG) { Trace.beginSection(listener.getClass().toString() + " onCallStateChanged"); } listener.onCallStateChanged(call, oldState, newState); if (Log.SYSTRACE_DEBUG) { Trace.endSection(); } } } Trace.endSection(); } }

最后回调onCallStateChanged方法通知监听者,这里监听call状态变化的对象有很多,我们看下InCallController的处理
步骤51,52:InCallController的onCallStateChanged方法
com.android.server.telecom.InCallController @Override public void onCallStateChanged(Call call, int oldState, int newState) { updateCall(call); }

private void updateCall(Call call, boolean videoProviderChanged) { if (!mInCallServices.isEmpty()) { ParcelableCall parcelableCall = ParcelableCallUtils.toParcelableCall( call, videoProviderChanged /* includeVideoProvider */, mCallsManager.getPhoneAccountRegistrar()); Log.i(this, "Sending updateCall %s ==> %s", call, parcelableCall); List< ComponentName> componentsUpdated = new ArrayList< > (); for (Map.Entry< ComponentName, IInCallService> entry : mInCallServices.entrySet()) { ComponentName componentName = entry.getKey(); IInCallService inCallService = entry.getValue(); componentsUpdated.add(componentName); try { inCallService.updateCall(parcelableCall); } catch (RemoteException ignored) { } } Log.i(this, "Components updated: %s", componentsUpdated); } } }

根据call信息生成ParcelableCall对象,给incallservice传递ParcelableCall
步骤53,54:InCallService的updateCall方法
android.telecom.InCallService @Override public void updateCall(ParcelableCall call) { mHandler.obtainMessage(MSG_UPDATE_CALL, call).sendToTarget(); }

private final Handler mHandler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { ...... case MSG_UPDATE_CALL: mPhone.internalUpdateCall((ParcelableCall) msg.obj); break; }

步骤55:Phone的internalUpdateCall方法
android.telecom.Phone final void internalUpdateCall(ParcelableCall parcelableCall) { Call call = mCallByTelecomCallId.get(parcelableCall.getId()); if (call != null) { checkCallTree(parcelableCall); call.internalUpdate(parcelableCall, mCallByTelecomCallId); } }

这里的Phone对象只是一个管理类,保存call列表信息和与telecom通信的AIDL接口对象,通过callid取出Call(android.telecom.Call)对象
【Android7.0 Phone应用源码分析 phone拒接流程分析】步骤56:Call的internalUpdate方法
android.telecom.Call final void internalUpdate(ParcelableCall parcelableCall, Map< String, Call> callIdMap) {Details details = Details.createFromParcelableCall(parcelableCall); ...... ......// Now we fire updates, ensuring that any client who listens to any of these notifications // gets the most up-to-date state.if (stateChanged) { fireStateChanged(mState); } if (details

    推荐阅读