Telephony基础之VoiceCall业务(MT流程)
MT流程之启动InCallUI时序图
MT流程是从底层状态变化开始。
首先进入RIL.processUnsolicited()
private void processUnsolicited (Parcel p) {
...
try {switch(response) {
...
case RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED:
ret = responseVoid(p); break;
...
}
switch(response) {
...
case RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED:
if (RILJ_LOGD)
unsljLog(response);//打印log日志
//发出通知(RegistrantList消息处理机制)
mCallStateRegistrants.notifyRegistrants(new
AsyncResult(null, null, null));
...
}
}
进入GsmCdmaCallTracker.handleMessage():
public void
handleMessage (Message msg) {
...
case EVENT_CALL_STATE_CHANGE:
//调用父类CallTracker查询Call List方法
pollCallsWhenSafe();
break;
...
}
进入CallTracker.pollCallsWhenSafe():
protected void pollCallsWhenSafe() {
...
if (checkNoOperationsPending()) {
//注意mLastRelevantPoll对象的消息类型,后面会用到
mLastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT);
mCi.getCurrentCalls(mLastRelevantPoll);
}
}
进入RIL.getCurrentCalls():
public void getCurrentCalls (Message result) {
//注意rr对象的消息类型,后面会用到
RILRequest rr = RILRequest.obtain(
RIL_REQUEST_GET_CURRENT_CALLS, result);
//打印log日志
if (RILJ_LOGD)
riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
send(rr);
}
返回RIL.processSolicited ():
private RILRequest processSolicited (Parcel p) {
...
case RIL_REQUEST_GET_CURRENT_CALLS:
ret = responseCallList(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消息通知
}
进入GsmCdmaCallTracker.handleMessage ():
public void handleMessage (Message msg) {
...
switch (msg.what) {
case EVENT_POLL_CALLS_RESULT:{
//打印log日志
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;
...
}
进入GsmCdmaCallTracker.handlePollCalls():
protected void
handlePollCalls(AsyncResult ar){
...
if (newRinging != null) {
mPhone.notifyNewRingingConnection(newRinging);
...
}
进入GsmCdmaPhone.notifyNewRingingConnection():
void notifyNewRingingConnection(Connection c) {
/* we'd love it if this was package-scoped*/
super.notifyNewRingingConnectionP(c);
}
进入Phone.notifyNewRingingConnectionP():
public void notifyNewRingingConnectionP(Connection cn) {
if (!mIsVoiceCapable)
return;
AsyncResult ar = new AsyncResult(null, cn, null);
mNewRingingConnectionRegistrants.notifyRegistrants(ar);
}
进入PstnIncomingCallNotifier.Handler.handleMessage():
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch(msg.what) {
case EVENT_NEW_RINGING_CONNECTION:
handleNewRingingConnection((AsyncResult) msg.obj);
...
}
进入PstnIncomingCallNotifier.handleNewRingingConnection():
private void handleNewRingingConnection(AsyncResult asyncResult) {
Log.d(this, "handleNewRingingConnection");
Connection connection = (Connection) asyncResult.result;
if (connection != null) {
Call call = connection.getCall();
// Final verification of the ringing state before sending the intent to Telecom.
if (call != null && call.getState().isRinging()) {
sendIncomingCallIntent(connection);
}
}
}
进入PstnIncomingCallNotifier.sendIncomingCallIntent():
private void sendIncomingCallIntent(Connection connection) {
...
TelecomManager.from(mPhoneProxy.getContext()).addNewIncomingCall(
}
进入TelecomManager.addNewIncomingCall():
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.addNewIncomingCall():
@Override
public void addNewIncomingCall(PhoneAccountHandle phoneAccountHandle, Bundle extras) {
try {
Log.startSession("TSI.aNIC");
synchronized (mLock) {
...
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) {
extras.setDefusable(true);
intent.putExtra(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS, extras);
}
mCallIntentProcessorAdapter.processIncomingCallIntent(
mCallsManager, intent);
} finally {
Binder.restoreCallingIdentity(token);
}
} else {
Log.w(this, "Null phoneAccountHandle. Ignoring request to add new" +
" incoming call");
}
...
}
进入CallIntentProcessor.AdapterImpl.processIncomingCallIntent():
@Override
public void processIncomingCallIntent(CallsManager callsManager, Intent intent) {
CallIntentProcessor.processIncomingCallIntent(callsManager, intent);
}
进入CallIntentProcessor.processIncomingCallIntent():
static void processIncomingCallIntent(CallsManager callsManager, Intent intent) {
...
Log.d(CallIntentProcessor.class,
"Processing incoming call from connection service [%s]",
phoneAccountHandle.getComponentName());
callsManager.processIncomingCallIntent(phoneAccountHandle, clientExtras);
}
进入CallsManager.processIncomingCallIntent():
/**
* Starts the process to attach the call to a connection service.
*
* @param phoneAccountHandle The phone account which contains the component name of the
* connection service to use for this call.
* @param extras The optional extras Bundle passed with the intent used for the incoming call.
*/
void processIncomingCallIntent(PhoneAccountHandle phoneAccountHandle, Bundle extras) {
Log.d(this, "processIncomingCallIntent");
Uri handle = extras.getParcelable(TelephonyManager.EXTRA_INCOMING_NUMBER);
Call call = new Call(
mContext,
mConnectionServiceRepository,
handle,
null /* gatewayInfo */,
null /* connectionManagerPhoneAccount */,
phoneAccountHandle,
true /* isIncoming */,
false /* isConference */);
call.setExtras(extras);
// TODO: Move this to be a part of addCall()
call.addListener(this);
call.startCreateConnection(mPhoneAccountRegistrar);
}
注意:
这里通过Call.startCreateConnection创建connection与MO基本相同.
先在ConnectionServiceWrapper bind to TelephonyConnectionservice,成功返回后通过Binder接口创建connection(GSMCDMAConnection).
这里和MO不同的是创建完connection后就调用mAdapter.handleCreateConnectionComplete()返回,而没有phone.dial()流程,因为是MT.
进入Call.startCreateConnection():
void startCreateConnection(PhoneAccountRegistrar phoneAccountRegistrar) {
if (mCreateConnectionProcessor != null) {
Log.w(this, "mCreateConnectionProcessor in startCreateConnection is not null. This is" +
" due to a race between NewOutgoingCallIntentBroadcaster and " +
"phoneAccountSelected, but is harmlessly resolved by ignoring the second " +
"invocation.");
return;
}
mCreateConnectionProcessor = new CreateConnectionProcessor(this, mRepository, this,
phoneAccountRegistrar, mContext);
mCreateConnectionProcessor.process();
}
进入CreateConnectionProcessor.process():
public void process() {
Log.v(this, "process");
clearTimeout();
mAttemptRecords = new ArrayList<>();
if (mCall.getTargetPhoneAccount() != null) {
mAttemptRecords.add(new CallAttemptRecord(
mCall.getTargetPhoneAccount(), mCall.getTargetPhoneAccount()));
}
adjustAttemptsForConnectionManager();
adjustAttemptsForEmergency(mCall.getTargetPhoneAccount());
mAttemptRecordIterator = mAttemptRecords.iterator();
attemptNextPhoneAccount();
}
进入CreateConnectionProcessor.attemptNextPhoneAccount():
private void attemptNextPhoneAccount() {
Log.v(this, "attemptNextPhoneAccount");
...
} else {
mConnectionAttempt++;
mCall.setConnectionManagerPhoneAccount(attempt.connectionManagerPhoneAccount);
mCall.setTargetPhoneAccount(attempt.targetPhoneAccount);
mCall.setConnectionService(mService);
setTimeoutIfNeeded(mService, attempt);
mService.createConnection(mCall, this);
}
}
进入ConnectionServiceWrapper.BindCallback.onSuccess():
public void onSuccess() {
...try {
mServiceInterface.createConnection(
call.getConnectionManagerPhoneAccount(),
callId,
new ConnectionRequest(
call.getTargetPhoneAccount(),
call.getHandle(),
extras,
call.getVideoState(),
callId),
call.shouldAttachToExistingConnection(),
call.isUnknown());
}
...
}
回到ConnectionServiceWrapper.Adapter.handleCreateConnectionComplete():
private final class Adapter extends IConnectionServiceAdapter.Stub {
@Override
public void handleCreateConnectionComplete(String callId, ConnectionRequest request,
ParcelableConnection connection) {
Log.startSession(Log.Sessions.CSW_HANDLE_CREATE_CONNECTION_COMPLETE);
long token = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
logIncoming("handleCreateConnectionComplete %s", callId);
ConnectionServiceWrapper.this
.handleCreateConnectionComplete(callId, request, connection);
}
} finally {
Binder.restoreCallingIdentity(token);
Log.endSession();
}
}
进入ConnectionServiceWrapper.handleCreateConnectionComplete()
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 {
// Successful connection
if (mPendingResponses.containsKey(callId)) {
mPendingResponses.remove(callId)
.handleCreateConnectionSuccess(mCallIdMapper, connection);
}
}
}
回调CreateConnectionProcessor.handleCreateConnectionSuccess():
@Override
public void handleCreateConnectionSuccess(
CallIdMapper idMapper,
ParcelableConnection connection) {
if (mCallResponse == null) {
// Nobody is listening for this connection attempt any longer; ask the responsible
// ConnectionService to tear down any resources associated with the call
mService.abort(mCall);
} else {
// Success -- share the good news and remember that we are no longer interested
// in hearing about any more attempts
mCallResponse.handleCreateConnectionSuccess(idMapper, connection);
mCallResponse = null;
// If there's a timeout running then don't clear it. The timeout can be triggered
// after the call has successfully been created but before it has become active.
}
}
回调Call.handleCreateConnectionSuccess():
public void handleCreateConnectionSuccess(
CallIdMapper idMapper,
ParcelableConnection connection) {
Log.v(this, "handleCreateConnectionSuccessful %s", connection);
switch (mCallDirection) {
case CALL_DIRECTION_INCOMING:
// Listeners (just CallsManager for now) will be responsible for checking whether
// the call should be blocked.
for (Listener l : mListeners) {
l.onSuccessfulIncomingCall(this);
}
break;
...
}
回调CallsManager.onSuccessfulIncomingCall():
public void onSuccessfulIncomingCall(Call incomingCall) {
Log.d(this, "onSuccessfulIncomingCall");
if (incomingCall.hasProperty(Connection.PROPERTY_EMERGENCY_CALLBACK_MODE)) {
Log.i(this, "Skipping call filtering due to ECBM");
onCallFilteringComplete(incomingCall, new CallFilteringResult(true, false, true, true));
return;
}
...
}
进入CallsManager.onCallFilteringComplete():
public void onCallFilteringComplete(Call incomingCall, CallFilteringResult result) {
// Only set the incoming call as ringing if it isn't already disconnected. It is possible
// that the connection service disconnected the call before it was even added to Telecom, in
// which case it makes no sense to set it back to a ringing state.
if (incomingCall.getState() != CallState.DISCONNECTED &&
incomingCall.getState() != CallState.DISCONNECTING) {
setCallState(incomingCall, CallState.RINGING,
result.shouldAllowCall ? "successful incoming call" : "blocking call");
} else {
Log.i(this, "onCallFilteringCompleted: call already disconnected.");
return;
}
...
if (result.shouldAllowCall) {
...
} else {
addCall(incomingCall);
setActiveSubscription(incomingCall.getTargetPhoneAccount().getId());
}
} else {
if (result.shouldReject) {
...
}
}
}
进入CallsManager.addCall():
private void addCall(Call call) {
...
updateCanAddCall();
// 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();
}
}
RcsCallHandler.getInstance().updateCallComposerDataToCall(call);
Trace.endSection();
}
进入InCallController.onCallAdded()
public void onCallAdded(Call call) {
if (!isBoundToServices()) {
bindToServices(call);
} else {
adjustServiceBindingsForEmergency();
Log.i(this, "onCallAdded: %s", call);
// Track the call if we don't already know about it.
addCall(call);
...
}
}
从这里开始就跟MO的启动UI的流程一样了。
首先,如果没有bind service就先去bind service.成功返回依次调用:
InCallController.addCall()-->InCallService.allCall()-->Phone.internalAddCall()-->InCallServiceImpl.onCallAdded()-->InCallPresenter.onCallAdded()-->InCallPresenter.startOrFinishUi()
至此InComingCall UI启动完毕, MT流程完毕。