安卓10 来电流程梳理
来电过程, 是由com.android.phone进程发起的,因为 com.android.phone 进程中 Telephony 直接与Moderm层交互, com.android.phone 进程收到来来电消息后,发送消息给 system 进程, system 进程(Telecom作为中介)开始和com.android.phone 进程建立链接, 并通知 UI 进程 (com.android.dialer) 更新。大体上和拨号过程类似。
来电流程时序图:
对于MO Call来说,一般是由用户自己操作来发起的主动动作,可以根据UI上的button来跟踪流程。但是对于MTcall来说,一般是被动的接收modem的消息,不太好从UI的层面来跟踪流程,所以大概的总结下MT流程。
首先,查来电消息的处理。在来电时,首先是由modem向上上报来电的消息,上层来处理。第一条消息是:RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED,消息报上来后,到RadioIndication.java来处理(在Android O后,UNSOL消息在RadioIndication处理,SOL消息在RadioResponse里处理。替代了之前的processUnsolicited & processSolicited方法)。这条消息就是modem 告知上层:当前Call的状态发送变化了。但是上层并不知道真正的变化是什么,所以Tele回去询问当前Call到底是属于什么状态,发出请求:RIL_REQUEST_GET_CURRENT_CALLS。当Modem得到这些请求后,会将这些信息返回给Tele.
从RIL层开始往上追溯,RIL接收底层来电消息。来电的消息属于主动上报的UnSolicited消息,其对应的事件ID是CALL_STATE_CHANGE,我们进入RIL.java开始查找。
UNSOL_RESPONSE_CALL STATE CHANGED状态改变
frameworks/opt/telephony/src/java/com/android/internal/telephony/RIL.java
@UnsupportedAppUsage
5692 void unsljLog(int response) {
5693 riljLog("[UNSL]< " + responseToString(response));
5694 }@UnsupportedAppUsage
5561 static String responseToString(int request) {
5562 switch(request) {
5563 case RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED:
5564 return "UNSOL_RESPONSE_RADIO_STATE_CHANGED";
5565 case RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED:
5566 return "UNSOL_RESPONSE_CALL_STATE_CHANGED";
5567 case RIL_UNSOL_RESPONSE_NETWORK_STATE_CHANGED:
frameworks/opt/telephony/src/java/com/android/internal/telephony/RadioIndication.java
RadioIndication 类有17 个方法调用RIL 对象的unsljLog 方法打印消息,处理此
RIL 消息的代码逻辑详情如下:
public void callStateChanged(int indicationType) {mRil.processIndication(indicationType);if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED);mRil.mCallStateRegistrants.notifyRegistrants();}
- 调用processIndication向底层发送确认收到消息
- notifyRegistrant发出通知
RIL层在接收到底层的消息后,发起了notify通知。----------- 观测者模式
其中:
public class RadioIndication extends IRadioIndication.Stub {
而 mCallStateRegistrants 在:
RIL继承于父类 BaseCommands ,实现了 mCallStateRegistrants
public class RIL extends BaseCommands implements CommandsInterface
protected RegistrantList mCallStateRegistrants = new RegistrantList();
此处是: registerForCallStateChanged
搜索到有:
- BaseCommands
- GsmCdmaCallTracker
- CommandsInterface
其中BascCommands,CommandsInterface都是接口
mCalIStateReg istrants. notifyRegistrant 发出通知后,仅一个 地方可响应此消息通知,即GsmCdmaCallTracker 类的handleMessage 方法,本质上是个Handler处理类
frameworks/opt/telephony/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
跟进GsmCdmaCallTracker的handleMessage方法,可以看到其在初始化的地方注册了CallStateChanged消息的监听,注册的时候传入了自身的handler,notify的时候使用该handler发送消息,所以我们可以在handleMessage中响应并处理EVENT_CALL_STATE_CHANGE消息。
@Overridepublic void handleMessage(Message msg) {AsyncResult ar;switch (msg.what) {...case EVENT_CALL_STATE_CHANGE:pollCallsWhenSafe();...
pollCallsWhenSafe 调用父类CallTracker 方法
frameworks/opt/telephony/src/java/com/android/internal/telephony/CallTracker.java
protected void pollCallsWhenSafe() {mNeedsPoll = true;mLastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT);mCi.getCurrentCalls(mLastRelevantPoll);}}
发现其又调用了mCi.getCurrentCalls去查询当前的Call情况,此方法会调用到RIL中对应的方法去(Phone创建的时候对mCi进行初始化使得具有RIL通信能力,其mCi即调用RIL.java)
又回到了 RIL 中
frameworks/opt/telephony/src/java/com/android/internal/telephony/RIL.java
@Overridepublic void getCurrentCalls(Message result) {IRadio radioProxy = getRadioProxy(result);if (radioProxy != null) {RILRequest rr = obtainRequest(RIL_REQUEST_GET_CURRENT_CALLS, result,mRILDefaultWorkSource);if (RILJ_LOGD) {riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));}try {//向底层发起请求radioProxy.getCurrentCalls(rr.mSerial);} catch (RemoteException | RuntimeException e) {handleRadioProxyExceptionForRR(rr, "getCurrentCalls", e);}}}
在发送请求后,等待modem返回结果给RIL层,继续跟踪返回消息的接收(省去 与 RIL 层的交互)
在RIL.java中查找底层消息反馈的处理方法,发现有两个方法processRespose和processResponseDone,向上追溯发现其是在RadioResponse.java中的
responseCurrentCalls方法 调用了 processResponseDone 方法,如下:
frameworks/opt/telephony/src/java/com/android/internal/telephony/RadioResponse.java
其继承自一个底层的服务
public class RadioResponse extends IRadioResponse.Stub
CurrentCall的消息继续在responseCurrentCalls方法追踪。
public void getCurrentCallsResponse(RadioResponseInfo responseInfo,ArrayList<android.hardware.radio.V1_0.Call> calls) {responseCurrentCalls(responseInfo, calls);}
继续追踪调用的responseCurrentCalls方法
private void responseCurrentCalls(RadioResponseInfo responseInfo,ArrayList<android.hardware.radio.V1_0.Call> calls) {RILRequest rr = mRil.processResponse(responseInfo);if (rr != null) {int num = calls.size();ArrayList<DriverCall> dcCalls = new ArrayList<DriverCall>(num);DriverCall dc;for (int i = 0; i < num; i++) {dc = new DriverCall();// TODO: change name of function stateFromCLCC() in DriverCall.java to name// clarifying what is CLCCdc.state = DriverCall.stateFromCLCC((int) (calls.get(i).state));dc.index = calls.get(i).index;dc.TOA = calls.get(i).toa;dc.isMpty = calls.get(i).isMpty;dc.isMT = calls.get(i).isMT;dc.als = calls.get(i).als;dc.isVoice = calls.get(i).isVoice;dc.isVoicePrivacy = calls.get(i).isVoicePrivacy;dc.number = calls.get(i).number;dc.numberPresentation =DriverCall.presentationFromCLIP((int) (calls.get(i).numberPresentation));dc.name = calls.get(i).name;dc.namePresentation =DriverCall.presentationFromCLIP((int) (calls.get(i).namePresentation));if (calls.get(i).uusInfo.size() == 1) {dc.uusInfo = new UUSInfo();dc.uusInfo.setType(calls.get(i).uusInfo.get(0).uusType);dc.uusInfo.setDcs(calls.get(i).uusInfo.get(0).uusDcs);if (!TextUtils.isEmpty(calls.get(i).uusInfo.get(0).uusData)) {byte[] userData = calls.get(i).uusInfo.get(0).uusData.getBytes();dc.uusInfo.setUserData(userData);} else {mRil.riljLog("responseCurrentCalls: uusInfo data is null or empty");}mRil.riljLogv(String.format("Incoming UUS : type=%d, dcs=%d, length=%d",dc.uusInfo.getType(), dc.uusInfo.getDcs(),dc.uusInfo.getUserData().length));mRil.riljLogv("Incoming UUS : data (hex): "+ IccUtils.bytesToHexString(dc.uusInfo.getUserData()));} else {mRil.riljLogv("Incoming UUS : NOT present!");}// Make sure there's a leading + on addresses with a TOA of 145dc.number = PhoneNumberUtils.stringFromStringAndTOA(dc.number, dc.TOA);dcCalls.add(dc);if (dc.isVoicePrivacy) {mRil.mVoicePrivacyOnRegistrants.notifyRegistrants();mRil.riljLog("InCall VoicePrivacy is enabled");} else {mRil.mVoicePrivacyOffRegistrants.notifyRegistrants();mRil.riljLog("InCall VoicePrivacy is disabled");}}Collections.sort(dcCalls);if ((num == 0) && mRil.mTestingEmergencyCall.getAndSet(false)) {if (mRil.mEmergencyCallbackModeRegistrant != null) {mRil.riljLog("responseCurrentCalls: call ended, testing emergency call,"+ " notify ECM Registrants");mRil.mEmergencyCallbackModeRegistrant.notifyRegistrant();}}//发送返回消息if (responseInfo.error == RadioError.NONE) {sendMessageResponse(rr.mResult, dcCalls);}mRil.processResponseDone(rr, responseInfo, dcCalls);}}发现其通过底层返回的消息创建了dcCalls对象,也就是当前的Call状态信息,对dc状态进行判断后如果有需要就notify通知,如果没有异常则通过sendMessageResponse方法发送消息public static void sendMessageResponse(Message msg, Object ret) {if (msg != null) {AsyncResult.forMessage(msg, ret, null);msg.sendToTarget();}}
继续回到GsmCdmaCallTracker的handleMessage中,之前发送请求的时候有发送EVENT_POLL_CALLS_RESULT,这里我们继续回到该事件处理的地方来分析
frameworks/opt/telephony/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
@Overridepublic void handleMessage(Message msg) {AsyncResult ar;switch (msg.what) {case EVENT_POLL_CALLS_RESULT:Rlog.d(LOG_TAG, "Event EVENT_POLL_CALLS_RESULT Received");if (msg == mLastRelevantPoll) {if (DBG_POLL) log("handle EVENT_POLL_CALL_RESULT: set needsPoll=F");mNeedsPoll = false;mLastRelevantPoll = null;handlePollCalls((AsyncResult)msg.obj);}break;
继续追踪handlePollCalls
@Overrideprotected synchronized void handlePollCalls(AsyncResult ar) {//解析返回的结果for (int i = 0, curDC = 0, dcSize = polledCalls.size(); i < mConnections.length; i++) {GsmCdmaConnection conn = mConnections[i];DriverCall dc = null;// polledCall list is sparseif (curDC < dcSize) {dc = (DriverCall) polledCalls.get(curDC);if (dc.index == i+1) {curDC++;} else {dc = null;}}...if (conn == null && dc != null) {...状态的处理及识别//响铃消息通知if (newRinging != null) {mPhone.notifyNewRingingConnection(newRinging);}// clear the "local hangup" and "missed/rejected call"// cases from the "dropped during poll" list// These cases need no "last call fail" reasonArrayList<GsmCdmaConnection> locallyDisconnectedConnections = new ArrayList<>();for (int i = mDroppedDuringPoll.size() - 1; i >= 0 ; i--) {GsmCdmaConnection conn = mDroppedDuringPoll.get(i);//CDMAboolean wasDisconnected = false;//来电处理,本地挂断或者未接,本地挂断的话直接设置挂断的原因为LOCAL或INVALID_NUMBERif (conn.isIncoming() && conn.getConnectTime() == 0) {// Missed or rejected callint 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;locallyDisconnectedConnections.add(conn);} else if (conn.mCause == DisconnectCause.LOCAL|| conn.mCause == DisconnectCause.INVALID_NUMBER) {mDroppedDuringPoll.remove(i);hasAnyCallDisconnected |= conn.onDisconnect(conn.mCause);wasDisconnected = true;locallyDisconnectedConnections.add(conn);}if (!isPhoneTypeGsm() && wasDisconnected && unknownConnectionAppeared&& conn == newUnknownConnectionCdma) {unknownConnectionAppeared = false;newUnknownConnectionCdma = null;}}if (locallyDisconnectedConnections.size() > 0) {mMetrics.writeRilCallList(mPhone.getPhoneId(), locallyDisconnectedConnections);}/* Disconnect any pending Handover connections *///通话断开的一些处理操作...if (newRinging != null || hasNonHangupStateChanged || hasAnyCallDisconnected) {internalClearDisconnected();}//更新phone状态if (VDBG) log("handlePollCalls calling updatePhoneState()");updatePhoneState();...}
其先对底层反馈的消息进行解析,获取其通话状态,判断如果是来电则发出notifyNewRingingConnection响铃消息通知,然后进行一些通话断开连接的操作及更新phone状态。继续跟进notifyNewRingingConnection响铃消息,该消息调用的是phone的方法,往上追溯。
继续查看响铃消息通知到何处,全局搜索(registerForNewRingingConnection),找到
PstnIncommingCallNotifier.java
packages/services/Telephony/src/com/android/services/telephony/PstnIncomingCallNotifier.java
/*** Listens to incoming-call events from the associated phone object and notifies Telecom upon each* occurence. One instance of these exists for each of the telephony-based call services.*/
用来 监听来电消息
private final Handler mHandler = new Handler() {@Overridepublic 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方法处理调用sendIncommingCallIntent发送Intent,这里其实就已经从RIL传递消息到了应用层了
private void handleNewRingingConnection(AsyncResult asyncResult) {Log.d(this, "handleNewRingingConnection");Connection connection = (Connection) asyncResult.result;if (connection != null) {Call call = connection.getCall();// Check if we have a pending number verification request.if (connection.getAddress() != null) {if (NumberVerificationManager.getInstance().checkIncomingCall(connection.getAddress())) {// Disconnect the call if it matchestry {connection.hangup();} catch (CallStateException e) {Log.e(this, e, "Error hanging up potential number verification call");}return;}}// Final verification of the ringing state before sending the intent to Telecom.if (call != null && call.getState().isRinging()) {sendIncomingCallIntent(connection);}}}
private void sendIncomingCallIntent(Connection connection) {------PhoneAccountHandle handle = findCorrectPhoneAccountHandle();if (handle == null) {try {connection.hangup();} catch (CallStateException e) {// connection already disconnected. Do nothing}} else {TelecomManager.from(mPhone.getContext()).addNewIncomingCall(handle, extras);}}
//获取telecomm服务,通过aidl接口调用 telecomService 的addNewIncomingCall方法
//获取telecomm服务public static TelecomManager from(Context context) {return (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);}
进入到TelecomService层
packages/services/Telecomm/src/com/android/server/telecom/TelecomServiceImpl.java
108 private final ITelecomService.Stub mBinderImpl = new ITelecomService.Stub() {
109 @Override1083 public void addNewIncomingCall(PhoneAccountHandle phoneAccountHandle, Bundle extras) {
1084 try {
1085 Log.startSession("TSI.aNIC");
1086 synchronized (mLock) {
1087 Log.i(this, "Adding new incoming call with phoneAccountHandle %s",
1088 phoneAccountHandle);
1089 。。。。。。。。。
。。。。。。。。。
1116 long token = Binder.clearCallingIdentity();
1117 try {
1118 Intent intent = new Intent(TelecomManager.ACTION_INCOMING_CALL);
1119 intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
1120 phoneAccountHandle);
1121 intent.putExtra(CallIntentProcessor.KEY_IS_INCOMING_CALL, true);
1122 if (extras != null) {
1123 extras.setDefusable(true);
1124 intent.putExtra(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS, extras);
1125 }
1126 mCallIntentProcessorAdapter.processIncomingCallIntent(
1127 mCallsManager, intent);
1128 } finally {
1129 Binder.restoreCallingIdentity(token);
1130 }
1131 } else {
1132 Log.w(this, "Null phoneAccountHandle. Ignoring request to add new" +
1133 " incoming call");
1134 }
1135 }
1136 } finally {
1137 Log.endSession();
1138 }
1139 }
继续跟进mCallIntentProcessorAdapter.processIncomingCallIntent,
packages/services/Telecomm/src/com/android/server/telecom/CallIntentProcessor.java
287 static void processIncomingCallIntent(CallsManager callsManager, Intent intent) {
288 PhoneAccountHandle phoneAccountHandle = intent.getParcelableExtra(
289 TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
290
291 if (phoneAccountHandle == null) {
292 Log.w(CallIntentProcessor.class,
293 "Rejecting incoming call due to null phone account");
294 return;
295 }
296 if (phoneAccountHandle.getComponentName() == null) {
297 Log.w(CallIntentProcessor.class,
298 "Rejecting incoming call due to null component name");
299 return;
300 }
301
302 Bundle clientExtras = null;
303 if (intent.hasExtra(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS)) {
304 clientExtras = intent.getBundleExtra(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS);
305 }
306 if (clientExtras == null) {
307 clientExtras = new Bundle();
308 }
309
310 Log.d(CallIntentProcessor.class,
311 "Processing incoming call from connection service [%s]",
312 phoneAccountHandle.getComponentName());
313 callsManager.processIncomingCallIntent(phoneAccountHandle, clientExtras);
314 }
--------------------------------------------------------------------------
下方与拨号相同
packages/services/Telecomm/src/com/android/server/telecom/CallsManager.java
进入到CallsManager中继续跟进
void processIncomingCallIntent(PhoneAccountHandle phoneAccountHandle, Bundle extras) {Log.d(this, "processIncomingCallIntent");...Call call = new Call(getNextCallId(),mContext,this,mLock,mConnectionServiceRepository,mContactsAsyncHelper,mCallerInfoAsyncQueryFactory,mPhoneNumberUtilsAdapter,handle,null /* gatewayInfo */,null /* connectionManagerPhoneAccount */,phoneAccountHandle,Call.CALL_DIRECTION_INCOMING /* callDirection */,false /* forceAttachToExistingConnection */,false, /* isConference */mClockProxy);...call的一些状态设置...call.initAnalytics();if (getForegroundCall() != null) {getForegroundCall().getAnalytics().setCallIsInterrupted(true);call.getAnalytics().setCallIsAdditional(true);}setIntentExtrasAndStartTime(call, extras);//添加监听// TODO: Move this to be a part of addCall()call.addListener(this);if (!isHandoverAllowed || (call.isSelfManaged() && !isIncomingCallPermitted(call,call.getTargetPhoneAccount()))) {notifyCreateConnectionFailed(phoneAccountHandle, call);} else {//成功上报上去建立连接call.startCreateConnection(mPhoneAccountRegistrar);}}
packages/services/Telecomm/src/com/android/server/telecom/Call.java
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();}
继续跟踪process 方法
packages/services/Telecomm/src/com/android/server/telecom/CreateConnectionProcessor.java
public void process() {Log.v(this, "process");clearTimeout();mAttemptRecords = new ArrayList<>();if (mCall.getTargetPhoneAccount() != null) {mAttemptRecords.add(new CallAttemptRecord(mCall.getTargetPhoneAccount(), mCall.getTargetPhoneAccount()));}if (!mCall.isSelfManaged()) {adjustAttemptsForConnectionManager();adjustAttemptsForEmergency(mCall.getTargetPhoneAccount());}mAttemptRecordIterator = mAttemptRecords.iterator();attemptNextPhoneAccount();}
其中 attemptNextPhoneAccount 方法
private void attemptNextPhoneAccount() {Log.v(this, "attemptNextPhoneAccount");CallAttemptRecord attempt = null;if (mCallResponse != null && attempt != null) {Log.i(this, "Trying attempt %s", attempt);PhoneAccountHandle phoneAccount = attempt.connectionManagerPhoneAccount;mService = mRepository.getService(phoneAccount.getComponentName(),phoneAccount.getUserHandle());if (mService == null) {Log.i(this, "Found no connection service for attempt %s", attempt);attemptNextPhoneAccount();} else {mConnectionAttempt++;mCall.setConnectionManagerPhoneAccount(attempt.connectionManagerPhoneAccount);mCall.setTargetPhoneAccount(attempt.targetPhoneAccount);mCall.setConnectionService(mService);setTimeoutIfNeeded(mService, attempt);if (mCall.isIncoming()) {mService.createConnection(mCall, CreateConnectionProcessor.this);} else {// Start to create the connection for outgoing call after the ConnectionService// of the call has gained the focus.mCall.getConnectionServiceFocusManager().requestFocus(mCall,new CallsManager.RequestCallback(new CallsManager.PendingAction() {@Overridepublic void performAction() {Log.d(this, "perform create connection");mService.createConnection(mCall,CreateConnectionProcessor.this);}}));}}} else {Log.v(this, "attemptNextPhoneAccount, no more accounts, failing");DisconnectCause disconnectCause = mLastErrorDisconnectCause != null ?mLastErrorDisconnectCause : new DisconnectCause(DisconnectCause.ERROR);notifyCallConnectionFailure(disconnectCause);}}
核心代码:
private ConnectionServiceWrapper mService; if (mCall.isIncoming()) {mService.createConnection(mCall, CreateConnectionProcessor.this);}
void createConnection(final Call call, final CreateConnectionResponse response) {// 链接创建成功后 onSuccess 会被调用BindCallback callback = new BindCallback() {@Overridepublic void onSuccess() {try {/// M: For VoLTE @{boolean isConferenceDial = call.isConferenceDial();// 会议通话if (isConferenceDial) {logOutgoing("createConference(%s) via %s.", call, getComponentName());mServiceInterface.createConference(call.getConnectionManagerPhoneAccount(),callId,new ConnectionRequest(call.getTargetPhoneAccount(),call.getHandle(),extras,call.getVideoState()),call.getConferenceParticipants(),call.isIncoming());// 非会议(拨打电话会走这里)} else {// 通过远程接口创建链接mServiceInterface.createConnection(call.getConnectionManagerPhoneAccount(),callId,new ConnectionRequest(call.getTargetPhoneAccount(),call.getHandle(),extras,call.getVideoState()),call.isIncoming(),call.isUnknown());}/// @}} catch (RemoteException e) {}}@Overridepublic void onFailure() {}};mBinder.bind(callback, call);
}
mBinder是Binder2对象,Binder2是ConnectionServiceWrapper的父类ServiceBinder内部类,所以此处调用的是的ServiceBinder的内部类的Binder2类的bind()方法,先new一个ServiceConnection对象,然后绑定一个远程服务端服务。如果绑定成功的话,在ServiceBinder的内部类ServiceBinderConnection的onServiceConnected()方法就被调用。
在这里做了两件事:
1).通过setBinder()方法,回调ConnectionServiceWrapper的setServiceInterface()方法,通过mServiceInterface = IConnectionService.Stub.asInterface(binder);
这行代码获取一个远程服务端的对象mServiceInterface 。
2)、再通过调用handleSuccessfulConnection()方法回调callback 的onSuccess()方法,也就又回到ConnectionServiceWrapper的createConnection()方法里。调用ConnectionService.java里mBinder的createConnection()方法然后通过message传递调用createConnection()方法。
packages/services/Telecomm/src/com/android/server/telecom/ServiceBinder.java
mBider 的bind 方法
void bind(BindCallback callback, Call call) {if (mServiceConnection == null) {Intent serviceIntent = new Intent(mServiceAction).setComponent(mComponentName);ServiceConnection connection = new ServiceBinderConnection(call);// 进行绑定if (mUserHandle != null) {isBound = mContext.bindServiceAsUser(serviceIntent, connection, bindingFlags,mUserHandle);} else {isBound = mContext.bindService(serviceIntent, connection, bindingFlags);}if (!isBound) {handleFailedConnection();return;}} else {}}
绑定成功后,ServiceBinderConnection 的 onServiceConnected 会触发
@Overridepublic void onServiceConnected(ComponentName componentName, IBinder binder) {try {if (binder != null) {mServiceDeathRecipient = new ServiceDeathRecipient(componentName);try {binder.linkToDeath(mServiceDeathRecipient, 0);mServiceConnection = this;
// 会触发 ConnectionServiceWrapper.setServiceInterface ==>
ConnectionServiceWrapper.addConnectionServiceAdapter
通过mServiceInterface,给绑定的服务,提供一个访问自己的接口setBinder(binder);// 触发bind(BindCallback callback, Call call) 中 callback 的 onSuccesshandleSuccessfulConnection();} catch (RemoteException e) {Log.w(this, "onServiceConnected: %s died.");if (mServiceDeathRecipient != null) {mServiceDeathRecipient.binderDied();}}}}} finally {Log.endSession();}}
整个绑定过程,只做2件事,
一是给远程服务提供访问自己的接口,
二是利用远程接口创建一个通话链接。
这2件事都是跨进程进行的。远程服务访问自己的接口是 ConnectionServiceWrapper.Adapter
, 是一个Binder。
packages/services/Telecomm/src/com/android/server/telecom/ConnectionServiceWrapper.java
由内部类 Adapter 继承于 IConnectionServiceAdapter.Stub,可以看出会进行跨进程访问
public class ConnectionServiceWrapper extends ServiceBinder implementsConnectionServiceFocusManager.ConnectionServiceFocus {private final class Adapter extends IConnectionServiceAdapter.Stub {
调用ConnectionService.java里mBinder的createConnection()方法然后通过message传递调用createConnection()方法。
private void handleSuccessfulConnection() {// Make a copy so that we don't have a deadlock inside one of the callbacks.Set<BindCallback> callbacksCopy = new ArraySet<>();synchronized (mCallbacks) {callbacksCopy.addAll(mCallbacks);mCallbacks.clear();}for (BindCallback callback : callbacksCopy) {callback.onSuccess();}}
1094 public void createConnection(final Call call, final CreateConnectionResponse response) {
1095 Log.d(this, "createConnection(%s) via %s.", call, getComponentName());
1096 BindCallback callback = new BindCallback() {
1097 @Override
1098 public void onSuccess() {
1099 String callId = mCallIdMapper.getCallId(call);
1100 mPendingResponses.put(callId, response);
1101
///---------//1146
1150 // 核心代码
1151 try {
1152 mServiceInterface.createConnection(
1153 call.getConnectionManagerPhoneAccount(),
1154 callId,
1155 connectionRequest,
1156 call.shouldAttachToExistingConnection(),
1157 call.isUnknown(),
1158 Log.getExternalSession());
1159
1160 } catch (RemoteException e) {
1161 Log.e(this, e, "Failure to createConnection -- %s", getComponentName());
1162 mPendingResponses.remove(callId).handleCreateConnectionFailure(
1163 new DisconnectCause(DisconnectCause.ERROR, e.toString()));
1164 }
1165 }
1166
1167 @Override
1168 public void onFailure() {
1169 Log.e(this, new Exception(), "Failure to call %s", getComponentName());
1170 response.handleCreateConnectionFailure(new DisconnectCause(DisconnectCause.ERROR));
1171 }
1172 };
1173
1174 mBinder.bind(callback, call);
1175 }
private IConnectionService mServiceInterface;
-----------------------------------------------------------------
createConnection()方法通过判断是来电还是去电分别创建不同的connection,
/*** This can be used by telecom to either create a new outgoing call or attach to an existing* incoming call. In either case, telecom will cycle through a set of services and call* createConnection util a connection service cancels the process or completes it successfully.*//** {@hide} */protected 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);//判断是来电还是去电创造不同的connectionConnection connection = isUnknown ? onCreateUnknownConnection(callManagerAccount, request): isIncoming ? onCreateIncomingConnection(callManagerAccount, request): onCreateOutgoingConnection(callManagerAccount, request);...Log.d(this, "createConnection, calling handleCreateConnectionSuccessful %s", callId);//创建成功后调用mAdapter.handleCreateConnectionComplete(callId,request,new ParcelableConnection(...if (isIncoming && request.shouldShowIncomingCallUi() &&(connection.getConnectionProperties() & Connection.PROPERTY_SELF_MANAGED) ==Connection.PROPERTY_SELF_MANAGED) {// Tell ConnectionService to show its incoming call UX.connection.onShowIncomingCallUi();}if (isUnknown) {triggerConferenceRecalculate();}}
此处先分析: onCreateIncomingConnection
运行完成后分析:mAdapter.handleCreateConnectionComplete
其中:
public Connection onCreateIncomingConnection(PhoneAccountHandle connectionManagerPhoneAccount,ConnectionRequest request) {return null;}
当判断是来电时会通过 onCreateIncomingConnection 创建连接,直接跟进去发现是空实现,那么该方法的实现应该在该类的子类中,跟进到其子类TelephonyConnectionService中
packages/services/Telephony/src/com/android/services/telephony/TelephonyConnectionService.java
public class TelephonyConnectionService extends ConnectionService {
/*** Service for making GSM and CDMA connections.*/
public class TelephonyConnectionService extends ConnectionService @Overridepublic Connection onCreateIncomingConnection(PhoneAccountHandle connectionManagerPhoneAccount,ConnectionRequest request) {...Phone phone = getPhoneForAccount(accountHandle, isEmergency);...Call call = phone.getRingingCall();com.android.internal.telephony.Connection originalConnection =call.getState() == Call.State.WAITING ?call.getLatestConnection() : call.getEarliestConnection();...Connection connection =createConnectionFor(phone, originalConnection, false /* isOutgoing */,request.getAccountHandle(), request.getTelecomCallId(),request.getAddress(), videoState);...}
跳转到 createConnectionFor 方法中
private TelephonyConnection createConnectionFor(Phone phone,com.android.internal.telephony.Connection originalConnection,boolean isOutgoing,PhoneAccountHandle phoneAccountHandle,String telecomCallId,Uri address,int videoState) {TelephonyConnection returnConnection = null;int phoneType = phone.getPhoneType();if (phoneType == TelephonyManager.PHONE_TYPE_GSM) {returnConnection = new GsmConnection(originalConnection, telecomCallId, isOutgoing);} else if (phoneType == TelephonyManager.PHONE_TYPE_CDMA) {boolean allowsMute = allowsMute(phone);returnConnection = new CdmaConnection(originalConnection, mEmergencyTonePlayer,allowsMute, isOutgoing, telecomCallId);}if (returnConnection != null) {// Listen to Telephony specific callbacks from the connectionreturnConnection.addTelephonyConnectionListener(mTelephonyConnectionListener);returnConnection.setVideoPauseSupported(TelecomAccountRegistry.getInstance(this).isVideoPauseSupported(phoneAccountHandle));returnConnection.setManageImsConferenceCallSupported(TelecomAccountRegistry.getInstance(this).isManageImsConferenceCallSupported(phoneAccountHandle));returnConnection.setShowPreciseFailedCause(TelecomAccountRegistry.getInstance(this).isShowPreciseFailedCause(phoneAccountHandle));}return returnConnection;}
继续 分析 mAdapter.handleCreateConnectionComplete
这里通过AIDL进行通信,搜索 IConnectionServiceAdapter.Stub,跟进到 ConnectionServiceWrapper
frameworks/base/telecomm/java/android/telecom/ConnectionServiceAdapter.java
private final class Adapter extends IConnectionServiceAdapter.Stub {@Overridepublic void handleCreateConnectionComplete(String callId, ConnectionRequest request,ParcelableConnection connection, Session.Info sessionInfo) {Log.startSession(sessionInfo, LogUtils.Sessions.CSW_HANDLE_CREATE_CONNECTION_COMPLETE);long token = Binder.clearCallingIdentity();try {synchronized (mLock) {logIncoming("handleCreateConnectionComplete %s", callId);ConnectionServiceWrapper.this.handleCreateConnectionComplete(callId, request, connection);
继续跟进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 uniformlyCall foundCall = mCallIdMapper.getCall(callId);if (foundCall != null) {// The post-dial digits are created when the call is first created. Normally// the ConnectionService is responsible for stripping them from the address, but// since a failed connection will not have done this, we could end up with duplicate// post-dial digits.foundCall.clearPostDialDigits();}removeCall(callId, connection.getDisconnectCause());} else {// Successful connectionif (mPendingResponses.containsKey(callId)) {mPendingResponses.remove(callId).handleCreateConnectionSuccess(mCallIdMapper, connection);}}}
mPendingResponses是hashMap容器,每次在 createConnection 的时候会将对象加入该容器,如果此时connection还未断开的,会移除此connection,调用hanleCreateConnectionSuccess方法。
往上追溯CreateConnectionResponse,是一个接口,跟踪到mService.createConnection(mCall, this);
public class CreateConnectionProcessor implements CreateConnectionResponse {
CreateConnectionProcessor.java会把自身传入,发现该类也实现了 CreateConnectionResponse ,所以这里的 handleCreateConnectionSuccess
调用的是这个类里面的方法
packages/services/Telecomm/src/com/android/server/telecom/CreateConnectionProcessor.java
private CreateConnectionResponse mCallResponse;@Overridepublic 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 callmService.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.}}
还是 CreateConnectionResponse 接口
实现的是 Call 对象 ,Call 也实现了该接口
public class Call implements CreateConnectionResponse, EventManager.Loggable,
84 ConnectionServiceFocusManager.CallFocus {
packages/services/Telecomm/src/com/android/server/telecom/Call.java
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());setConnectionProperties(connection.getConnectionProperties());setIsVoipAudioMode(connection.getIsVoipAudioMode());setSupportedAudioRoutes(connection.getSupportedAudioRoutes());setVideoProvider(connection.getVideoProvider());setVideoState(connection.getVideoState());setRingbackRequested(connection.isRingbackRequested());setStatusHints(connection.getStatusHints());putExtras(SOURCE_CONNECTION_SERVICE, connection.getExtras());mConferenceableCalls.clear();for (String id : connection.getConferenceableConnectionIds()) {mConferenceableCalls.add(idMapper.getCall(id));}switch (mCallDirection) {case CALL_DIRECTION_INCOMING:for (Listener l : mListeners) {
// 此处触发了回调l.onSuccessfulIncomingCall(this);}break;
。。。。。
这里根据来电类型,触发回调,监听者会收到通知
,之前在CallManager中执行 processIncomingCallIntent 方法创建Call的时候就添加了监听,所以最后会回调到CallsManager中
packages/services/Telecomm/src/com/android/server/telecom/CallsManager.java
可以看出CallsManager的管理作用,创建Calls并添加监听,在完成Call的相关初始化后进行进一步处理,其实就是传递消息到别的的地方去。
public void onSuccessfulIncomingCall(Call incomingCall) {Log.d(this, "onSuccessfulIncomingCall");PhoneAccount phoneAccount = mPhoneAccountRegistrar.getPhoneAccountUnchecked(incomingCall.getTargetPhoneAccount());Bundle extras =phoneAccount == null || phoneAccount.getExtras() == null? new Bundle(): phoneAccount.getExtras();if (incomingCall.hasProperty(Connection.PROPERTY_EMERGENCY_CALLBACK_MODE) ||incomingCall.isSelfManaged() ||extras.getBoolean(PhoneAccount.EXTRA_SKIP_CALL_FILTERING)) {Log.i(this, "Skipping call filtering for %s (ecm=%b, selfMgd=%b, skipExtra=%b)",incomingCall.getId(),incomingCall.hasProperty(Connection.PROPERTY_EMERGENCY_CALLBACK_MODE),incomingCall.isSelfManaged(),extras.getBoolean(PhoneAccount.EXTRA_SKIP_CALL_FILTERING));onCallFilteringComplete(incomingCall, new CallFilteringResult(true, false, true, true));incomingCall.setIsUsingCallFiltering(false);return;}incomingCall.setIsUsingCallFiltering(true);
//迭代器模式List<IncomingCallFilter.CallFilter> filters = new ArrayList<>();filters.add(new DirectToVoicemailCallFilter(mCallerInfoLookupHelper));filters.add(new AsyncBlockCheckFilter(mContext, new BlockCheckerAdapter(),mCallerInfoLookupHelper, null));filters.add(new CallScreeningServiceController(mContext, this, mPhoneAccountRegistrar,new ParcelableCallUtils.Converter(), mLock,new TelecomServiceImpl.SettingsSecureAdapterImpl(), mCallerInfoLookupHelper,new CallScreeningServiceHelper.AppLabelProxy() {@Overridepublic CharSequence getAppLabel(String packageName) {PackageManager pm = mContext.getPackageManager();try {ApplicationInfo info = pm.getApplicationInfo(packageName, 0);return pm.getApplicationLabel(info);} catch (PackageManager.NameNotFoundException nnfe) {Log.w(this, "Could not determine package name.");}return null;}}));
//IncomingCallFilter创建并执行 performFilteringnew IncomingCallFilter(mContext, this, incomingCall, mLock,mTimeoutsAdapter, filters).performFiltering();}
IncomingCallFilter创建并执行 performFiltering
packages/services/Telecomm/src/com/android/server/telecom/callfiltering/IncomingCallFilter.java
public void performFiltering() {Log.addEvent(mCall, LogUtils.Events.FILTERING_INITIATED);for (CallFilter filter : mFilters) {//遍历调用,依次执行异步查询方法filter.startFilterLookup(mCall, this);}// synchronized to prevent a race on mResult and to enter into Telecom.mHandler.postDelayed(new Runnable("ICF.pFTO", mTelecomLock) { // performFiltering time-out@Overridepublic void loggedRun() {if (mIsPending) {//超时处理的方法Log.i(IncomingCallFilter.this, "Call filtering has timed out.");Log.addEvent(mCall, LogUtils.Events.FILTERING_TIMED_OUT);//回CallsManager中的监听事件mListener.onCallFilteringComplete(mCall, mResult);mIsPending = false;}}}.prepare(), mTimeoutsAdapter.getCallScreeningTimeoutMillis(mContext.getContentResolver()));}
如果没有超时则在异步查询结束后,会通过回调方法将CallFilterResult传回onCallFilteringComplete.
public void onCallFilteringComplete(Call call, CallFilteringResult result) {synchronized (mTelecomLock) { // synchronizing to prevent race on mResultmNumPendingFilters--;mResult = result.combine(mResult);if (mNumPendingFilters == 0) {// synchronized on mTelecomLock to enter into Telecom.mHandler.post(new Runnable("ICF.oCFC", mTelecomLock) {@Overridepublic void loggedRun() {if (mIsPending) {Log.addEvent(mCall, LogUtils.Events.FILTERING_COMPLETED, mResult);mListener.onCallFilteringComplete(mCall, mResult);mIsPending = false;}}}.prepare());}}}
然后, 回到CallsManager中进行onCallFilteringComplete处理
packages/services/Telecomm/src/com/android/server/telecom/CallsManager.java
@Overridepublic void onCallFilteringComplete(Call incomingCall, CallFilteringResult result) {...if (incomingCall.getState() != CallState.DISCONNECTED &&incomingCall.getState() != CallState.DISCONNECTING &&incomingCall.getState() != CallState.ACTIVE) {//设置Call状态为RingsetCallState(incomingCall, CallState.RINGING,result.shouldAllowCall ? "successful incoming call" : "blocking call");} else {Log.i(this, "onCallFilteringCompleted: call already disconnected.");return;}if (result.shouldAllowCall) {if (MtkUtil.isInSingleVideoCallMode(incomingCall)) {...}if (hasMaximumManagedRingingCalls(incomingCall)) {if (shouldSilenceInsteadOfReject(incomingCall)) {incomingCall.silence();} else {Log.i(this, "onCallFilteringCompleted: Call rejected! " +"Exceeds maximum number of ringing calls.");rejectCallAndLog(incomingCall);}} else {//添加CalladdCall(incomingCall);}} else {if (result.shouldReject) {Log.i(this, "onCallFilteringCompleted: blocked call, rejecting.");incomingCall.reject(false, null);}if (result.shouldAddToCallLog) {Log.i(this, "onCallScreeningCompleted: blocked call, adding to call log.");if (result.shouldShowNotification) {Log.w(this, "onCallScreeningCompleted: blocked call, showing notification.");}//添加通话记录mCallLogManager.logCall(incomingCall, Calls.MISSED_TYPE,result.shouldShowNotification);} else if (result.shouldShowNotification) {Log.i(this, "onCallScreeningCompleted: blocked call, showing notification.");mMissedCallNotifier.showMissedCallNotification(new MissedCallNotifier.CallInfo(incomingCall));}}}
跟踪到 addCall 方法
3207 @VisibleForTesting
3208 public void addCall(Call call) {
3209 Trace.beginSection("addCall");
3210 Log.v(this, "addCall(%s)", call);
3211 call.addListener(this);
3212 mCalls.add(call);
3213
3214 // Specifies the time telecom finished routing the call. This is used by the dialer for
3215 // analytics.
3216 Bundle extras = call.getIntentExtras();
3217 extras.putLong(TelecomManager.EXTRA_CALL_TELECOM_ROUTING_END_TIME_MILLIS,
3218 SystemClock.elapsedRealtime());
3219
3220 updateCanAddCall();
3221 // onCallAdded for calls which immediately take the foreground (like the first call).
3222 for (CallsManagerListener listener : mListeners) {
3223 if (LogUtils.SYSTRACE_DEBUG) {
3224 Trace.beginSection(listener.getClass().toString() + " addCall");
3225 }
3226 listener.onCallAdded(call);
3227 if (LogUtils.SYSTRACE_DEBUG) {
3228 Trace.endSection();
3229 }
3230 }
3231 Trace.endSection();
3232 }
//通知监听Call添加的观察者
listener.onCallAdded(call);
添加的观察者,和拨号一样,主要是 CllAudilManager (负责响铃)和 InCallController
mListeners.add(mInCallWakeLockController);mListeners.add(statusBarNotifier);mListeners.add(mCallLogManager);mListeners.add(mPhoneStateBroadcaster);mListeners.add(mInCallController);mListeners.add(mCallAudioManager);mListeners.add(mCallRecordingTonePlayer);mListeners.add(missedCallNotifier);mListeners.add(mHeadsetMediaButton);mListeners.add(mProximitySensorManager);
其中:
packages/services/Telecomm/src/com/android/server/telecom/CallAudioManager.java
private void onCallEnteringRinging() {
641 if (mRingingCalls.size() == 1) {
642 mCallAudioModeStateMachine.sendMessageWithArgs(
643 CallAudioModeStateMachine.NEW_RINGING_CALL,
644 makeArgsForModeStateMachine());
645 }
646 }
packages/services/Telecomm/src/com/android/server/telecom/InCallController.java
查看 InCallController 的 onCallAdded 方法
@Overridepublic void onCallAdded(Call call) {if (!isBoundAndConnectedToServices()) {bindToServices(call);} else {// We are bound, and we are connected.adjustServiceBindingsForEmergency();// This is in case an emergency call is added while there is an existing call.mEmergencyCallHelper.maybeGrantTemporaryLocationPermission(call,mCallsManager.getCurrentUserHandle());//添加CalladdCall(call);List<ComponentName> componentsUpdated = new ArrayList<>();for (Map.Entry<InCallServiceInfo, IInCallService> entry : mInCallServices.entrySet()) {InCallServiceInfo info = entry.getKey();if (call.isExternalCall() && !info.isExternalCallsSupported()) {continue;}if (call.isSelfManaged() && !info.isSelfManagedCallsSupported()) {continue;}// Only send the RTT call if it's a UI in-call serviceboolean includeRttCall = info.equals(mInCallServiceConnection.getInfo());componentsUpdated.add(info.getComponentName());IInCallService inCallService = entry.getValue();ParcelableCall parcelableCall = ParcelableCallUtils.toParcelableCall(call,true /* includeVideoProvider */, mCallsManager.getPhoneAccountRegistrar(),info.isExternalCallsSupported(), includeRttCall);try {//AIDL调用远程的addCall方法inCallService.addCall(parcelableCall);} catch (RemoteException ignored) {}}Log.i(this, "Call added to components: %s", componentsUpdated);}}
Telecomm Framework
frameworks/base/telecomm/java/android/telecom/InCallService.java
291 /** Manages the binder calls so that the implementor does not need to deal with it. */
292 private final class InCallServiceBinder extends IInCallService.Stub {
293 @Override
294 public void setInCallAdapter(IInCallAdapter inCallAdapter) {
295 mHandler.obtainMessage(MSG_SET_IN_CALL_ADAPTER, inCallAdapter).sendToTarget();
296 }
297
298 @Override
299 public void addCall(ParcelableCall call) {
300 mHandler.obtainMessage(MSG_ADD_CALL, call).sendToTarget();
301 }
继续跟进MSG_ADD_CALL消息的处理
205 /** Default Handler used to consolidate binder method calls onto a single thread. */
206 private final Handler mHandler = new Handler(Looper.getMainLooper()) {
207 @Override
208 public void handleMessage(Message msg) {
209 if (mPhone == null && msg.what != MSG_SET_IN_CALL_ADAPTER) {
210 return;
211 }
212
213 switch (msg.what) {
214 case MSG_SET_IN_CALL_ADAPTER:
215 String callingPackage = getApplicationContext().getOpPackageName();
216 mPhone = new Phone(new InCallAdapter((IInCallAdapter) msg.obj), callingPackage,
217 getApplicationContext().getApplicationInfo().targetSdkVersion);
218 mPhone.addListener(mPhoneListener);
219 onPhoneCreated(mPhone);
220 break;
221 case MSG_ADD_CALL:
222 mPhone.internalAddCall((ParcelableCall) msg.obj);
223 break;
继续跟进internalAddCall
frameworks/base/telecomm/java/android/telecom/Phone.java
final void internalAddCall(ParcelableCall parcelableCall) {//创建CallCall call = new Call(this, parcelableCall.getId(), mInCallAdapter,parcelableCall.getState(), mCallingPackage, mTargetSdkVersion);mCallByTelecomCallId.put(parcelableCall.getId(), call);//添加到列表中mCalls.add(call);checkCallTree(parcelableCall);call.internalUpdate(parcelableCall, mCallByTelecomCallId);//调用phone的监听者的onCallAddedfireCallAdded(call);}private void fireCallAdded(Call call) {for (Listener listener : mListeners) {listener.onCallAdded(this, call);}}
在 InCallService 中handleMessage处理MSG_SET_IN_CALL_ADAPTER消息的时候就注册了监听,这里继续跟进到InCallService中的实现去
frameworks/base/telecomm/java/android/telecom/InCallService.java
private Phone.Listener mPhoneListener = new Phone.Listener() {/** ${inheritDoc} */@Overridepublic void onAudioStateChanged(Phone phone, AudioState audioState) {InCallService.this.onAudioStateChanged(audioState);}
。。。。。///** ${inheritDoc} */@Overridepublic void onCallAdded(Phone phone, Call call) {InCallService.this.onCallAdded(call);}
这其实是个空实现,具体实现是在子类中,继续跟进到子类 InCallServiceImpl 中分析
packages/apps/Dialer/java/com/android/incallui/InCallServiceImpl.java
Dialer 层
public class InCallServiceImpl extends InCallService {private ReturnToCallController returnToCallController;private CallList.Listener feedbackListener;// We only expect there to be one speakEasyCallManager to be instantiated at a time.// We did not use a singleton SpeakEasyCallManager to avoid holding on to state beyond the// lifecycle of this service, because the singleton is associated with the state of the// Application, not this service.private SpeakEasyCallManager speakEasyCallManager;@Overridepublic void onCallAudioStateChanged(CallAudioState audioState) {Trace.beginSection("InCallServiceImpl.onCallAudioStateChanged");AudioModeProvider.getInstance().onAudioStateChanged(audioState);Trace.endSection();}///-----------@Overridepublic void onCallAdded(Call call) {Trace.beginSection("InCallServiceImpl.onCallAdded");InCallPresenter.getInstance().onCallAdded(call);Trace.endSection();}
跟踪到这里可以看到其所在目录是Dialer下的inCallUI了,这就到dialer层了,如果查看Diler的清单文件可以发现其有android.telecom.InCallService声明
packages/apps/Dialer/java/com/android/incallui/InCallPresenter.java
public void onCallAdded(final android.telecom.Call call) {LatencyReport latencyReport = new LatencyReport(call);if (shouldAttemptBlocking(call)) {maybeBlockCall(call, latencyReport);} else {if (call.getDetails().hasProperty(CallCompat.Details.PROPERTY_IS_EXTERNAL_CALL)) {mExternalCallList.onCallAdded(call);} else {latencyReport.onCallBlockingDone();//CallList(Call的维护列表)调用onCallAddedmCallList.onCallAdded(mContext, call, latencyReport);}}// Since a call has been added we are no longer waiting for Telecom to send us a call.setBoundAndWaitingForOutgoingCall(false, null);call.registerCallback(mCallCallback);}
packages/apps/Dialer/java/com/android/incallui/call/CallList.java
public void onCallAdded(final Context context, final android.telecom.Call telecomCall, LatencyReport latencyReport) {Trace.beginSection("onCallAdded");...if (call.getState() == DialerCall.State.INCOMING|| call.getState() == DialerCall.State.CALL_WAITING) {//来电调用onIncoming(call);} else {dialerCallListener.onDialerCallUpdate();}...
}
跟踪 onIncoming 方法
/** Called when a single call has changed. */private void onIncoming(DialerCall call) {Trace.beginSection("CallList.onIncoming");if (updateCallInMap(call)) {LogUtil.i("CallList.onIncoming", String.valueOf(call));}for (Listener listener : listeners) {listener.onIncomingCall(call);}Trace.endSection();}
packages/apps/Dialer/java/com/android/incallui/InCallPresenter.java
InCallPresenter 监听来电状态
可以看出 InCallPresenter 实现了 CallList.Listener 的接口,所以可以监听来电的状态
public class InCallPresenter implements CallList.Listener, AudioModeProvider.AudioModeListener {
跟踪到 onCallListChange 方法
@Overridepublic void onCallListChange(CallList callList) {Trace.beginSection("InCallPresenter.onCallListChange");if (inCallActivity != null && inCallActivity.isInCallScreenAnimating()) {awaitingCallListUpdate = true;Trace.endSection();return;}if (callList == null) {Trace.endSection();return;}awaitingCallListUpdate = false;InCallState newState = getPotentialStateFromCallList(callList);InCallState oldState = inCallState;LogUtil.d("InCallPresenter.onCallListChange","onCallListChange oldState= " + oldState + " newState=" + newState);// If the user placed a call and was asked to choose the account, but then pressed "Home", the// incall activity for that call will still exist (even if it's not visible). In the case of// an incoming call in that situation, just disconnect that "waiting for account" call and// dismiss the dialog. The same activity will be reused to handle the new incoming call. See// a bug for more details.DialerCall waitingForAccountCall;if (newState == InCallState.INCOMING&& (waitingForAccountCall = callList.getWaitingForAccountCall()) != null) {waitingForAccountCall.disconnect();// The InCallActivity might be destroyed or not started yet at this point.if (isActivityStarted()) {inCallActivity.dismissPendingDialogs();}}// 核心代码newState = startOrFinishUi(newState);LogUtil.d("InCallPresenter.onCallListChange", "onCallListChange newState changed to " + newState);// Set the new state before announcing it to the worldLogUtil.i("InCallPresenter.onCallListChange","Phone switching state: " + oldState + " -> " + newState);inCallState = newState;// Foreground call changedDialerCall primary = null;if (newState == InCallState.INCOMING) {primary = callList.getIncomingCall();} else if (newState == InCallState.PENDING_OUTGOING || newState == InCallState.OUTGOING) {primary = callList.getOutgoingCall();if (primary == null) {primary = callList.getPendingOutgoingCall();}} else if (newState == InCallState.INCALL) {primary = getCallToDisplay(callList, null, false);}if (primary != null) {onForegroundCallChanged(primary);}// notify listeners of new statefor (InCallStateListener listener : listeners) {LogUtil.d("InCallPresenter.onCallListChange","Notify " + listener + " of state " + inCallState.toString());listener.onStateChange(oldState, inCallState, callList);}if (isActivityStarted()) {final boolean hasCall =callList.getActiveOrBackgroundCall() != null || callList.getOutgoingCall() != null;inCallActivity.dismissKeyguard(hasCall);}Trace.endSection();}
继续跟踪 startOrFinishUi 方法
private InCallState startOrFinishUi(InCallState newState) {if ((showCallUi || showAccountPicker) && !shouldStartInBubbleMode()) {LogUtil.i("InCallPresenter.startOrFinishUi", "Start in call UI");
// 展示来电界面showInCall(false /* showDialpad */, !showAccountPicker /* newOutgoingCall */);} else if (newState == InCallState.INCOMING) {LogUtil.i("InCallPresenter.startOrFinishUi", "Start Full Screen in call UI");try {PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);PowerHALManager powerHALManager = new PowerHALManager(context, new Handler());PowerHALManager.PowerHintScene sceneIncall = powerHALManager.createPowerHintScene("InCall: InCallPresenter", PowerHintVendorSprd.POWER_HINT_VENDOR_RADIO_CALL, null);if (sceneIncall != null && !pm.isScreenOn()) {Log.i(this, "power Hint POWER_HINT_VENDOR_RADIO_CALL");sceneIncall.acquire(1000);}} catch (java.lang.NoClassDefFoundError e) {LogUtil.e("InCallPresenter.startOrFinishUi", "Exception:" + e.getMessage());} catch (java.util.NoSuchElementException e) {LogUtil.e("InCallPresenter.startOrFinishUi", "Exception:" + e.getMessage());} catch (Exception e) {LogUtil.e("InCallPresenter.startOrFinishUi", "Exception:" + e.getMessage());}} else if (newState == InCallState.NO_CALLS) {// The new state is the no calls state. Tear everything down.inCallState = newState;attemptFinishActivity();attemptCleanup();}
跟踪 showInCall 方法
public void showInCall(boolean showDialpad, boolean newOutgoingCall) {LogUtil.i("InCallPresenter.showInCall", "Showing InCallActivity");context.startActivity(InCallActivity.getIntent(context, showDialpad, newOutgoingCall, false /* forFullScreen */));}
至此,显示来电界面。
MO 是从Dialer 发起,MT是从Modem发起。所以两个流程最大的差别也就是如何开始创建Call & connection。当准备开始创建和创建完成后,MO & MT在流程上几乎是没有差异的,都是一些Call & connection的控制,然后通过UI显示出来。
总的来电各层之间的流程:
RIL→TelephonyFramework →TeleponyService→ TeleComService→
TeleComFramework→ TeleComService→TeleComFramework-->InCallUI
安卓10 来电流程梳理相关推荐
- 安卓10拨号流程梳理
电话应用框架 Android电话模块是一个典型的分层结构设计,如下: 电话框架分为4个层次,分别为:应用层.框架层(framework层,简称fw).RIL(Radio Interface Layer ...
- 安卓10来电流程详细总结
来电过程, 是由com.android.phone进程发起的,因为 com.android.phone 进程中 Telephony 直接与Moderm层交互, com.android.phone 进程 ...
- 安卓10拨号流程详细总结
电话应用框架 Android电话模块是一个典型的分层结构设计,如下: 电话框架分为4个层次,分别为:应用层.框架层(framework层,简称fw).RIL(Radio Interface Layer ...
- 安卓手机来电亮屏流程分析
来电亮屏流程分析 本文档是针对手机来电时候自主点亮屏幕这一流程的分析,很自然的就将其分为2个阶段,第一个是来电,第二个是点亮屏幕. 来电的流程: 来电消息是从RIL层接收到的,然后才开始传递上来. A ...
- android 刷系统,安卓10的刷机教程,教你刷好Killer的精简包
本帖最后由 大熊花 于 2020-6-13 22:03 编辑 安卓10真香-- 之前我还退回到安卓9的公测33,心想为了玩游戏流畅,结果发现还是安卓10更好一点. 下午把东西准备齐了刷了killer的 ...
- 开发安卓app游戏_「安卓APP开发流程」安卓APP如何开发的?
21世纪,智能手机走进了人们的生活,现在的智能手机的操作系统基本分为两种,一种是IOS系统(苹果系统).安卓系统,其中,安卓系统是开源的,所以很多品牌商会讲安卓包装成自己的系统,但核心还是一样的,都是 ...
- android10rom包,安卓10的刷机教程,教你刷好Killer的精简包
本帖最后由 大熊花 于 2020-6-13 22:03 编辑 安卓10真香-- 之前我还退回到安卓9的公测33,心想为了玩游戏流畅,结果发现还是安卓10更好一点. 下午把东西准备齐了刷了killer的 ...
- Android10支持volte,Nemo_LG V35 安卓10.0解锁Volte(联通、电信、移动)教程_Nemo社区_LinkNemo_关于分享和探索的好地方...
什么是volte? 早期的4g,只支持上网而不支持通话,这也就意味着当在通话的时候,网络会从4g会回落到3g或者2g上. 而后续随着技术的迭代,推出了volte,就是为了解决4g下通话的问题. LG ...
- 安卓app开发方案_「安卓APP开发流程」安卓APP如何开发的?
21世纪,智能手机走进了人们的生活,现在的智能手机的操作系统基本分为两种,一种是IOS系统(苹果系统).安卓系统,其中,安卓系统是开源的,所以很多品牌商会讲安卓包装成自己的系统,但核心还是一样的,都是 ...
最新文章
- tensorflow在训练和验证时监视不同的summary的操作
- 20190216 vagrant up 失败问题
- windows 禁用ipv6服务_在 Windows 7 中禁用IPv6协议/IPv6隧道
- phpstudy mysql5.1_linux下mysql5.1 和 5.7安装教程详解
- LNMP与CA认证的童话故事
- caffe学习路的起点
- 抽奖砍价之类以均值为基础的波动算法 demo版本
- 洛谷P3386 【模板】二分图匹配
- 【NOIP初赛】【Luogu1787】普及组2013(洛谷初赛题提交水AC方法了解一下)
- d3.js(v5.7)的node与数据匹配(自动匹配扩展函数)
- C++ 常量引用用法
- 【洛谷P3804】统计每个子串出现的次数和长度(后缀自动机模版+拓扑序计数)
- font-awesome在Vue项目中的使用(npm使用)
- 解析腾讯企业邮箱到自己域名,设置mail的cname
- 计算机多媒体化简笔画,计算器简笔画教程
- 微信小程序文件下载并保存
- python自动做表格_用Python做自动化的表格处理(批量智能替换) - Python趣用之法2...
- tcp连接失败触发的异常
- QT+ffmpeg+SDL2播放视频流
- 系统备份(ghost工具)
热门文章
- SQL leetcode刷题答案(一)
- CodeForces 1216CodeForces 1221 A~D
- 使用@PersistenceContext获取EntityManager报NullPointerException异常
- 2020程序员高质量网站集锦(时间有限,网站贵精不贵多,质量最重要)
- C# Int16,Int32和Int64的区别
- 使用python+Selenium动态爬取《率土之滨》藏宝阁账号信息
- 【附源码】计算机毕业设计JAVA东理咨询交流论坛
- Object.assign的原理及其实现方式
- IEC61850开发流程
- Adobe Illustrator CC 制作排版