1.1 TelePhony Framework
当有新来电时,最先得到消息的肯定是Modem,接着Modem response到RILD,RILD通过“rild”socket accpet得到的clientsocket将消息上传到RILJ。
而在之前分析MO的RILJ时,已经知道存在着两种消息Solicited消息和UnSolicited消息。UnSolicited消息可以基本认定为是底层主动上报的消息,因此对于Call来电消息,根据之前的分析会交给processUnsolicited()处理。请查看processUnsolicited()中关于消息RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED的处理(已标黄)。可以看到实际上的处理就是向此RIL消息的注册者们mCallStateRegistrants发出了通知。其处理过程大致如下图所示。
应该注意到的是,一个Phone除了基本的通话功能之外其实还存在着其他功能,比如移动网络,这些都需要依赖于手机卡的支持。因此,将phone的不同功能划分开来是很有必要的。因此就有了CallTracker负责通话功能,ServiceStateTracker提供状态服务,DcTracker负责网络数据通信服务的划分,当然他们三者之间也是存在联系的,暂不详述。
图中三类Tracker都继承了Handler类。对于非URC消息,Tracker在下发命令的时候会携带mResult参数以指明应答的Handler,RILJ可以根据这个Handler将消息传递的上层。而对于URC消息(其实还包含部分非URC消息),由于是底层主动上报的消息,我们很容易就能想到会使用观察者模式:RILJ维护多个RegistrantList,上层向RIL注册将自己注册到某个自己感兴趣的事件对应的RegistrantList,当有感兴趣的事件发生时,相应的上层结构得到RILJ的通知。
RILJ维护了很多这样的RegistrantList,用以区分不同事件的感兴趣者。对于来电,CdmaCallTracker已经在构造函数中向RILJ的mCallStateRegistrants注册。因此,处理此消息的为CdmaCallTracker。找到其handlerMessage()方法。需要注意的是来电首先触发的消息是RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED,RILJ将这个消息通知到其注册者CdmaCallTracker。此时Message携带的Event消息为EVENT_CALL_STATE_CHANGE,其处理过程如下所示。
public void handleMessage (Message msg) { AsyncResult ar;
if (!mPhone.mIsTheCurrentActivePhone) { Rlog.w(LOG_TAG, "Ignoring events received on inactive CdmaPhone"); return; } switch (msg.what) { case EVENT_POLL_CALLS_RESULT:{ //MT第二次 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);//MT_2时候走这里 } } break;
case EVENT_OPERATION_COMPLETE: operationComplete(); //之前的MO时进入过这里 break;
case EVENT_SWITCH_RESULT: // In GSM call operationComplete() here which gets the // current call list. But in CDMA there is no list so // there is nothing to do. break;
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; }
for (int i = 0, s = mDroppedDuringPoll.size() ; i < s ; i++ ) { CdmaConnection conn = mDroppedDuringPoll.get(i);
conn.onRemoteDisconnect(causeCode, vendorCause); }
updatePhoneState();
mPhone.notifyPreciseCallStateChanged(); mDroppedDuringPoll.clear(); break;
case EVENT_REPOLL_AFTER_DELAY: case EVENT_CALL_STATE_CHANGE: //MT第一次 //调用父类CallTracker查询Call List方法 pollCallsWhenSafe(); //MO时也进入这里,此时MT_1也进入 break;
case EVENT_RADIO_AVAILABLE: handleRadioAvailable(); break;
case EVENT_RADIO_NOT_AVAILABLE: handleRadioNotAvailable(); break;
case EVENT_EXIT_ECM_RESPONSE_CDMA: // no matter the result, we still do the same here if (mPendingCallInEcm) { mCi.dial(mPendingMO.getAddress(), mPendingCallClirMode, obtainCompleteMessage()); mPendingCallInEcm = false; } mPhone.unsetOnEcbModeExitResponse(this); break;
case EVENT_CALL_WAITING_INFO_CDMA: ar = (AsyncResult)msg.obj; if (ar.exception == null) { handleCallWaitingInfo((CdmaCallWaitingNotification)ar.result); Rlog.d(LOG_TAG, "Event EVENT_CALL_WAITING_INFO_CDMA Received"); } break;
case EVENT_THREE_WAY_DIAL_L2_RESULT_CDMA: ar = (AsyncResult)msg.obj; if (ar.exception == null) { // Assume 3 way call is connected mPendingMO.onConnectedInOrOut(); mPendingMO = null; } break;
case EVENT_THREE_WAY_DIAL_BLANK_FLASH: ar = (AsyncResult) msg.obj; if (ar.exception == null) { postDelayed( new Runnable() { public void run() { if (mPendingMO != null) { mCi.sendCDMAFeatureCode(mPendingMO.getAddress(), obtainMessage(EVENT_THREE_WAY_DIAL_L2_RESULT_CDMA)); } } }, m3WayCallFlashDelay); } else { mPendingMO = null; Rlog.w(LOG_TAG, "exception happened on Blank Flash for 3-way call"); } break;
default:{ throw new RuntimeException("unexpected event not handled"); } } } |
根据之前的分析,MT第一次case将进入pollCallsWhenSafe(),其实现在其父类CallTracker中找到:
protected void pollCallsWhenSafe() { mNeedsPoll = true;
if (checkNoOperationsPending()) { mLastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT); mCi.getCurrentCalls(mLastRelevantPoll); } } |
仔细对比就可以发现,这些处理其实和之前MO分析时候的处理时是一样的。它会进入RILJ的getCurrentCalls()。mCi就是RILJ,这里需要注意的是之前RILJ上报的消息之所以能够case到EVENT_CALL_STATE_CHANGE是应为在cdmaCallTracker向RILJ注册的参数已经指明了是EVENT_CALL_STATE_CHANGE。而这里不一样,这里是非URC消息,所匹配的EVENT消息是在下发CMD的时候指明的(上面金底红字部分)。
public void getCurrentCalls (Message result) { RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_CURRENT_CALLS, result); if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); send(rr); } |
此时的RILJ又向下层下发了SolicitedRequest,下层处理后再次进入ProcessSolicited(),第二次case调用rr.mResult.sendToTarget()向CallTracker发出消息。handleMessage()调用handlePollCalls((AsyncResult)msg.obj)根据RIL发出的Call List对象,判断Call的状态并发出通知,这里发出的是新来电通知Phone.notifyNewRingingConnection(),handlePollCalls代码之前MO已经贴过了,这里只截取相关代码如下。
protected void handlePollCalls(AsyncResult ar) { …… if (newRinging != null) { mPhone.notifyNewRingingConnection(newRinging); } …… }
--------àCDMAPhone.java public void notifyNewRingingConnection(Connection c) { super.notifyNewRingingConnectionP(c); } |
回调了基类PhoneBase的notifyNewRingingConnectionP(c)方法。此时来电消息进入了Framework层。
public void notifyNewRingingConnectionP(Connection cn) { if (!mIsVoiceCapable) return; AsyncResult ar = new AsyncResult(null, cn, null); mNewRingingConnectionRegistrants.notifyRegistrants(ar); } |
可以看出是向监听mNewRingConnectionRegistrant的观察者发送消息,这个消息包含了一个connection。此消息将在TelephonyService模块中处理。
1.2 Telephony Service
首先要分析的是这个消息的观察者是谁。在SourceInsight中查找,发现有多个地方注册了这个事件,包括CallManager,PstnIncomingCallNotifier。CallManager虽然注册了该消息但是其向上二次上报的观察者为空(CallStateMonitor中原先有注册,但在Android6.0中已经被注释掉了,并且Android6.0已经注明可以删除与这些注释相关的代码),因此查看PstnIncomingCallNotifier的相关方法:
private void registerForNotifications() { Phone newPhone = mPhoneProxy.getActivePhone(); if (newPhone != mPhoneBase) { unregisterForNotifications();
if (newPhone != null) { Log.i(this, "Registering: %s", newPhone); mPhoneBase = newPhone; mPhoneBase.registerForNewRingingConnection( mHandler, EVENT_NEW_RINGING_CONNECTION, null); mPhoneBase.registerForCallWaiting( mHandler, EVENT_CDMA_CALL_WAITING, null); mPhoneBase.registerForUnknownConnection(mHandler, EVENT_UNKNOWN_CONNECTION, null); } } } |
从以上代码可以看出新来电的观察者为PstnIncomingCallNotifier。找到mHandler的相关方法
private final Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch(msg.what) { case EVENT_NEW_RINGING_CONNECTION: handleNewRingingConnection((AsyncResult) msg.obj); break; case EVENT_CDMA_CALL_WAITING: handleCdmaCallWaiting((AsyncResult) msg.obj); break; case EVENT_UNKNOWN_CONNECTION: handleNewUnknownConnection((AsyncResult) msg.obj); break; default: break; } } }; |
进入handleNewRingingConnection()。
private void handleNewRingingConnection(AsyncResult asyncResult) { Log.d(this, "handleNewRingingConnection"); Connection connection = (Connection) asyncResult.result;//解消息 if (connection != null) { Call call = connection.getCall();//获得对应的Call
// 将intent发送给 Telecom. if (call != null && call.getState().isRinging()) { sendIncomingCallIntent(connection); } } } |
具体的发送过程
private void sendIncomingCallIntent(Connection connection) { Bundle extras = null; if (connection.getNumberPresentation() == TelecomManager.PRESENTATION_ALLOWED && !TextUtils.isEmpty(connection.getAddress())) { extras = new Bundle(); Uri uri = Uri.fromParts(PhoneAccount.SCHEME_TEL, connection.getAddress(), null); extras.putParcelable(TelecomManager.EXTRA_INCOMING_CALL_ADDRESS, uri); } TelecomManager.from(mPhoneProxy.getContext()).addNewIncomingCall( PhoneUtils.makePstnPhoneAccountHandle(mPhoneProxy), extras); } |
进入TelecomManager
public static TelecomManager from(Context context) { return (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE); } /** * Registers a new incoming call. A {@link ConnectionService} should invoke this method when it has an incoming call. The specified {@link PhoneAccountHandle} must have been registered with {@link #registerPhoneAccount}. Once invoked, this method will cause the system to bind to the {@link ConnectionService} associated with the {@link PhoneAccountHandle} and request additional information about the call (See {@link ConnectionService#onCreateIncomingConnection}) before starting the incoming call UI. @param phoneAccount A {@link PhoneAccountHandle} registered with{@link #registerPhoneAccount}.@param extras A bundle that will be passed through to{@link ConnectionService#onCreateIncomingConnection}. */ public void addNewIncomingCall(PhoneAccountHandle phoneAccount, Bundle extras) { try { if (isServiceConnected()) { getTelecomService().addNewIncomingCall( phoneAccount, extras == null ? new Bundle() : extras); } } catch (RemoteException e) { Log.e(TAG, "RemoteException adding a new incoming call: " + phoneAccount, e); } } |
因此进入TelecomServiceImpl
public void addNewIncomingCall(PhoneAccountHandle phoneAccountHandle, Bundle extras) { synchronized (mLock) { Log.i(this, "Adding new incoming call with phoneAccountHandle %s", phoneAccountHandle); if (phoneAccountHandle != null && phoneAccountHandle.getComponentName() != null) { // TODO(sail): Add unit tests for adding incoming calls from a SIM call manager. if (isCallerSimCallManager() && TelephonyUtil.isPstnComponentName( phoneAccountHandle.getComponentName())) { Log.v(this, "Allowing call manager to add incoming call with PSTN handle"); } else { mAppOpsManager.checkPackage( Binder.getCallingUid(), phoneAccountHandle.getComponentName().getPackageName()); // Make sure it doesn't cross the UserHandle boundary enforceUserHandleMatchesCaller(phoneAccountHandle); }
long token = Binder.clearCallingIdentity(); try { Intent intent = new Intent(TelecomManager.ACTION_INCOMING_CALL); intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle); intent.putExtra(CallIntentProcessor.KEY_IS_INCOMING_CALL, true); if (extras != null) { intent.putExtra(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS, extras); } CallIntentProcessor.processIncomingCallIntent(mCallsManager, intent); } finally { Binder.restoreCallingIdentity(token); } } else { Log.w(this, "Null phoneAccountHandle. Ignoring request to add new incoming call"); } } } |
熟悉的CallIntentProcessor,之前在的MO的时候启用过它的processOutgoingCallIntent()方法。此时来电调用的是processIncomingCallIntent(),如下
static void processIncomingCallIntent(CallsManager callsManager, Intent intent) { PhoneAccountHandle phoneAccountHandle = intent.getParcelableExtra( TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
if (phoneAccountHandle == null) { Log.w(CallIntentProcessor.class, "Rejecting incoming call due to null phone account"); return; } if (phoneAccountHandle.getComponentName() == null) { Log.w(CallIntentProcessor.class, "Rejecting incoming call due to null component name"); return; }
Bundle clientExtras = null; if (intent.hasExtra(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS)) { clientExtras = intent.getBundleExtra(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS); } if (clientExtras == null) { clientExtras = new Bundle(); }
Log.d(CallIntentProcessor.class, "Processing incoming call from connection service [%s]", phoneAccountHandle.getComponentName()); callsManager.processIncomingCallIntent(phoneAccountHandle, clientExtras); } |
进入CallsManager的processIncomingCallIntent()方法
void processIncomingCallIntent(PhoneAccountHandle phoneAccountHandle, Bundle extras) { Log.d(this, "processIncomingCallIntent"); Uri handle = extras.getParcelable(TelecomManager.EXTRA_INCOMING_CALL_ADDRESS); if (handle == null) { // Required for backwards compatibility handle = extras.getParcelable(TelephonyManager.EXTRA_INCOMING_NUMBER); } Call call = new Call( mContext, this, mLock, mConnectionServiceRepository, mContactsAsyncHelper, mCallerInfoAsyncQueryFactory, handle, null /* gatewayInfo */, null /* connectionManagerPhoneAccount */, phoneAccountHandle, true /* isIncoming */, false /* isConference */);
call.setIntentExtras(extras); // TODO: Move this to be a part of addCall() call.addListener(this); call.startCreateConnection(mPhoneAccountRegistrar); } |
再次回到熟悉的地方,在之前的MO分析是已经分析过了。主要是新建了一个连接执行器。并调用其process方法建立connection
void startCreateConnection(PhoneAccountRegistrar phoneAccountRegistrar) { Preconditions.checkState(mCreateConnectionProcessor == null); mCreateConnectionProcessor = new CreateConnectionProcessor(this, mRepository, this, phoneAccountRegistrar, mContext); mCreateConnectionProcessor.process(); } |
之前在MO中已经贴了代码,这里不厌其烦将后面的代码再贴几段一次。(与MO有区别的地方已经蓝底标出来了)。
void process() { Log.v(this, "process"); clearTimeout(); mAttemptRecords = new ArrayList<>(); if (mCall.getTargetPhoneAccount() != null) { mAttemptRecords.add(new CallAttemptRecord( mCall.getTargetPhoneAccount(), mCall.getTargetPhoneAccount())); } adjustAttemptsForConnectionManager(); adjustAttemptsForEmergency(); mAttemptRecordIterator = mAttemptRecords.iterator(); attemptNextPhoneAccount(); } |
一样进入attemptNextPhoneAccount:
private void attemptNextPhoneAccount() { Log.v(this, "attemptNextPhoneAccount"); CallAttemptRecord attempt = null; if (mAttemptRecordIterator.hasNext()) { attempt = mAttemptRecordIterator.next();
if (!mPhoneAccountRegistrar.phoneAccountRequiresBindPermission( attempt.connectionManagerPhoneAccount)) { Log.w(this, "Connection mgr does not have BIND_TELECOM_CONNECTION_SERVICE for " + "attempt: %s", attempt); attemptNextPhoneAccount(); return; }
// If the target PhoneAccount differs from the ConnectionManager phone acount, ensure it // also requires the BIND_TELECOM_CONNECTION_SERVICE permission. if (!attempt.connectionManagerPhoneAccount.equals(attempt.targetPhoneAccount) && !mPhoneAccountRegistrar.phoneAccountRequiresBindPermission( attempt.targetPhoneAccount)) { Log.w(this, "Target PhoneAccount does not have BIND_TELECOM_CONNECTION_SERVICE for " + "attempt: %s", attempt); attemptNextPhoneAccount(); return; } }
if (mResponse != null && attempt != null) { Log.i(this, "Trying attempt %s", attempt); PhoneAccountHandle phoneAccount = attempt.connectionManagerPhoneAccount; ConnectionServiceWrapper service = mRepository.getService( phoneAccount.getComponentName(), phoneAccount.getUserHandle()); if (service == null) { Log.i(this, "Found no connection service for attempt %s", attempt); attemptNextPhoneAccount(); } else { mCall.setConnectionManagerPhoneAccount(attempt.connectionManagerPhoneAccount); mCall.setTargetPhoneAccount(attempt.targetPhoneAccount); mCall.setConnectionService(service); setTimeoutIfNeeded(service, attempt);
service.createConnection(mCall, new Response(service)); } } else { Log.v(this, "attemptNextPhoneAccount, no more accounts, failing"); if (mResponse != null) { clearTimeout(); mResponse.handleCreateConnectionFailure(mLastErrorDisconnectCause != null ? mLastErrorDisconnectCause : new DisconnectCause(DisconnectCause.ERROR)); mResponse = null; mCall.clearConnectionService(); } } } |
一样进入到ConnectionServiceWrapper的createConnection():
void createConnection(final Call call, final CreateConnectionResponse response) { Log.d(this, "createConnection(%s) via %s.", call, getComponentName()); BindCallback callback = new BindCallback() { @Override public void onSuccess() { String callId = mCallIdMapper.getCallId(call); mPendingResponses.put(callId, response);
GatewayInfo gatewayInfo = call.getGatewayInfo(); Bundle extras = call.getIntentExtras(); if (gatewayInfo != null && gatewayInfo.getGatewayProviderPackageName() != null && gatewayInfo.getOriginalAddress() != null) { extras = (Bundle) extras.clone(); extras.putString( TelecomManager.GATEWAY_PROVIDER_PACKAGE, gatewayInfo.getGatewayProviderPackageName()); extras.putParcelable( TelecomManager.GATEWAY_ORIGINAL_ADDRESS, gatewayInfo.getOriginalAddress()); }
Log.event(call, Log.Events.START_CONNECTION, Log.piiHandle(call.getHandle())); try { mServiceInterface.createConnection( call.getConnectionManagerPhoneAccount(), callId, new ConnectionRequest( call.getTargetPhoneAccount(), call.getHandle(), extras, call.getVideoState()), call.isIncoming(), call.isUnknown()); } catch (RemoteException e) { Log.e(this, e, "Failure to createConnection -- %s", getComponentName()); mPendingResponses.remove(callId).handleCreateConnectionFailure( new DisconnectCause(DisconnectCause.ERROR, e.toString())); } }
@Override public void onFailure() { Log.e(this, new Exception(), "Failure to call %s", getComponentName()); response.handleCreateConnectionFailure(new DisconnectCause(DisconnectCause.ERROR)); } };
mBinder.bind(callback, call); } |
进入到ConnectionService.createConnection
private void createConnection( final PhoneAccountHandle callManagerAccount, final String callId, final ConnectionRequest request, boolean isIncoming, boolean isUnknown) { Log.d(this, "createConnection, callManagerAccount: %s, callId: %s, request: %s, " + "isIncoming: %b, isUnknown: %b", callManagerAccount, callId, request, isIncoming, isUnknown);
Connection connection = isUnknown ? onCreateUnknownConnection(callManagerAccount, request) : isIncoming ? onCreateIncomingConnection(callManagerAccount, request) : onCreateOutgoingConnection(callManagerAccount, request); Log.d(this, "createConnection, connection: %s", connection); if (connection == null) { connection = Connection.createFailedConnection( new DisconnectCause(DisconnectCause.ERROR)); }
if (connection.getState() != Connection.STATE_DISCONNECTED) { addConnection(callId, connection); }
Uri address = connection.getAddress(); String number = address == null ? "null" : address.getSchemeSpecificPart(); Log.v(this, "createConnection, number: %s, state: %s, capabilities: %s", Connection.toLogSafePhoneNumber(number), Connection.stateToString(connection.getState()), Connection.capabilitiesToString(connection.getConnectionCapabilities()));
Log.d(this, "createConnection, calling handleCreateConnectionSuccessful %s", callId); mAdapter.handleCreateConnectionComplete( callId, request, new ParcelableConnection( request.getAccountHandle(), connection.getState(), connection.getConnectionCapabilities(), connection.getAddress(), connection.getAddressPresentation(), connection.getCallerDisplayName(), connection.getCallerDisplayNamePresentation(), connection.getVideoProvider() == null ? null : connection.getVideoProvider().getInterface(), connection.getVideoState(), connection.isRingbackRequested(), connection.getAudioModeIsVoip(), connection.getConnectTimeMillis(), connection.getStatusHints(), connection.getDisconnectCause(), createIdList(connection.getConferenceables()), connection.getExtras())); } |
void handleCreateConnectionComplete( String id, ConnectionRequest request, ParcelableConnection connection) { for (IConnectionServiceAdapter adapter : mAdapters) { try { adapter.handleCreateConnectionComplete(id, request, connection); } catch (RemoteException e) { } } } |
public void handleCreateConnectionComplete( String callId, ConnectionRequest request, ParcelableConnection connection) { long token = Binder.clearCallingIdentity(); try { synchronized (mLock) { logIncoming("handleCreateConnectionComplete %s", callId); if (mCallIdMapper.isValidCallId(callId)) { ConnectionServiceWrapper.this .handleCreateConnectionComplete(callId, request, connection); } } } finally { Binder.restoreCallingIdentity(token); } } |
这里回调了外围类的同名函数。
private void handleCreateConnectionComplete( String callId, ConnectionRequest request, ParcelableConnection connection) { // TODO: Note we are not using parameter "request", which is a side effect of our tacit // assumption that we have at most one outgoing connection attempt per ConnectionService. // This may not continue to be the case. if (connection.getState() == Connection.STATE_DISCONNECTED) { // A connection that begins in the DISCONNECTED state is an indication of // failure to connect; we handle all failures uniformly removeCall(callId, connection.getDisconnectCause()); } else { // 创建connection成功 if (mPendingResponses.containsKey(callId)) { mPendingResponses.remove(callId) .handleCreateConnectionSuccess(mCallIdMapper, connection); } } } |
mPendingResponse存储的是Call.因此Call的方法被调用。
@Override public void handleCreateConnectionSuccess( CallIdMapper idMapper, ParcelableConnection connection) { Log.v(this, "handleCreateConnectionSuccessful %s", connection); setTargetPhoneAccount(connection.getPhoneAccount()); setHandle(connection.getHandle(), connection.getHandlePresentation()); setCallerDisplayName( connection.getCallerDisplayName(), connection.getCallerDisplayNamePresentation()); setConnectionCapabilities(connection.getConnectionCapabilities()); setVideoProvider(connection.getVideoProvider()); setVideoState(connection.getVideoState()); setRingbackRequested(connection.isRingbackRequested()); setIsVoipAudioMode(connection.getIsVoipAudioMode()); setStatusHints(connection.getStatusHints()); setExtras(connection.getExtras());
mConferenceableCalls.clear(); for (String id : connection.getConferenceableConnectionIds()) { mConferenceableCalls.add(idMapper.getCall(id)); }
if (mIsUnknown) { for (Listener l : mListeners) { l.onSuccessfulUnknownCall(this, getStateFromConnectionState(connection.getState())); } } else if (mIsIncoming) { // We do not handle incoming calls immediately when they are verified by the connection // service. We allow the caller-info-query code to execute first so that we can read the // direct-to-voicemail property before deciding if we want to show the incoming call to // the user or if we want to reject the call. mDirectToVoicemailQueryPending = true;
// Timeout the direct-to-voicemail lookup execution so that we dont wait too long before // showing the user the incoming call screen. mHandler.postDelayed(mDirectToVoicemailRunnable, Timeouts.getDirectToVoicemailMillis( mContext.getContentResolver())); } else { for (Listener l : mListeners) { l.onSuccessfulOutgoingCall(this, getStateFromConnectionState(connection.getState())); } } } |
都是谁注册进了mListeners?仔细查看代码发现CallsManager将自己注册进了这个mListeners,因此程序又回到了CallsManager。在onSuccessfulIncomingCall()方法中把Call的状态改成RINGING,并把此Call添加到Call集合里。并触发CallsManagerListener的OnCallAdded()开启UI界面。
@Override public void onSuccessfulIncomingCall(Call incomingCall) { Log.d(this, "onSuccessfulIncomingCall"); setCallState(incomingCall, CallState.RINGING, "successful incoming call");
if (hasMaximumRingingCalls()) { incomingCall.reject(false, null); // since the call was not added to the list of calls, we have to call the missed // call notifier and the call logger manually. mMissedCallNotifier.showMissedCallNotification(incomingCall); mCallLogManager.logCall(incomingCall, Calls.MISSED_TYPE); } else { addCall(incomingCall); } } |
进入addCall
private void addCall(Call call) { Trace.beginSection("addCall"); Log.v(this, "addCall(%s)", call); call.addListener(this); mCalls.add(call);
// TODO: Update mForegroundCall prior to invoking // onCallAdded for calls which immediately take the foreground (like the first call). for (CallsManagerListener listener : mListeners) { if (Log.SYSTRACE_DEBUG) { Trace.beginSection(listener.getClass().toString() + " addCall"); } listener.onCallAdded(call); if (Log.SYSTRACE_DEBUG) { Trace.endSection(); } } updateCallsManagerState(); Trace.endSection(); } |
这里作一下小结,来电的大致过程如下图所示
1.3 UI界面的开启
待进一步分析研究
1.4 接听电话
此时假设UI界面已经呈现,并且响铃,按下接听键,AnswerFragment的onAnswer()被触发:
public void onAnswer(int videoState, Context context) { Log.d(this, "onAnswer videoState=" + videoState + " context=" + context); getPresenter().onAnswer(videoState, context); } |
进入AnswerPresenter.java的onAnswer():
public void onAnswer(int videoState, Context context) { if (mCallId == null) { return; }
if (mCall.getSessionModificationState() == Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST) { Log.d(this, "onAnswer (upgradeCall) mCallId=" + mCallId + " videoState=" + videoState); InCallPresenter.getInstance().acceptUpgradeRequest(videoState, context); } else { Log.d(this, "onAnswer (answerCall) mCallId=" + mCallId + " videoState=" + videoState); TelecomAdapter.getInstance().answerCall(mCall.getId(), videoState); } } |
进入TelecomAdapter的answerCall():
void answerCall(String callId, int videoState) { android.telecom.Call call = getTelecommCallById(callId); //根据ID得到Call if (call != null) { call.answer(videoState); } else { Log.e(this, "error answerCall, call not in call list: " + callId); } } |
进入Call.answer()
public void answer(int videoState) { mInCallAdapter.answerCall(mTelecomCallId, videoState); } |
进入InCallAdapter.answer()
public void answerCall(String callId, int videoState) { try { mAdapter.answerCall(callId, videoState);//mAdapter是个IInCallAdapter } catch (RemoteException e) { } } //真正的InCallAdapter↓ public void answerCall(String callId, int videoState) { long token = Binder.clearCallingIdentity(); try { synchronized (mLock) { Log.d(this, "answerCall(%s,%d)", callId, videoState); if (mCallIdMapper.isValidCallId(callId)) { Call call = mCallIdMapper.getCall(callId);//根据ID得到Call if (call != null) { mCallsManager.answerCall(call, videoState);//回到CallsManager } else { Log.w(this, "answerCall, unknown call id: %s", callId); } } } } finally { Binder.restoreCallingIdentity(token); } } |
又进入了CallsManager.java
/** * Instructs Telecom to answer the specified call. Intended to be invoked by the in-call * app through {@link InCallAdapter} after Telecom notifies it of an incoming call followed by * the user opting to answer said call. * * @param call The call to answer. * @param videoState The video state in which to answer the call. */ void answerCall(Call call, int videoState) { if (!mCalls.contains(call)) { Log.i(this, "Request to answer a non-existent call %s", call); } else { // If the foreground call is not the ringing call and it is currently isActive() or // STATE_DIALING, put it on hold before answering the call. if (mForegroundCall != null && mForegroundCall != call && (mForegroundCall.isActive() || mForegroundCall.getState() == CallState.DIALING)) { if (0 == (mForegroundCall.getConnectionCapabilities() & Connection.CAPABILITY_HOLD)) { // This call does not support hold. If it is from a different connection // service, then disconnect it, otherwise allow the connection service to // figure out the right states. if (mForegroundCall.getConnectionService() != call.getConnectionService()) { mForegroundCall.disconnect(); } } else { Call heldCall = getHeldCall(); if (heldCall != null) { Log.v(this, "Disconnecting held call %s before holding active call.", heldCall); heldCall.disconnect(); }
Log.v(this, "Holding active/dialing call %s before answering incoming call %s.", mForegroundCall, call); mForegroundCall.hold(); } // TODO: Wait until we get confirmation of the active call being // on-hold before answering the new call. // TODO: Import logic from CallManager.acceptCall() }
for (CallsManagerListener listener : mListeners) { listener.onIncomingCallAnswered(call); }
// We do not update the UI until we get confirmation of the answer() through // {@link #markCallAsActive}. call.answer(videoState); //进入Call(另一个,是TelecomService模块的) if (VideoProfile.isVideo(videoState) && !mWiredHeadsetManager.isPluggedIn() && !mCallAudioManager.isBluetoothDeviceAvailable() && isSpeakerEnabledForVideoCalls()) { call.setStartWithSpeakerphoneOn(true); } } } |
进入Call.answer:
void answer(int videoState) { 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 'answer' and Telecom receives the command. if (isRinging("answer")) { // At this point, we are asking the connection service to answer but we don't assume // that it will work. Instead, we wait until confirmation from the connectino service // that the call is in a non-STATE_RINGING state before changing the UI. See // {@link ConnectionServiceAdapter#setActive} and other set* methods. //这是个ConnectionServiceWrapper实例 mConnectionService.answer(this, videoState); Log.event(this, Log.Events.REQUEST_ACCEPT); } } |
进入mConnectionService.answer()
/** @see IConnectionService#answer(String) */ void answer(Call call, int videoState) { final String callId = mCallIdMapper.getCallId(call);//获取CallID if (callId != null && isServiceValid("answer")) { try { logOutgoing("answer %s %d", callId, videoState); if (VideoProfile.isAudioOnly(videoState)) {//如果仅仅是音频通话 mServiceInterface.answer(callId);//调用Service同名方法 } else {//视频通话 //调用Service同名方法 mServiceInterface.answerVideo(callId, videoState); } } catch (RemoteException e) { } } } |
这里的东西一样很熟悉,根据之前的MO分析经验,最终调用的会是ConnectionService的answer()方法或answerVideo()方法。从下面可以看出他们唯一的区别就是参数。
private void answerVideo(String callId, int videoState) { Log.d(this, "answerVideo %s", callId); findConnectionForAction(callId, "answer").onAnswer(videoState); }
private void answer(String callId) { Log.d(this, "answer %s", callId); findConnectionForAction(callId, "answer").onAnswer(); } |
下面的过程就不详细描述了,最终肯定会调用RILJ的AccptCall方法,接收电话。