編輯:關於android開發
接上篇博文:Android7.0 Phone應用源碼分析(二) phone來電流程分析
今天我們再來分析下Android7.0 的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 void doDisconnect() {
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)對象
步驟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 (detailsChanged) {
fireDetailsChanged(mDetails);
}
if (cannedTextResponsesChanged) {
fireCannedTextResponsesLoaded(mCannedTextResponses);
}
if (videoCallChanged) {
fireVideoCallChanged(mVideoCallImpl);
}
if (parentChanged) {
fireParentChanged(getParent());
}
if (childrenChanged) {
fireChildrenChanged(getChildren());
}
// If we have transitioned to DISCONNECTED, that means we need to notify clients and
// remove ourselves from the Phone. Note that we do this after completing all state updates
// so a client can cleanly transition all their UI to the state appropriate for a
// DISCONNECTED Call while still relying on the existence of that Call in the Phone's list.
if (mState == STATE_DISCONNECTED) {
fireCallDestroyed();
}
}
步驟57:轉化ParcelableCall 信息為Detail信息,判斷call狀態是否有變化,有則進入fireStateChanged
private void fireStateChanged(final int newState) {
for (CallbackRecord<Callback> record : mCallbackRecords) {
final Call call = this;
final Callback callback = record.getCallback();
record.getHandler().post(new Runnable() {
@Override
public void run() {
callback.onStateChanged(call, newState);
}
});
}
}
步驟58:這裡遍歷Call(android.telecom.Call)對象裡的回調監聽者
private final List<CallbackRecord<Callback>> mCallbackRecords = new CopyOnWriteArrayList<>();
也就是每次InCallPresenter添加Call(android.telecom.Call)時添加的注冊回調事件
com.android.incallui.InCallPresenter
public void onCallAdded(final android.telecom.Call call) {
if (shouldAttemptBlocking(call)) {
maybeBlockCall(call);
} else {
mCallList.onCallAdded(call);
}
// Since a call has been added we are no longer waiting for Telecom to send us a call.
setBoundAndWaitingForOutgoingCall(false, null);
call.registerCallback(mCallCallback);
}
這裡有兩個地方注冊了事件回調,一個是在CallList的onCallAdd方法裡轉化Call(android.telecom.Call)創建了Call(com.android.incallui.Call)對象時注冊的
com.android.incallui.CallList
public void onCallAdded(final android.telecom.Call telecomCall) {
Trace.beginSection("onCallAdded");
final Call call = new Call(telecomCall);
Log.d(this, "onCallAdded: callState=" + call.getState());
if (call.getState() == Call.State.INCOMING ||
call.getState() == Call.State.CALL_WAITING) {
onIncoming(call, call.getCannedSmsResponses());
} else {
onUpdate(call);
}
call.logCallInitiationType();
Trace.endSection();
}
com.android.incallui.Call
public Call(android.telecom.Call telecomCall) {
mTelecomCall = telecomCall;
mId = ID_PREFIX + Integer.toString(sIdCounter++);
updateFromTelecomCall();
mTelecomCall.registerCallback(mTelecomCallCallback);
mTimeAddedMs = System.currentTimeMillis();
}
還有就是InCallPresenter的成員變量mCallCallback的注冊
這裡onStateChange只有Call(com.android.incallui.Call)的成員變量mTelecomCallCallback有處理
com.android.incallui.Call
private final android.telecom.Call.Callback mTelecomCallCallback =
new android.telecom.Call.Callback() {
......
......
@Override
public void onStateChanged(android.telecom.Call call, int newState) {
Log.d(this, "TelecomCallCallback onStateChanged call=" + call + " newState="
+ newState);
update();
}
@Override
public void onCallDestroyed(android.telecom.Call call) {
Log.d(this, "TelecomCallCallback onStateChanged call=" + call);
call.unregisterCallback(this);
}
};
步驟59:進入Call(com.android.incallui.Call)的update方法
private void update() {
Trace.beginSection("Update");
int oldState = getState();
updateFromTelecomCall();
if (oldState != getState() && getState() == Call.State.DISCONNECTED) {
CallList.getInstance().onDisconnect(this);
} else {
CallList.getInstance().onUpdate(this);
}
Trace.endSection();
}
步驟61~69:由於是DISCONNECTED狀態,進入CallList的onDisconnect,最後回調到InCallPresenter的onDisconnect方法
com.android.incallui.InCallPresenter
@Override
public void onDisconnect(Call call) {
maybeShowErrorDialogOnDisconnect(call);
// We need to do the run the same code as onCallListChange.
onCallListChange(mCallList);
if (isActivityStarted()) {
mInCallActivity.dismissKeyguard(false);
}
if (call.isEmergencyCall()) {
FilteredNumbersUtil.recordLastEmergencyCallTime(mContext);
}
}
InCallPresenter內部更新call狀態事件並觸發回調通知,細節再次就不一一羅列了
回調步驟57在執行完fireStateChanged方法後,後續還有fireDetailsChanged等事件(如果有變化的話),這裡我們關注下fireCallDestroyed
android.telecom.Call
private void fireCallDestroyed() {
final Call call = this;
if (mCallbackRecords.isEmpty()) {
// No callbacks registered, remove the call from Phone's record.
mPhone.internalRemoveCall(call);
}
for (final CallbackRecord<Callback> record : mCallbackRecords) {
final Callback callback = record.getCallback();
record.getHandler().post(new Runnable() {
@Override
public void run() {
boolean isFinalRemoval = false;
RuntimeException toThrow = null;
try {
callback.onCallDestroyed(call);
} catch (RuntimeException e) {
toThrow = e;
}
synchronized(Call.this) {
mCallbackRecords.remove(record);
if (mCallbackRecords.isEmpty()) {
isFinalRemoval = true;
}
}
if (isFinalRemoval) {
mPhone.internalRemoveCall(call);
}
if (toThrow != null) {
throw toThrow;
}
}
});
}
}
這裡 callback.onCallDestroyed(call);通知call銷毀事件
步驟60:反注冊Call(android.telecom.Call )的監聽事件
com.android.incallui.Call
@Override
public void onCallDestroyed(android.telecom.Call call) {
Log.d(this, "TelecomCallCallback onStateChanged call=" + call);
call.unregisterCallback(this);
}
步驟63,64:mPhone.internalRemoveCall(call); 把Call(android.telecom.Call)對象從列表中移除,通知onCallRemoved事件
android.telecom.Phone
final void internalRemoveCall(Call call) {
mCallByTelecomCallId.remove(call.internalGetCallId());
mCalls.remove(call);
InCallService.VideoCall videoCall = call.getVideoCall();
if (videoCall != null) {
videoCall.destroy();
}
fireCallRemoved(call);
}
private void fireCallRemoved(Call call) {
for (Listener listener : mListeners) {
listener.onCallRemoved(this, call);
}
}
步驟70,71:InCallService處理onCallRemoved事件
com.android.incallui.InCallServiceImpl
@Override
public void onCallAdded(Call call) {
InCallPresenter.getInstance().onCallAdded(call);
}
步驟72:進入InCallPresenter的onCallRemoved方法
com.android.incallui.InCallPresenter
public void onCallRemoved(android.telecom.Call call) {
mCallList.onCallRemoved(call);
call.unregisterCallback(mCallCallback);
}
步驟73~78:CallList的onCallRemoved方法:
com.android.incallui.CallList
public void onCallRemoved(android.telecom.Call telecomCall) {
if (mCallByTelecomCall.containsKey(telecomCall)) {
Call call = mCallByTelecomCall.get(telecomCall);
Logger.logCall(call);
if (updateCallInMap(call)) {
Log.w(this, "Removing call not previously disconnected " + call.getId());
}
updateCallTextMap(call, null);
}
}
CallList內部更新狀態最終回調InCallPresenter的onCallListChange方法
com.android.incallui.InCallPresenter
public void onCallListChange(CallList callList) {
if (mInCallActivity != null && mInCallActivity.getCallCardFragment() != null &&
mInCallActivity.getCallCardFragment().isAnimating()) {
mAwaitingCallListUpdate = true;
return;
}
if (callList == null) {
return;
}
mAwaitingCallListUpdate = false;
InCallState newState = getPotentialStateFromCallList(callList);
InCallState oldState = mInCallState;
Log.d(this, "onCallListChange oldState= " + oldState + " newState=" + newState);
newState = startOrFinishUi(newState);
Log.d(this, "onCallListChange newState changed to " + newState);
// Set the new state before announcing it to the world
Log.i(this, "Phone switching state: " + oldState + " -> " + newState);
mInCallState = newState;
// notify listeners of new state
for (InCallStateListener listener : mListeners) {
Log.d(this, "Notify " + listener + " of state " + mInCallState.toString());
listener.onStateChange(oldState, mInCallState, callList);
}
if (isActivityStarted()) {
final boolean hasCall = callList.getActiveOrBackgroundCall() != null ||
callList.getOutgoingCall() != null;
mInCallActivity.dismissKeyguard(hasCall);
}
}
步驟79,80,81:InCallPresenter處理disconnected事件並觸發相關回調更新界面等
至此,一個來電的整體流程都分析完了,大致流程如下:
InCallUI →TeleComService→TeleponyService→TelephonyFramework →RIL→
RIL→TelephonyFramework →TeleponyService→TeleComFramework→TeleComService→TeleComFramework-->InCallUI
http://www.bkjia.com/Androidjc/1192396.htmlwww.bkjia.comtruehttp://www.bkjia.com/Androidjc/1192396.htmlTechArticleAndroid7.0 Phone應用源碼分析(三) phone拒接流程分析,android7.0拒接 接上篇博文: Android7.0 Phone應用源碼分析(二) phone來電流程分析 今天我...
OpenCV學習筆記(七)—— OpenCV for Android實時圖像處理
OpenCV學習筆記(七)—— OpenCV for Android實時圖像處理 在上篇中我們已經實現了相機打開和實時圖像信息的獲取,那麼接下來我們可以嘗
Android中手機錄屏並轉換GIF的兩種方式,android錄屏
Android中手機錄屏並轉換GIF的兩種方式,android錄屏之前在博文中為了更好的給大家演示APP的實現效果,本人了解學習了幾種給手機錄屏的方法,今天就給大家介紹兩
Android中Action Bar的使用
Android中Action Bar的使用 內容概要 示例演示和基本介紹 啟用Action Bar 在Action Bar上添加按鈕 自定義Action Bar樣式 自動
Android平台Camera實時濾鏡實現方法探討(九)--磨皮算法探討(一)
Android平台Camera實時濾鏡實現方法探討(九)--磨皮算法探討(一) 上一篇開頭提到了一些可用於磨皮的去噪算法,下面我們實現這些算法並且觀察效果,咱不考慮實時性