前言
刚刚开始看InCallContrller内部逻辑是会有点复杂的所以我今天来做一个笔记。
作用 : InCallContrller的作用就是为了调用InCallUi页面,InCallContrller的作用就是控制InCallUi的启动与关闭操作。
关系图:
流程图:
第一步我们首先来看初始化过程吧:
第一步来电或者去电都是调用一个方法吊起InCall的方法bindToService()方法,看方法的名字就可以看出来,他的意思是绑定服务,服务就是InCallUi的服务一直等待调用
这来开InCallContrller逻辑,首先看bindToService()方法,这里会初始化里面的一些内部类,进行内部绑定
public void bindToServices(Call call) {
if (mInCallServiceConnection == null) {
InCallServiceConnection dialerInCall = null;
InCallServiceInfo defaultDialerComponentInfo = getDefaultDialerComponent();
Log.i(this, "defaultDialer: " + defaultDialerComponentInfo);
if (defaultDialerComponentInfo != null &&
!defaultDialerComponentInfo.getComponentName().equals(
mSystemInCallComponentName)) {
dialerInCall = new InCallServiceBindingConnection(defaultDialerComponentInfo);
}
Log.i(this, "defaultDialer: " + dialerInCall);
InCallServiceInfo systemInCallInfo = getInCallServiceComponent(
mSystemInCallComponentName, IN_CALL_SERVICE_TYPE_SYSTEM_UI);
EmergencyInCallServiceConnection systemInCall =
new EmergencyInCallServiceConnection(systemInCallInfo, dialerInCall);
systemInCall.setHasEmergency(mCallsManager.hasEmergencyCall());
InCallServiceConnection carModeInCall = null;
InCallServiceInfo carModeComponentInfo = getCarModeComponent();
if (carModeComponentInfo != null &&
!carModeComponentInfo.getComponentName().equals(mSystemInCallComponentName)) {
carModeInCall = new InCallServiceBindingConnection(carModeComponentInfo);//初始化连接
}
mInCallServiceConnection =
new CarSwappingInCallServiceConnection(systemInCall, carModeInCall);
}
mInCallServiceConnection.setCarMode(shouldUseCarModeUI());
// Actually try binding to the UI InCallService. If the response mInCallServiceConnection这个对象,他在两个独立的实例中切换UI
if (mInCallServiceConnection.connect(call) ==//mInCallServiceConnection.connect(call)开启调用InCallUi
InCallServiceConnection.CONNECTION_SUCCEEDED) {
// Only connect to the non-ui InCallServices if we actually connected to the main UI
// one.只有当我们实际连接到主UI InCallServices时,才连接到非UI InCallServices。
connectToNonUiInCallServices(call);
} else {
Log.i(this, "bindToServices: current UI doesn't support call; not binding.");
}
}
这里初始化一个内部类CarSwappingInCallServiceConnection,他的作用是互相切换InCallServiceBindingConnection和EmergencyInCallServiceConnection。CarSwappingInCallServiceConnection 切换两个子类直接的切换
EmergencInCallSerivceConnection他的作用是处理InCallServiceBindingConnection断开后通过紧急处理Emergenc就是紧急的意思。
CatSwappingInCallServiceConnection:
private class CarSwappingInCallServiceConnection extends InCallServiceConnection {
private final InCallServiceConnection mDialerConnection;
private final InCallServiceConnection mCarModeConnection;
private InCallServiceConnection mCurrentConnection;
private boolean mIsCarMode = false;
private boolean mIsConnected = false;
public CarSwappingInCallServiceConnection(
InCallServiceConnection dialerConnection,
InCallServiceConnection carModeConnection) {
mDialerConnection = dialerConnection;
mCarModeConnection = carModeConnection;
mCurrentConnection = getCurrentConnection();
}
public synchronized void setCarMode(boolean isCarMode) {
Log.i(this, "carmodechange: " + mIsCarMode + " => " + isCarMode);
if (isCarMode != mIsCarMode) {
mIsCarMode = isCarMode;
InCallServiceConnection newConnection = getCurrentConnection();
if (newConnection != mCurrentConnection) {
if (mIsConnected) {
mCurrentConnection.disconnect();
int result = newConnection.connect(null);
mIsConnected = result == CONNECTION_SUCCEEDED;
}
mCurrentConnection = newConnection;
}
}
}
@Override
public int connect(Call call) {
if (mIsConnected) {
Log.i(this, "already connected");
return CONNECTION_SUCCEEDED;
} else {//mCurrentConnection这个获取对象取决于mIsCarMode这个如果是false就是InCallServiceBindingConnection
int result = mCurrentConnection.connect(call); //InCallUi调用 调到EmergencyInCallServiceConnection内部类中
if (result != CONNECTION_FAILED) {
mIsConnected = result == CONNECTION_SUCCEEDED;
return result;
}
}
return CONNECTION_FAILED;
}
@Override
public void disconnect() {
if (mIsConnected) {
mCurrentConnection.disconnect();
mIsConnected = false;
} else {
Log.i(this, "already disconnected");
}
}
@Override
public boolean isConnected() {
return mIsConnected;
}
@Override
public void setHasEmergency(boolean hasEmergency) {
if (mDialerConnection != null) {
mDialerConnection.setHasEmergency(hasEmergency);
}
if (mCarModeConnection != null) {
mCarModeConnection.setHasEmergency(hasEmergency);
}
}
@Override
public InCallServiceInfo getInfo() {
return mCurrentConnection.getInfo();
}
@Override
public void dump(IndentingPrintWriter pw) {
pw.print("Car Swapping ICS [");
pw.append(mIsConnected ? "" : "not ").append("connected]\n");
pw.increaseIndent();
if (mDialerConnection != null) {
pw.print("Dialer: ");
mDialerConnection.dump(pw);
}
if (mCarModeConnection != null) {
pw.print("Car Mode: ");
mCarModeConnection.dump(pw);
}
}
private InCallServiceConnection getCurrentConnection() {
if (mIsCarMode && mCarModeConnection != null) {
return mCarModeConnection;
} else {
return mDialerConnection;
}
}
}
CatSwappingInCallServiceConnection这个内部类主要是要看构造方法里面的 mCurrentConnection = getCurrentConnection();这里的getCurrentConnection决定最切换那个的调用目的,mIsCarMode如果是fasle就回调用EmergencyInCallServiceConnection内部类,他的意思就是紧急调用InCallSerivceConnection的意思,在这个内部类中需要看connec方法他里面做了
接下来看一下EmergencyInCallServiceConnection内部处理
/**
* InCallServiceBindingConnection的一个版本,它将所有调用委托给一个辅助连接,直到它发现一个紧急调用,
* 或者另一个连接终止。当这两种情况之一发生时,该类实例将接管连接。
*/
private class EmergencyInCallServiceConnection extends InCallServiceBindingConnection {
private boolean mIsProxying = true;
private boolean mIsConnected = false;
private final InCallServiceConnection mSubConnection;
private Listener mSubListener = new Listener() {
@Override
public void onDisconnect(InCallServiceConnection subConnection) {
if (subConnection == mSubConnection) {
if (mIsConnected && mIsProxying) {
// At this point we know that we need to be connected to the InCallService
// and we are proxying to the sub connection. However, the sub-connection
// just died so we need to stop proxying and connect to the system in-call
// service instead.此时,我们知道需要连接到InCallService,并代理到子连接。然而,子连接刚刚结束,因此我们需要停止代理并转而连接到系统内调用服务。
mIsProxying = false;
connect(null);
}
}
}
};
public EmergencyInCallServiceConnection(
InCallServiceInfo info, InCallServiceConnection subConnection) {
super(info);
mSubConnection = subConnection;//传进来时一般是Null subConnection直接搭载的是 InCallServiceBindingConnection 就是创建他的时候满足条件直接调用父类内容
if (mSubConnection != null) {
mSubConnection.setListener(mSubListener);
}
mIsProxying = (mSubConnection != null);
}
@Override
public int connect(Call call) {
mIsConnected = true;
if (mIsProxying) { //这里经过我Debug 他进入只会开机第一次进入 如果是false表示代理结束,如果是true开始代理 开机拨号第一次会是true
int result = mSubConnection.connect(call);//InCallUi调用
mIsConnected = result == CONNECTION_SUCCEEDED;
if (result != CONNECTION_FAILED) {
return result;
}
// Could not connect to child, stop proxying.
mIsProxying = false;
}
mEmergencyCallHelper.maybeGrantTemporaryLocationPermission(call,
mCallsManager.getCurrentUserHandle());
if (call != null && call.isIncoming()
&& mEmergencyCallHelper.getLastEmergencyCallTimeMillis() > 0) {
// Add the last emergency call time to the call
Bundle extras = new Bundle();
extras.putLong(android.telecom.Call.EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS,
mEmergencyCallHelper.getLastEmergencyCallTimeMillis());
call.putExtras(Call.SOURCE_CONNECTION_SERVICE, extras);
}
// 如果条件满足,连接父类中的connect函数
return super.connect(call);
}
@Override
public void disconnect() {
Log.i(this, "Disconnect forced!");
if (mIsProxying) {
mSubConnection.disconnect();
} else {
super.disconnect();
mEmergencyCallHelper.maybeRevokeTemporaryLocationPermission();
}
mIsConnected = false;
}
@Override
public void setHasEmergency(boolean hasEmergency) {
if (hasEmergency) {
takeControl();
}
}
@Override
public InCallServiceInfo getInfo() {
if (mIsProxying) {
return mSubConnection.getInfo();
} else {
return super.getInfo();
}
}
@Override
protected void onDisconnected() {
// Save this here because super.onDisconnected() could force us to explicitly
// disconnect() as a cleanup step and that sets mIsConnected to false.
boolean shouldReconnect = mIsConnected;
super.onDisconnected();
// We just disconnected. Check if we are expected to be connected, and reconnect.
if (shouldReconnect && !mIsProxying) {
connect(null); // reconnect
}
}
@Override
public void dump(IndentingPrintWriter pw) {
pw.print("Emergency ICS Connection [");
pw.append(mIsProxying ? "" : "not ").append("proxying, ");
pw.append(mIsConnected ? "" : "not ").append("connected]\n");
pw.increaseIndent();
pw.print("Emergency: ");
super.dump(pw);
if (mSubConnection != null) {
pw.print("Default-Dialer: ");
mSubConnection.dump(pw);
}
pw.decreaseIndent();
}
/**
* Forces the connection to take control from it's subConnection.
*/
private void takeControl() {
if (mIsProxying) {
mIsProxying = false;
if (mIsConnected) {
mSubConnection.disconnect();
super.connect(null);
}
}
}
}
在这里会做一个是否需要代理,如果需要处理代理逻辑,这里的代理获取的内部对象也是有判断的,建议去看内部逻辑这里就不多讲解了。
接着调用父类的的connec()方法,先看代码。
InCallServiceBindingConnection
private class InCallServiceBindingConnection extends InCallServiceConnection {
private final ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.startSession("ICSBC.oSC");
synchronized (mLock) {
try {
Log.d(this, "onServiceConnected: %s %b %b", name, mIsBound, mIsConnected);
mIsBound = true;
if (mIsConnected) {//是否连接
// Only proceed if we are supposed to be connected.只有当我们被认为是有联系的时候才继续。 成功后调用InCallServer
onConnected(service);
}
} finally {
Log.endSession();
}
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.startSession("ICSBC.oSD");
synchronized (mLock) {
try {
Log.d(this, "onDisconnected: %s", name);
mIsBound = false;
onDisconnected();
} finally {
Log.endSession();
}
}
}
};
private final InCallServiceInfo mInCallServiceInfo;
private boolean mIsConnected = false;
private boolean mIsBound = false;
public InCallServiceBindingConnection(InCallServiceInfo info) {
mInCallServiceInfo = info;
}
@Override
public int connect(Call call) {
if (mIsConnected) {
Log.addEvent(call, LogUtils.Events.INFO, "Already connected, ignoring request.");
return CONNECTION_SUCCEEDED;
}
if (call != null && call.isSelfManaged() &&
!mInCallServiceInfo.isSelfManagedCallsSupported()) {
Log.i(this, "Skipping binding to %s - doesn't support self-mgd calls",
mInCallServiceInfo);
mIsConnected = false;
return CONNECTION_NOT_SUPPORTED;
}
//开启InCallService
Intent intent = new Intent(InCallService.SERVICE_INTERFACE);
intent.setComponent(mInCallServiceInfo.getComponentName());
if (call != null && !call.isIncoming() && !call.isExternalCall()){
intent.putExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS,
call.getIntentExtras());
intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
call.getTargetPhoneAccount());
}
Log.i(this, "Attempting to bind to InCall %s, with %s", mInCallServiceInfo, intent);
mIsConnected = true;
if (!mContext.bindServiceAsUser(intent, mServiceConnection, //开启服务 调用InCallServiceImpl
Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE |
Context.BIND_ABOVE_CLIENT,
UserHandle.CURRENT)) {
Log.w(this, "Failed to connect.");
mIsConnected = false;
}
if (call != null && mIsConnected) {
call.getAnalytics().addInCallService(
mInCallServiceInfo.getComponentName().flattenToShortString(),
mInCallServiceInfo.getType());
}
return mIsConnected ? CONNECTION_SUCCEEDED : CONNECTION_FAILED;
}
@Override
public InCallServiceInfo getInfo() {
return mInCallServiceInfo;
}
@Override
public void disconnect() {
if (mIsConnected) {
mContext.unbindService(mServiceConnection);
mIsConnected = false;
} else {
Log.addEvent(null, LogUtils.Events.INFO, "Already disconnected, ignoring request.");
}
}
@Override
public boolean isConnected() {
return mIsConnected;
}
@Override
public void dump(IndentingPrintWriter pw) {
pw.append("BindingConnection [");
pw.append(mIsConnected ? "" : "not ").append("connected, ");
pw.append(mIsBound ? "" : "not ").append("bound]\n");
}
protected void onConnected(IBinder service) {
boolean shouldRemainConnected =
InCallController.this.onConnected(mInCallServiceInfo, service);//往下执行
if (!shouldRemainConnected) {
// Sometimes we can opt to disconnect for certain reasons, like if the
// InCallService rejected our initialization step, or the calls went away
// in the time it took us to bind to the InCallService. In such cases, we go
// ahead and disconnect ourselves.
disconnect();
}
}
protected void onDisconnected() {
InCallController.this.onDisconnected(mInCallServiceInfo);
disconnect(); // Unbind explicitly if we get disconnected.
if (mListener != null) {
mListener.onDisconnect(InCallServiceBindingConnection.this);
}
}
}
这里内部处理了启动InCallUi页面逻辑