前言
与通话相关的绝大多数操作都与CallsManager类相关,因此在对Telecom中的各种功能和机制分析之前,有必要先对CallsManager进行一定的了解。
本文内容基于Android M版本。
CallsManager位置:alps\packages\services\Telecomm\src\com\android\server\telecom\CallsManager.java
Telecom中的Call
在分析CallsManager之前,先来看一下Telecom中的Call类。
官方对Call的说明:包含了一路通话的生命周期中所涉及到的所有信息,通话是在Telecom接收到Call的Intent时开始(即通话连接之时)。
什么是Call
总而言之,可以简单的将Call看做在Telecom中对一路通话的所有描述,无论是去电或是来电都会创建出一个新的Call,该Call与framework中的Connection一一对应,两者通过IConnectionService接口传递数据。以挂断电话流程为例:
IConnectionService接口:AIDL接口,实现类为ConnectionService,包装类为ConnectionServiceWrapper,其他类调用接口中的方法时应通过ConnectionServiceWrapper进行调用,而不应直接使用ConnectionService。
Connection:在framework中表示一路语音通话或视频通话的类,与modem中每一路通话连接一一对应,承载了。Connection为抽象类,在不同的网络下有不同的具体实现类,例如GSMConnection(GSM网络)、CDMAConnection(CDMA网络)。
Call与CallsManager的关联
Call中有一个监听器接口Listener,该接口负责在Call状态改变时进行回调。以来电时挂断电话流程为例:
而CallsManager就是该接口的一个实现类,Call在状态改变时(无论是主动或者被动)就会回调Call.Listener接口的对应方法,而CallsManager就会在对应方法中进行相应处理,这是非常经典的观察者模式。实际上不仅Call使用了观察者模式来监听自身状态的改变,CallsManager也同样使用了该模式以对其他具体的功能类进行管理,下面进行具体的阐述。
CallsManager - 观察者模式
Call与CallsManager均使用了观察者模式以处理状态的变化:
Call.Listener
在Call中,Call为被观察者,Call.Listener为观察者,观察者Call.Listener实现类为CallsManager和InCallController
CallsManager.CallsManagerListener
在CallsManager中,CallsManager为被观察者,CallsManager.CallsManagerListener为观察者,观察者CallsManager.CallsManagerListener实现类为其他通话相关功能类(如Ringer)。
InCallController:提供服务以更新InCallUI(通话界面)的界面和数据。
Ringer:控制通话相关的铃声、振动等,比如来电铃声。
下面以拨号接通时的部分流程为例来说明其中的关系:
CallsManager在onSuccessfulOutgoingCall中的部分代码:
@Override
public void onSuccessfulOutgoingCall(Call call, int callState) {
……
//调用所有观察者的对应回调方法
for (CallsManagerListener listener : mListeners) {
listener.onConnectionServiceChanged(call, null, call.getConnectionService());
}
……
}
CallsManager工作机制简图
上图大致包含了CallsManager、Call、CallsManagerListener的工作机制:
Telecom启动之时就创建了CallsManager,而CallsManager在构造器内就将通话相关功能类注册到了监听列表中。在发生通话时(MT或MO),CallsManager首先创建出Call并回调所有观察者的对应方法,在Call状态更新时,会回调CallsManager相关方法,然后CallsManager会对Call进行相关处理,并会回调所有观察者的对应方法。
CallsManager与Telecom
Telecom中的类和代码量都相当多,因此对 Telecom 的分析需要以点带面,而CallsManager就是这个点,通过以上对CallsManager的简单分析,我们大概了解了CallsManager的工作机制,而Telecom的主要功能基本也是围绕着CallsManager所展开的,因此CallsManager可以算是Telecom中的核心中的核心,只要围绕着CallsManager进行分析和扩展,就可以比较容易的理清Telecom的整体架构和工作机制。而Telecom中比较复杂的功能开发,最好也以CallsManager作为切入点。
关于InCallService
InCallService是UI和telecom的桥梁。通过InCallService来完成各种call的更新与操作。
启动
Call 和CallsManager 类都在 packages/services/Telecomm/src/com/android/server/telecom/
CallsManager在生成新的Call实例后,会通过回调InCallController的onCallAdded函数,
这个时候如果InCallService没有被启动,InCallController会去启动InCallService,如果已经绑定了,那么直接调用incallService的addCall函数。即,所有CallsManager的回调,最后都调到InCallService的相关函数来处理。
在拨号流程中,会走到 CallsManager 的 startOutgoingCall 的方法
CompletableFuture<Call> startOutgoingCall(Uri handle,
PhoneAccountHandle requestedAccountHandle,
Bundle extras, UserHandle initiatingUser, Intent originalIntent,
String callingPackage) {
boolean isReusedCall;
Call call = reuseOutgoingCall(handle);
/-----/
// Create a call with original handle. The handle may be changed when the call is attached
// to a connection service, but in most cases will remain the same.
if (call == null) {
call = new Call(getNextCallId(), mContext,
this,
mLock,
mConnectionServiceRepository,
mPhoneNumberUtilsAdapter,
handle,
null /* gatewayInfo */,
null /* connectionManagerPhoneAccount */,
null /* requestedAccountHandle */,
Call.CALL_DIRECTION_OUTGOING /* callDirection */,
false /* forceAttachToExistingConnection */,
false, /* isConference */
mClockProxy);
call.initAnalytics(callingPackage);
//-----
if (isPotentialMMICode(handle) && !isSelfManaged) {
// Do not add the call if it is a potential MMI code.
callToUse.addListener(this);
} else if (!mCalls.contains(callToUse)) {
// We check if mCalls already contains the call because we could
// potentially be reusing
// a call which was previously added (See {@link #reuseOutgoingCall}).
addCall(callToUse);
}
其中 addCall 方法:addCall 添加call, 然后callsManager通知监听者,添加了一个call
CallsManager对象将保存多个Call 对象到mCalls 集合中, Call 对象则设置Listener 对象为CallsManager , 对象之间相互引用。而CallsManager 对象通过 mlisteners 发出onCallAdded 消息回调。那么mlisteners 究竟是什么呢?摘录出CallsManager类的属性定义和构造方法中的关键逻辑,
CallsManager 的构造函数
CallsManager(
Context context,
TelecomSystem.SyncRoot lock,
ContactsAsyncHelper contactsAsyncHelper,
CallerInfoAsyncQueryFactory callerInfoAsyncQueryFactory,
MissedCallNotifier missedCallNotifier,
PhoneAccountRegistrar phoneAccountRegistrar,
HeadsetMediaButtonFactory headsetMediaButtonFactory,
ProximitySensorManagerFactory proximitySensorManagerFactory,
InCallWakeLockControllerFactory inCallWakeLockControllerFactory) {
// ....
// 在CallsManager构造的时候, 会给CallsManager添加一系列监听者, 当CallsManager变化时,会通知他们
mListeners.add(statusBarNotifier);
mListeners.add(mCallLogManager);
mListeners.add(mPhoneStateBroadcaster);
// 重点关注 InCallController, log中会打印
mListeners.add(mInCallController);
mListeners.add(mRinger);
mListeners.add(new RingbackPlayer(this, playerFactory));
mListeners.add(new InCallToneMonitor(playerFactory, this));
// 监听手机是否是蓝牙、有限耳机、听筒、扬声器模式
mListeners.add(mCallAudioManager);
mListeners.add(missedCallNotifier);
// 语音提示声
mListeners.add(mDtmfLocalTonePlayer);
mListeners.add(mHeadsetMediaButton);
mListeners.add(mProximitySensorManager);
// ...
}
重点关注的是 InCallController,因为 InCallController 监听CallsManager, 收到来自于CallsManager的消息后, 跨进程回到 最初的 com.android.dialer 进程, 通知 UI 发生变化,也就是说 InCallController 是system 进程 主动沟通 com.android.dialer 进程的桥梁,下面详细解释,InCallController时如何沟通 com.android.dialer进程的。
lnCallController. onCallAdded 消息回调
@Override
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);
for (Map.Entry<ComponentName, IInCallService> entry : mInCallServices.entrySet()) {
ComponentName componentName = entry.getKey();
IInCallService inCallService = entry.getValue();
ParcelableCall parcelableCall = toParcelableCall(call,
true /* includeVideoProvider */);
try {
inCallService.addCall(parcelableCall);
} catch (RemoteException ignored) {
}
}
}
}
下面直接看一下,启动完成后,在onConnected函数中,做了什么
private void onConnected(ComponentName componentName, IBinder service) {
Trace.beginSection("onConnected: " + componentName);
Log.i(this, "onConnected to %s", componentName);
IInCallService inCallService = IInCallService.Stub.asInterface(service);
mInCallServices.put(componentName, inCallService);
try {
inCallService.setInCallAdapter(
new InCallAdapter(
mCallsManager,
mCallIdMapper,
mLock));
} catch (RemoteException e) {
Log.e(this, e, "Failed to set the in-call adapter.");
Trace.endSection();
onInCallServiceFailure(componentName, "setInCallAdapter");
return;
}
// Upon successful connection, send the state of the world to the service.
Collection<Call> calls = mCallsManager.getCalls();
if (!calls.isEmpty()) {
Log.i(this, "Adding %s calls to InCallService after onConnected: %s", calls.size(),
componentName);
for (Call call : calls) {
try {
// Track the call if we don't already know about it.
addCall(call);
inCallService.addCall(toParcelableCall(call, true /* includeVideoProvider */));
} catch (RemoteException ignored) {
}
}
onCallAudioStateChanged(
null,
mCallsManager.getAudioState());
onCanAddCallChanged(mCallsManager.canAddCall());
} else {
unbindFromServices();
}
Trace.endSection();
}
- 通过inCallService的setInCallAdapter设置了和InCallService双向通信的binder接口。
- 这个时候要是还有没出来的call,调用incallService.addCall
双向操作
可以看到InCallAdapter也是个IInCallAdapter的服务端实现(frameworks/base/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl)
class InCallAdapter extends IInCallAdapter.Stub {
private final CallsManager mCallsManager;
private final CallIdMapper mCallIdMapper;
private final TelecomSystem.SyncRoot mLock;
....
InCallServiceImpl(packages/apps/InCallUI/src/com/android/incallui/InCallServiceImpl.java)继承了InCallService,所以最后最后启动了InCallServiceImpl。
当用户操作的时候,通过InCallAdapter对象来通知telecom(最终是调用CallsManager相关函数),当底层状态变化(也是通过CallsManager调用InCallController的回调,最后调到InCallService),用InCallService来让上层更新相关状态。
所以,分层来看InCallService连接上下,操作的角度CallsManager承上启下。