安卓来电、去电非常重要的2个类:CallsManager和IncallService

前言

与通话相关的绝大多数操作都与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接口传递数据。以挂断电话流程为例:
通话挂断部分流程(从Call至Connection)
IConnectionService接口:AIDL接口,实现类为ConnectionService,包装类为ConnectionServiceWrapper,其他类调用接口中的方法时应通过ConnectionServiceWrapper进行调用,而不应直接使用ConnectionService。
Connection:在framework中表示一路语音通话或视频通话的类,与modem中每一路通话连接一一对应,承载了。Connection为抽象类,在不同的网络下有不同的具体实现类,例如GSMConnection(GSM网络)、CDMAConnection(CDMA网络)。

Call与CallsManager的关联

Call中有一个监听器接口Listener,该接口负责在Call状态改变时进行回调。以来电时挂断电话流程为例:
来电时挂断通话Call中Listener接口回调流程
而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工作机制简图
上图大致包含了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作为切入点。

from

 

关于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();
}
  1. 通过inCallService的setInCallAdapter设置了和InCallService双向通信的binder接口。
  2. 这个时候要是还有没出来的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承上启下。


作者:wbxjack

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值