前言

与通话相关的绝大多数操作都与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作为切入点。

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

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

  1. 安卓开发框架(MVP+主流框架+基类+工具类)--- Fresco

    转载自:http://blog.csdn.net/ljy_programmer/article/details/78273267 学习/参考地址:  https://www.fresco-cn.org ...

  2. 安卓来电归属地_HavocOS v3.5 (Android10) ROM 使用体验,带你探索原生安卓的魅力!...

    扫描二维码 获取更多精彩 云飞科技站 今天我又带来另一款原生类ROM的使用体验--Havoc-OS v3.5 (Android10) ROM. Havoc-OS是一款基于AOSP源代码编译制作的ROM ...

  3. 安卓来电归属地_原生 Android 也能远离骚扰电话:开源应用来电信息

    现在看到一个陌生的电话打来,是不是都已经没有接听的冲动了?国内各种各样符合国情定制化的安卓手机系统,已经可以拦截众多烦人的骚扰电话. 但无论是原生 Android 党还是在国外购机的回国用户,都会发现 ...

  4. 安卓来电归属地_唯一数码带你看WWDC | 数码怪圈,安卓、IOS、WP居然互相抄袭

    IOS一直是手机领域的系统高端产品,也一直是其他手机争相模仿的对象,不过这些年来,苹果是否有点"黔驴技穷"?比如这次WWDC2020开完后,网上一度的风评是:"苹果终于实 ...

  5. 安卓来电归属地_如何做一名突出的iPhone用户?安卓勿进!

    你能一眼分出iPhone用户和安卓用户吗?最近,知乎有个很有趣的问题上了热搜:有哪些特征一眼会被看出是iPhone用户?特征?iPhone用户最大的特征不是没有口袋,喜欢把手机拿在手上吗?不开玩笑了, ...

  6. 安卓导出Excel,txt文件工具类

    开始 安卓开发中,有时候会遇到到处文件的需求,尤其是平板上的开发,这个需求更为普遍,本文记录导出excel,txt文件的方法,并提供工具类,抛砖引玉,让大家遇到类似需求的时候,处理起来更为顺手. 导出 ...

  7. android 监听来电去电,Android监听来电和去电的实现方法

    本文实例讲述了Android监听来电和去电的实现方法.分享给大家供大家参考,具体如下: 要监听android打电话和接电话,只需下面2步骤 第一步,写一个Receiver继承自BroadcastRec ...

  8. 安卓来电归属地_不封号电销卡、企业电销专用外呼线路11位数号码(归属地自定)...

    电销人员的号码每天都是高频呼出几乎没有呼入记录的,易被封号; 每次电话营销,客户手机显示的都是自己的真实号码,易被标记封号 对于中小企业来说,电销封卡是个无法回避的困局.随着电信管控趋严,无论是擦边球 ...

  9. android监听来电去电广播

    转http://www.cnblogs.com/pen-ink/archive/2010/12/20/1911957.html 要监听android打电话和接电话,只需下面2步骤 1.第一步,写一个R ...

最新文章

  1. form提交后,jquery 显示 文本框选择值和下拉框选中值
  2. 【错误记录】Python 安装依赖库报错 ( ERROR: Could not find a version that satisfies the requirement elftools )
  3. flutter怎么手动刷新_flutter局部刷新的实现示例
  4. 大学生如何成功就业。
  5. 通汇手机为何卖得那么红火
  6. 程序员的SOHO:接单到完成的全过程
  7. php 返回设置时间戳,PHP-返回int时间戳而不是datetime
  8. 解决servlet中get方式中中文乱码问题前驱(一):装饰者模式再理解
  9. 小米主题显示服务器不可用,小米主题商店 小米主题怎么混搭
  10. php 问卷调查,使用php问卷调查结果统计
  11. Java开发-日期与时间戳转换封装工具类
  12. 运用HTML制作简单效果
  13. 机器人瓦力漫威_章节目录 86、机器人瓦力
  14. Modbus通讯协议(四)——Java实现ModbusTCP Slave(从机)
  15. iPad菜单日渐走热美国
  16. 入门数据分析,需要会什么
  17. uboot中LCD驱动修改
  18. 导出mysql表数据到文件
  19. go 并发编程 之 数据竞争 data race (三)
  20. SuperMap iClient3D for WebGL中加载地方天地图

热门文章

  1. 以人文底蕴为前引解读售楼处设计的趋势
  2. 用Pytorch搭建一个房价预测模型
  3. 标梵分享无纸动画的制作软件有哪些?
  4. jmeter连接达梦数据库进行测试
  5. ArcMap 图层样式批量设置
  6. 三星Note2(N7100)刷机
  7. 终于有一款多功能的串口网络中控播放器
  8. 基于PHP的车辆违章查询api调用代码示例
  9. svn服务器搭建需要什么配置文件,架设svn服务器安装配置图解教程
  10. CSS修改浏览器滚动条样式