3.2.1 Telecomm简述
Android5.0在Telephony的变化又比较大,增加了一个Telecomm模块,它位于界面应用如InCallUI和Phone框架之间,其具体的设计意图尚不明确,从代码分析上来看,流程比原来的架构复杂很多,可能是想把Phone进程独立得更开一些,类似于RIL进程,给应用提供一个扁平的Phone接口,不希望像以前一样,呼叫流程在Phone进程和应用进程的纵深过大,引起函数的耦合性太大,有些相同的函数会在不同的流程执行在不同的进程里面。
其改变前后的关系可用下面的图来简述:
之前函数的调用流程在不同进程间的接口不够平滑,现在添加新的Telecomm层后,整个软件框架就比较有序和易于管理。
对于telephony相关的部分,从5.0和4.4的代码比较来看,
1)在framework下面,5.0新增了telecomm和ims部分的代码,截图如下,
结合后面的代码分析,我们知道在framework部分的telecomm代码的作用是承上启下的关系,它通过aidl接口,一方面和Phone进行交互,这一层是以InCallService、ConnectionService为代表,它的相关部分会运行在Phone进程;另一方面,和TelecommApp进程下的服务如TelecomService通信,这一层以TelecomManager等为代表,但他们往往运行在应用进程里面。
2)在Package目录下,5.0的应用部分InCallUI下没有了manifest文件,Service目录则多了MMS、Telecomm目录,MMS部分为短彩信增加了新的服务流程,telecomm部分则添加了一些关键文件,如作为进程载体的TelecomApp.java文件,作为服务载体的TelecomServiceImpl.java,还有CallsManager、CallActivity等文件,这些文件的关系和作用将在后面逐步分解。。
3.2.2 Phone
新增加的Phone.java在 (frameworks\base\telecomm\java\android\telecom)目录下,其功能是“A unified virtual device providing a means of voice (and other) communication on a device.”,即作为一个虚拟设备提供通信服务。其类型如下,
public final class Phone {
它的方法的实现主要依赖3个辅助类,InCallAdapter、Listener、Call,即呼叫适配器、监听器、控制器(?),后续展开分析。
对于Phone的初始化,我们可以通过其方法的调用关系找到,例如查找internalAddCall,我们就会发现调用者为InCallService,其中mPhone即为Phone的实例,
case MSG_ADD_CALL:
mPhone.internalAddCall((ParcelableCall) msg.obj);
在InCallService里,当收到MSG_SET_IN_CALL_ADAPTER消息时,会创建一个Phone实例,同时也顺带创建了一个InCallAdapter实例,
case MSG_SET_IN_CALL_ADAPTER:
mPhone = new Phone(new InCallAdapter((IInCallAdapter) msg.obj));
onPhoneCreated(mPhone);
break;
MSG_SET_IN_CALL_ADAPTER消息是setInCallAdapter发出的,它是在InCallService里实现的AIDL接口类的服务端方法,
private final class InCallServiceBinder extends IInCallService.Stub {
@Override
public void setInCallAdapter(IInCallAdapter inCallAdapter) {
mHandler.obtainMessage(MSG_SET_IN_CALL_ADAPTER, inCallAdapter).sendToTarget();
}
根据我们对service的了解,它在service类创建时创建binder,在service的onbind()方法里将binder实例传入,执行客户端的ServiceConnection的onServiceConnected方法,所以在客户端,onConnected被执行,之后就是客户端InCallController里的setInCallAdapter被调用,再间接调用前面提到的服务端的setInCallAdapter。
private class InCallServiceConnection implements ServiceConnection {
/** {@inheritDoc} */
@Override public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(this, "onServiceConnected: %s", name);
onConnected(name, service);
}
private void onConnected(ComponentName componentName, IBinder service) {
ThreadUtil.checkOnMainThread();
Log.i(this, "onConnected to %s", componentName);
IInCallService inCallService = IInCallService.Stub.asInterface(service);
try {
inCallService.setInCallAdapter(new InCallAdapter(CallsManager.getInstance(),
mCallIdMapper));
mInCallServices.put(componentName, inCallService);
} catch (RemoteException e) {
Log.e(this, e, "Failed to set the in-call adapter.");
return;
}
所以可以看出,Phone的实例就是在InCallService服务启动过程中创建的。
3.2.3 InCallController
前面提到的binder的客户端InCallController位于(packages\services\telecomm\src \com\android\server\telecom)目录,根据manifest文件,它运行在TelecomApp这个应用里,这是一个的5.0新进程。又根据android:persistent="true"这个属性我们知道,TelecomApp是开机自启动的。
InCallController是在CallsManager的构造函数里创建的,CallsManager又是在TelecomApp的onCreate方法里面创建的,
public void onCreate() {
super.onCreate();
if (UserHandle.myUserId() == UserHandle.USER_OWNER) {
// Note: This style of initialization mimics what will be performed once Telecom is
// moved
// to run in the system service. The emphasis is on ensuring that initialization of all
// telecom classes happens in one place without relying on Singleton initialization.
mMissedCallNotifier = new MissedCallNotifier(this);
mPhoneAccountRegistrar = new PhoneAccountRegistrar(this);
mCallsManager = new CallsManager(this, mMissedCallNotifier, mPhoneAccountRegistrar);
CallsManager.initialize(mCallsManager);
mTelecomService = new TelecomServiceImpl(mMissedCallNotifier, mPhoneAccountRegistrar,
mCallsManager, this);
ServiceManager.addService(Context.TELECOM_SERVICE, mTelecomService);
// Start the BluetoothPhoneService
BluetoothPhoneService.start(this);
}
}
所以,TelecomApp进程的启动过程中,创建了InCallController和CallsManager两个实例,这两个类实例相互关联。
3.2.4 TelecomService
实际上,并不存在TelecomService这个名字的文件或类名,但存在一个这样的服务。
还是在TelecomApp的onCreate方法里(如上),创建了一个TelecomServiceImpl类实例,它对应于TelecomService服务的AIDL服务端,它就是以类实例为服务端,并不像service里面bind的binder接口,并且,它被作为一个服务添加到serviceManager里面。
mTelecomService = new TelecomServiceImpl(mMissedCallNotifier, mPhoneAccountRegistrar,
mCallsManager, this);
ServiceManager.addService(Context.TELECOM_SERVICE, mTelecomService);
…
public class TelecomServiceImpl extends ITelecomService.Stub {
TelecomServiceImpl依赖CallsManager、PhoneAccountRegistrar、MissedCallNotifier这几个类完成相应的功能。
TelecomService的客户端是TelecomManager,它通过getTelecomService获取到服务端接口,然后通过这个接口使用服务端的远程接口,
private ITelecomService getTelecomService() {
return ITelecomService.Stub.asInterface(ServiceManager.getService(Context.TELECOM_SERVICE));
}
3.2.5 TelecomManager
TelecomManager在framework的android.telecom包里面,它在ContextImpl被创建,并加入到注册列表里,属于系统级的服务,
registerService(TELECOM_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
return new TelecomManager(ctx.getOuterContext());
}});
…
应用要获取其实例,可以通过其from方法,也可以直接通过context.getSystemService(Context.TELECOM_SERVICE)这个语句来获取。
TelecomManager的功能则主要是对TelecomService提供的远程接口的封装,然后提供给应用使用,例如对于showInCallScreen方法,在TelecomServiceImpl提供服务端的远程方法,TelecomManager提供客户端访问接口,DialpadFragment等应用组件使用这个接口,
TelecomServiceImpl.java (packages\services\telecomm\src\com\android\server\telecom): public void showInCallScreen(boolean showDialpad)
TelecomManager.java (frameworks\base\telecomm\java\android\telecom): public void showInCallScreen(boolean showDialpad)
DialpadFragment.java (packages\apps\dialer\src\com\android\dialer\dialpad): getTelecomManager().showInCallScreen(showDialpad);
DialtactsActivity.java (packages\apps\dialer\src\com\android\dialer): getTelecomManager().showInCallScreen(false);
虽然TelecomServiceImpl是在packages目录下,TelecomManager在frameworks目录下,但前者作为一个服务,在单独进程里为后者提供服务,后者则为应用进程提供接口服务,再通过binder进程通信访问前者的服务,其关系如下,
3.2.6 InCallService
InCallService是一个抽象类,继承于service,它由四大部分组成,Handler、InCallServiceBinder、VideoCall以及自身的一些方法,其中InCallServiceBinder是aidl接口的服务端实现,对应于前面提到的InCallController,它主要是给当前服务发送消息,Handler接收并处理这些消息,如前面提到,Handler会创建一个Telecom的Phone实例,然后使用Phone的接口处理应用请求,所以实际上,客户端的请求实际上是Phone来完成的,至于Phone是如何实现功能的,请参见Phone的分析,这里的几个关键类的相互关系如下,
InCallService的主要功能是给应用提供管理Phone call的途径,它的子类InCallServiceImpl完成真正服务实例的创建,当存在呼叫连接时,它bind到Telecomm,并接受呼叫状态的更新。
服务实例的绑定过程是在InCallController里面完成的,
private void bind() {…
Intent serviceIntent = new Intent(InCallService.SERVICE_INTERFACE);
for (ResolveInfo entry : packageManager.queryIntentServices(serviceIntent, 0)) {
InCallServiceConnection inCallServiceConnection = new InCallServiceConnection();
ComponentName componentName = new ComponentName(serviceInfo.packageName,
serviceInfo.name);
Intent intent = new Intent(InCallService.SERVICE_INTERFACE);
intent.setComponent(componentName);
if (mContext.bindServiceAsUser(intent, inCallServiceConnection,
Context.BIND_AUTO_CREATE, UserHandle.CURRENT)) {
mServiceConnections.put(componentName, inCallServiceConnection);
}
…}
这个bind()是在onCallAdded()里被调用的,onCallAdded被调用的地方有两个:
CallsManager.java (packages\services\telecomm\src\com\android\server\telecom): listener.onCallAdded(call);
Phone.java (frameworks\base\telecomm\java\android\telecom): listener.onCallAdded(this, call);
他们都是通过listener的方式被调用的,通过分析两个类的listener,发现Phone的监听器主要在应用文件中注册,
CallList.java (packages\apps\incallui\src\com\android\incallui): mPhone.addListener(mPhoneListener);
InCallPresenter.java (packages\apps\incallui\src\com\android\incallui): mPhone.addListener(mPhoneListener);
CallsManager的监听器在其构造函数中注册,并且监听器类型为CallsManagerListener,
private final Set<CallsManagerListener> mListeners = Collections.newSetFromMap( new ConcurrentHashMap<CallsManagerListener, Boolean>(16, 0.9f, 1));
CallsManager(Context context, MissedCallNotifier missedCallNotifier,
PhoneAccountRegistrar phoneAccountRegistrar) {…
mCallLogManager = new CallLogManager(context);
mInCallController = new InCallController(context);
mListeners.add(statusBarNotifier);
mListeners.add(mCallLogManager);
mListeners.add(mPhoneStateBroadcaster);
mListeners.add(mInCallController);
mListeners.add(mRinger);
…}
所以是CallsManager. addCall调用了InCallController的onCallAdded。addCall则会被来电、去电、会议电话等接口方法调用,如startOutgoingCall。
所以当有通话连接要产生时,会启动InCallService服务,其大致过程如下: