安卓来电、去电非常重要的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接口传递数据。以挂断电话流程为例:
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();
}
- 通过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承上启下。
作者:wbxjack
安卓来电、去电非常重要的2个类:CallsManager和IncallService相关推荐
- 安卓开发框架(MVP+主流框架+基类+工具类)--- Fresco
转载自:http://blog.csdn.net/ljy_programmer/article/details/78273267 学习/参考地址: https://www.fresco-cn.org ...
- 安卓来电归属地_HavocOS v3.5 (Android10) ROM 使用体验,带你探索原生安卓的魅力!...
扫描二维码 获取更多精彩 云飞科技站 今天我又带来另一款原生类ROM的使用体验--Havoc-OS v3.5 (Android10) ROM. Havoc-OS是一款基于AOSP源代码编译制作的ROM ...
- 安卓来电归属地_原生 Android 也能远离骚扰电话:开源应用来电信息
现在看到一个陌生的电话打来,是不是都已经没有接听的冲动了?国内各种各样符合国情定制化的安卓手机系统,已经可以拦截众多烦人的骚扰电话. 但无论是原生 Android 党还是在国外购机的回国用户,都会发现 ...
- 安卓来电归属地_唯一数码带你看WWDC | 数码怪圈,安卓、IOS、WP居然互相抄袭
IOS一直是手机领域的系统高端产品,也一直是其他手机争相模仿的对象,不过这些年来,苹果是否有点"黔驴技穷"?比如这次WWDC2020开完后,网上一度的风评是:"苹果终于实 ...
- 安卓来电归属地_如何做一名突出的iPhone用户?安卓勿进!
你能一眼分出iPhone用户和安卓用户吗?最近,知乎有个很有趣的问题上了热搜:有哪些特征一眼会被看出是iPhone用户?特征?iPhone用户最大的特征不是没有口袋,喜欢把手机拿在手上吗?不开玩笑了, ...
- 安卓导出Excel,txt文件工具类
开始 安卓开发中,有时候会遇到到处文件的需求,尤其是平板上的开发,这个需求更为普遍,本文记录导出excel,txt文件的方法,并提供工具类,抛砖引玉,让大家遇到类似需求的时候,处理起来更为顺手. 导出 ...
- android 监听来电去电,Android监听来电和去电的实现方法
本文实例讲述了Android监听来电和去电的实现方法.分享给大家供大家参考,具体如下: 要监听android打电话和接电话,只需下面2步骤 第一步,写一个Receiver继承自BroadcastRec ...
- 安卓来电归属地_不封号电销卡、企业电销专用外呼线路11位数号码(归属地自定)...
电销人员的号码每天都是高频呼出几乎没有呼入记录的,易被封号; 每次电话营销,客户手机显示的都是自己的真实号码,易被标记封号 对于中小企业来说,电销封卡是个无法回避的困局.随着电信管控趋严,无论是擦边球 ...
- android监听来电去电广播
转http://www.cnblogs.com/pen-ink/archive/2010/12/20/1911957.html 要监听android打电话和接电话,只需下面2步骤 1.第一步,写一个R ...
最新文章
- form提交后,jquery 显示 文本框选择值和下拉框选中值
- 【错误记录】Python 安装依赖库报错 ( ERROR: Could not find a version that satisfies the requirement elftools )
- flutter怎么手动刷新_flutter局部刷新的实现示例
- 大学生如何成功就业。
- 通汇手机为何卖得那么红火
- 程序员的SOHO:接单到完成的全过程
- php 返回设置时间戳,PHP-返回int时间戳而不是datetime
- 解决servlet中get方式中中文乱码问题前驱(一):装饰者模式再理解
- 小米主题显示服务器不可用,小米主题商店 小米主题怎么混搭
- php 问卷调查,使用php问卷调查结果统计
- Java开发-日期与时间戳转换封装工具类
- 运用HTML制作简单效果
- 机器人瓦力漫威_章节目录 86、机器人瓦力
- Modbus通讯协议(四)——Java实现ModbusTCP Slave(从机)
- iPad菜单日渐走热美国
- 入门数据分析,需要会什么
- uboot中LCD驱动修改
- 导出mysql表数据到文件
- go 并发编程 之 数据竞争 data race (三)
- SuperMap iClient3D for WebGL中加载地方天地图