四.InCallController.java绑定InCallService.java实现进程间Binder通信
1).上面我们分析了每路通话的管理,介绍了每个层次之间的关系,但是在这个过程中我们看到,最终是通过调用InCallController.java类的onCallAdded方法去通知InCallService进行每路通话的保存处理,InCallController是在packages层的类,而InCallService是存在于framework的,这两个不同层次的类是如何联系的,我们接下来详细分析这一过程。
2). 我们先回顾前面的流程,CallsManager.java(packages/services/Telecomm)中的startOutgoingCall()方法调用:
addCall(call);
继而进入到InCallController.java中时调用onCallAdded():
bind(call);
这个是怎么做到的,inCallService实例是怎么得到的?这其中到底用到了什么思想设计模式?下面我们一一解析。
3).上面说了,当流程进入到InCallController.java时,回去调用onCallAdded()方法,这个里面回去调用inCallService方法,这个过程是怎样的,是不是直接去实例化inCallService呢,我想没有那么简单,第一inCallService是一个service实例,这两者之间不仅不在一个层次中(一个是services层,一个是framework层)而且还存在进程间通信问题,如何解决这些问题,我想大家应该都会想到进程间通信想到Binder技术,再想到aidl技术,要验证我们的想法,我们可以进入代码查看,首先我们看入口方法也就是onCallAdded():
if(mInCallServices.isEmpty()) {
bind(call);
}
第一次进来并没有直接去调用 inCallService的方法,而是调用了自身的bind方法:
if(mInCallServices.isEmpty()) {
PackageManager packageManager =mContext.getPackageManager();
Intent serviceIntent = new Intent(InCallService.SERVICE_INTERFACE);
for (ResolveInfo entry :packageManager.queryIntentServices(serviceIntent, 0)) {
ServiceInfo serviceInfo =entry.serviceInfo;
if (serviceInfo != null) {
booleanhasServiceBindPermission = serviceInfo.permission != null &&
serviceInfo.permission.equals(
Manifest.permission.BIND_INCALL_SERVICE);
booleanhasControlInCallPermission = packageManager.checkPermission(
Manifest.permission.CONTROL_INCALL_EXPERIENCE,
serviceInfo.packageName) == PackageManager.PERMISSION_GRANTED;
if(!hasServiceBindPermission) {
Log.w(this, "InCallService does not have BIND_INCALL_SERVICE permission:" +
serviceInfo.packageName);
continue;
}
if (!hasControlInCallPermission){
Log.w(this,
"InCall UI does not have CONTROL_INCALL_EXPERIENCEpermission: " +
serviceInfo.packageName);
continue;
}
InCallServiceConnectioninCallServiceConnection = new InCallServiceConnection();
ComponentNamecomponentName = new ComponentName(serviceInfo.packageName,serviceInfo.name);
Log.i(this,"Attempting to bind to InCall %s, is dupe? %b ",
serviceInfo.packageName,
mServiceConnections.containsKey(componentName));
if(!mServiceConnections.containsKey(componentName)) {
Intentintent = new Intent(InCallService.SERVICE_INTERFACE);
intent.setComponent(componentName);
final intbindFlags;
if(mInCallComponentName.equals(componentName)) {
bindFlags = Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT;
if (!call.isIncoming()) {
intent.putExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS,
call.getExtras());
intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
call.getTargetPhoneAccount());
}
} else {
bindFlags = Context.BIND_AUTO_CREATE;
}
if(mContext.bindServiceAsUser(intent, inCallServiceConnection, bindFlags,
UserHandle.CURRENT)) {
mServiceConnections.put(componentName, inCallServiceConnection);
}
}
}
}
}
上述方法看似很复杂,不过我们可以一一分解,首先:
IntentserviceIntent = new Intent(InCallService.SERVICE_INTERFACE);
for (ResolveInfoentry : packageManager.queryIntentServices(serviceIntent, 0)) {
这两行代码首先定义了一个intent,查看InCallService.SERVICE_INTERFACE类型,我们搜索到其值是android.telecom.InCallService,然后调用 PackageManager的queryIntentServices方法,这个方法有什么作用呢,看官方的解释:
通过PackageManager的queryIntentActivities方法,查询系统中所有满足ACTION_MAIN和CATEGORY_LAUNCHER的应用程序,获取他们的程序名、包名、入口类名。
好了,这下我们知道了,这个方法主要是查询android.telecom.InCallService对应的类我们去搜,会发现最后在packages/apps/Dialer中的AndroidManifest.xml中有如下几行:
serviceandroid:name="com.android.incallui.InCallServiceImpl"
android:permission="android.permission.BIND_INCALL_SERVICE">
<intent-filter>
<actionandroid:name="android.telecom.InCallService"/>
</intent-filter>
</service>
一目了然, android.telecom.InCallService对应的是类 com.android.incallui.InCallServiceImpl,因为Dialer包含了InCallUI模块,所以在这里定义并没有什么不妥,我们继续看bind方法下面的逻辑:
InCallServiceConnectioninCallServiceConnection = new InCallServiceConnection();
ComponentNamecomponentName = new ComponentName(serviceInfo.packageName,serviceInfo.name);
首先定义了一个 InCallServiceConnection对象,然后实例化了一个 componentName,上面说了需要找到的是InCallServiceImpl类,同时我们打log可以看到:
serviceInfo.packageName=com.android.dialer
serviceInfo.name=com.android.incallui.InCallServiceImpl
果然如此,serviceInfo.name的值是InCallServiceImpl,继续略过往下看:
if(mContext.bindServiceAsUser(intent, inCallServiceConnection, bindFlags,
UserHandle.CURRENT)) {
mServiceConnections.put(componentName, inCallServiceConnection);
}
这下就更清楚了,调用到了 bindServiceAsUser方法,明显使用到了进程间通信binder技术,接下来我们定位到packages/apps/InCallUI中的InCallServiceImpl.java类:
public classInCallServiceImpl extends InCallService
这就更加合乎情理了, InCallServiceImpl是 InCallService的子类,他们之间是有联系的,同时我们找到了onBind方法,更加验证了我们的想法:
InCallPresenter.getInstance().setUp(
getApplicationContext(),
CallList.getInstance(),
AudioModeProvider.getInstance());
InCallPresenter.getInstance().onServiceBind();
InCallPresenter.getInstance().maybeStartRevealAnimation(intent)
return super.onBind(intent);
其中做了 InCallPresenter的bind操作,关键的一行:
super.onBind(intent);
调用了父类InCallService的 onBind方法:
return newInCallServiceBinder();
返回了 InCallServiceBinder类的实例,查看此类是一个内部类,先看定义:
private finalclass InCallServiceBinder extends IInCallService.Stub
继承了IInCallService.Stub,可以预见IInCallService是一个aidl文件,现在可以确定使用到了aidl技术,
这里重写了addCall方法,这个方法等会会被用到,我们现在回到InCallController.java中的bind方法继续看:
if(mContext.bindServiceAsUser(intent, inCallServiceConnection, bindFlags,
UserHandle.CURRENT)) {
mServiceConnections.put(componentName, inCallServiceConnection);
}
if条件中:
mContext.bindServiceAsUser(intent,inCallServiceConnection, bindFlags,UserHandle.CURRENT)
刚才我们看了第一个参数intent是InCallServiceImpl.java,看第二个参数 inCallServiceConnection,我们可以定位到内部类InCallServiceConnection:
private classInCallServiceConnection implements ServiceConnection {
/** {@inheritDoc} */
@Override public void onServiceConnected(ComponentName name,IBinder service) {
Log.d(this, "onServiceConnected: %s",name);
onConnected(name, service);
}
/** {@inheritDoc} */
@Override public void onServiceDisconnected(ComponentName name) {
Log.d(this, "onDisconnected: %s", name);
onDisconnected(name);}}
这里就更加明显了,一个 ServiceConnection的子类,明显是要建立binder连接实现进程间通信,看 onServiceConnected方法,这个方法会在调用上述bindServiceAsUser()方法时去InCallService.java中返回一个 InCallServiceBinder实例,在此处的onServiceConnected第二个参数中传进来,这里会有人说为什么InCallServiceBinder是IInCallService.Stub类型的,而这里的形参是 IBinder类型的,binder机制可以告诉你,IInCallService.Stub内部实现其实就是继承了IBinder了的,所以这里并没有什么错,我们继续看 onConnected():
IInCallServiceinCallService = IInCallService.Stub.asInterface(service);
try {
inCallService.setInCallAdapter(newInCallAdapter(CallsManager.getInstance(),
mCallIdMapper));
mInCallServices.put(componentName, inCallService);
} catch (RemoteException e) {
Log.e(this, e, "Failed to set the in-calladapter.");
Trace.endSection();
return;
}
// Upon successful connection, send the state of the world to theservice.
Collection<Call> calls =CallsManager.getInstance().getCalls();
if (!calls.isEmpty()) {
Log.i(this, "Adding %s calls to InCallServiceafter onConnected: %s", calls.size(),
componentName);
for (Call call : calls) {
try {
// Track the call if wedon't already know about it.
Log.i(this, "addCallafter binding: %s", call);
addCall(call);
inCallService.addCall(toParcelableCall(call,
componentName.equals(mInCallComponentName) /* includeVideoProvider */));
} catch (RemoteException ignored) {
}
}
onAudioStateChanged(null,CallsManager.getInstance().getAudioState());
onCanAddCallChanged(CallsManager.getInstance().canAddCall());
} else {
unbind();
}
Trace.endSection();
我们逐行分析:
IInCallServiceinCallService = IInCallService.Stub.asInterface(service);
这里首先将 service还原出来了,并且赋值给inCallService,然后:
inCallService.setInCallAdapter(newInCallAdapter(CallsManager.getInstance(),
mCallIdMapper));
mInCallServices.put(componentName,inCallService);
在这里调用了 setInCallAdapter保存了一个InCallAdapter实例,会调用到inCallService 中InCallServiceBinder的setInCallAdapter方法去调用onPhoneCreated方法,然后将此service作为键值对保存到了mInCallServices中,继续往下看onConnected方法:
inCallService.addCall(toParcelableCall(call,
componentName.equals(mInCallComponentName) /* includeVideoProvider */));
在这里最终调用到了返回的service也就是InCallServiceBinder中的addCall方法,实现了进程间方法调用,使逻辑从service层流转到了framework层中,InCallService中做了很多中间的通话逻辑处理,继而就调用到了Phone中的各种方法。
4).以上就是通话拨号的中间处理流转过程,主要是应用到了binder技术以及aidl技术,总而言之是进程间通信的一个应用场景,在通话过程中使用到了,这样会帮助我们理解整个通话过程的框架结构,继而更加清晰的了解整套代码的构建原理与好处所在,5.1的通话流程在原来基础上做了很大的改变,与4.4截然不同,所以我们想要更好的了解通话这一块,就需要深入查看源码。之前只是介绍了通话连接在哪里建立,并没有解释通话连接到底是什么,是如何建立的,在通话过程中扮演什么角色,下面我们会详细分析通话连接的建立过程。