拨号流程分析(第三篇)
本文基于Android 8.0
InCallService服务的响应过程
lnCallServicelmpl类继承于lnCallService类,类代码文件在packages/apps/Dialer工程下,而lnCallService类对应的代码文件则在 framework 下,其服务接口的定义文件为: frameworks/base/telecomm/java/com/android/internal/telecom/llnCallService.aidl ,主要定义了addCall、setlnCallAdapter、updateCall 等接口方法。
InCallController在拨号流程中,首先绑定服务,接着调用服务的setlnCallAdaptr、addCall和onCallXXXChanged接口 。 llnCallService服务的响应逻辑是什么呢。接下来 ,我们分别分析IlnCallService的onBind 、setlnCallAdapter和addCall 服务接口 。
1. OnBind服务被绑定的响应方法
/packages/apps/Dialer/java/com/android/incallui/InCallServiceImpl.java
@Override
public IBinder onBind(Intent intent) {
...
InCallPresenter.getInstance().onServiceBind();// 设置服务绑定状态mServiceBound属性
InCallPresenter.getInstance().maybeStartRevealAnimation(intent);// 开启Activity
TelecomAdapter.getInstance().setInCallService(this);// 保存服务
...
return super.onBind(intent);// 调用父类的onBind方法
}
InCallService的OnBind方法:
public IBinder onBind(Intent intent) {
return new InCallServiceBinder();
}
InCallServiceBinder实现了IInCallServicer.aidl的接口,这些接口通过发送Handler消息 , 将服务接收到的服务请求转化为异步 处理方式:
private final class InCallServiceBinder extends IInCallService.Stub {
@Override
public void setInCallAdapter(IInCallAdapter inCallAdapter) {
mHandler.obtainMessage(MSG_SET_IN_CALL_ADAPTER, inCallAdapter).sendToTarget();
}
@Override
public void addCall(ParcelableCall call) {
mHandler.obtainMessage(MSG_ADD_CALL, call).sendToTarget();
}
@Override
public void updateCall(ParcelableCall call) {
mHandler.obtainMessage(MSG_UPDATE_CALL, call).sendToTarget();
}
...
}
mHandler发送Handler消息将llnCallService的同步调用转换为异步处理,在后续的setlnCallAdapter和addCall接口响应中继续分析其实现逻辑。
查看 lnCallPresenter.getlnstance().maybeStartRevealAnimation()方法的逻辑实现,将启动lnCallActivity,即通话界面的核心逻辑如下:
final Intent activityIntent =
InCallActivity.getIntent(mContext, false, true, false /* forFullScreen */);
activityIntent.putExtra(TouchPointManager.TOUCH_POINT, touchPoint);
mContext.startActivity(activityIntent);
activityIntent通过intent.setClass(context, InCallActivity.class)的方式指定了启动lnCallActivity,其 AndroidManifest.xml中的配置信息如下:
<activity
android:directBootAware="true"
android:excludeFromRecents="true"
android:exported="false"
android:label="@string/phoneAppLabel"
android:launchMode="singleInstance"
android:name="com.android.incallui.InCallActivity"
android:resizeableActivity="true"
android:screenOrientation="nosensor"
android:taskAffinity="com.android.incallui"
android:theme="@style/Theme.InCallScreen">
</activity>
上面对InCallActivity的定义重点关注以下几点:
- exported
exported为false ,说明无法以跨进程的方式启动此Activity,只能从当前进程启动 - launchMode
launchMode为singlelnstance ,说明为单例,方便界面管理, 减少内存开销 - name
指定com.android.incallui.lnCallActivity类对应的Activity配置项
2. InCallService的onBind流程
InCallService的onBind流程跟踪和分析总结如图所示:
重点关注lnCallPresenter的创建和初始化操作,即图中的步骤2和步骤3 ;以及步骤7 :调用startActivity初始化InCallActivity通话界面.
onBind只获取了intent对象,并没有获取Call对象的通话相关信息 , 可以理解为lnCallActivity界面的预加载过程;这里做一个猜测,与Call对象相关的信息获取或更新应该是通过lnCallService服务的接口addCall和updateCall采完成消息传递的
3. setlnCallAdapter设置Adapter
// InCallService服务的响应方法setInCallAdapter
public void setInCallAdapter(IInCallAdapter inCallAdapter) {
// 通过mHandler发送MSG_SET_IN_CALL_ADAPTER异步消息
mHandler.obtainMessage(MSG_SET_IN_CALL_ADAPTER, inCallAdapter).sendToTarget();
}
mHandler作为InCallService类的匿名内部类对象,异步接收和处理mHandler发出的消息
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_SET_IN_CALL_ADAPTER:
String callingPackage = getApplicationContext().getOpPackageName();
mPhone = new Phone(new InCallAdapter((IInCallAdapter) msg.obj), callingPackage,
getApplicationContext().getApplicationInfo().targetSdkVersion);
mPhone.addListener(mPhoneListener);
onPhoneCreated(mPhone);
break;
...
}
}
setInCallAdapter接口的响应逻辑,主要是创建Phone对象和设置Phone对象的Listener属性 。Phone与lnCallService是同一个package ,即frameworks/base/telecomm/java/android/telecom/Phone.java代码
4. setlnCallAdapter流程图(InCallService类)
lnCallService类的setlnCallAdapter流程总结如图所示:
重点关注步骤12和步骤13,创建Phone对象和增加Listener为InCallService类的mPhonelistener对象,也是拨号流程Dialer应用中的第一个Listener。在创建Phone对象之前有个容易被忽略的地方,就是通过IlnCal lAdapter Binder对象创建了lnCallAdapter
设置llnCallAdapterBinder对象有什么作用昵? 我们看一下其接口定义文件 frameworks/base/telecomrn/java/com/android/i nternal/telecom/InCallAdapter.aidI中的内容,原来它提供了answerCall、rejectCall、mute、setAudioRoute、playDtmfrone 等控制通话的接口, 因此通过Binder对象可跨进程访问Telecom应用,即system_server进程的系统服务相关接口
5. addCall增加主动拨号Outgoing Call
addCall的方法响应模式与setlnCallAdapter方法一致,都是通过mHandler对象发送MSG_ADD_CALL消息,并在其 handleMessage方法中异步响应,关键代码逻辑如下:
case MSG_ADD_CALL:
mPhone.internalAddCall((ParcelableCall) msg.obj);
break;
Phone对象的internalAddCall()逻辑如下:
final void internalAddCall(ParcelableCall parcelableCall) {
// 通过parcelableCall相关信息创建Call对象
Call call = new Call(this, parcelableCall.getId(), mInCallAdapter,
parcelableCall.getState(), mCallingPackage, mTargetSdkVersion);
mCallByTelecomCallId.put(parcelableCall.getId(), call);
mCalls.add(call);
checkCallTree(parcelableCall);
// 通过parcelableCall更新当前Call对象相关信息
call.internalUpdate(parcelableCall, mCallByTelecomCallId);
fireCallAdded(call);// 发出addCall的消息通知
}
注意:
- 在Telecom应用中 ,首先会创建Call对象, Dialer应用中也会创建Call对象,但这两个Call对象的定义是不同的 。Telecom中的Call对象是当前应用中的定义packages/services/Telecomm/src/com/android/server/telecom/Call.java , 而Dialer应用中Call对象的定义是frameworks/base/telecomm/java/android/telecom/Call.java
- Call对象的创建与转换。从Telecom应用中创建com.android.server.telecom.Call,并通过此对象创建跨进程传递的
android.telecom.parcelableCall对象(支持序列化相反序列化 , 因此可以跨进程传递此对象),而Dialer应用中是接收到parcelableCall对象后 ,通过此对象相关信息创建 android.telecom.Call对象
继续分析fireCallAdded(call)方法,关键业务逻辑和流程详情如下:
private void fireCallAdded(Call call) {
for (Listener listener : mListeners) {
// InCallService在处理 MSG_SET_IN_CALL_ADAPTER消息时设置的Listener
listener.onCallAdded(this, call);
}
}
InCallServicer.mPhoneListener的onCallAdded响应:
private Phone.Listener mPhoneListener = new Phone.Listener() {
...
@Override
public void onCallAdded(Phone phone, Call call) {
// InCallServiceimpl重写了此方法,Java多态将调用子类对应的重写方法
InCallService.this.onCallAdded(call);
}
// 省略onCallAdded、onCallRemoved、onAudioStateChanged等接口实现,都是基于InCallService.this.onXXX的调用
...
}
接着我们将连续执行两次onCallAdded的调用:
InCallPresenter.getInstance().onCallAdded(call);// InCallServiceImpl
mCallList.onCallAdded(mContext, call, latencyReport);// InCallPresenter
进入CallList类的onCallAdded方法,其中dialerCallListener.onDialerCallUpdate()调用与当前拨号流程有关 。dialerCalIListener为CallList类的私有内部 DialerCallListenerlmpl对象,其核心逻辑如下:
@Override
public void onDialerCallUpdate() {
onUpdateCall(mCall);// 更新Call信息
notifyGenericListeners();// 调用CallList类的notifyGenericListeners方法
}
// CallList类的notifyGenericListeners方法
private void notifyGenericListeners() {
for (Listener listener : mListeners) {// 拨号流程Dialer应用中的第二个Listener
// InCallPresenter.setUp方法中增加的Listener mCallList.addListener(this)
listener.onCallListChange(this);
}
}
// InCallPresenter.onCallListChange中的核心逻辑
for (InCallStateListener listener : mListeners) {// 拨号流程Dialer应用中的第三个Listener
listener.onStateChange(oldState, mInCallState, callList);
}
在Dialer通话界面展示的流程跟踪过程中,已经涉及三个Listenr,可见其逻辑的复杂度,很容易就跟踪错误 。InCallStatelistener 是一个interface接口,它定义了一个方法onStateChange ,在Dialer工程下找到了八个实现此接口的类,详情如下:
- DialpadPresenter
- VideoPauseController
- ConferenceManagerPresenter
- CallCardPresenter
- CallButtonPresenter
- VideoCallPresenter
- ProximitySensor
- StatusBarNotifier
这些类通过名字就能看出是与通话界面相关的,特别是DialpadPresenter、CallCardPresenter 、CallButtonPresenter这几个类名 ,在后面解析通话界面的章节我们再进行详细解析。拨号流程跟踪到此,我们仅需要掌握其中的展示和更新通话界面即可。
6. lnCallService类的addCall流程
lnCallService类的addCall流程的分析和总结, 如图所示:
重点关注三个 Listener的消息回调,分别是步骤 7、步骤13和步骤14 ,而这些步骤中,都将传递 android.telecom.Call对象,最终交给InCalIStatelistener对象进行通话界面中与通话相关信息的展示和更新。
总结
再次回顾拨号流程, Dialer应用提供用户拨号操作界面,用户输入电话号码发起拨号请求,到Telecom应用接收拨号请求,完成第一次跨进程服务接口调用, Telecom应用创建com.android.server.telecom.CalI对象,然后通过此对象信息创建可跨进程传递的 android.telecom.parcelableCalI对象,完成第二次跨进程服务接口调用;回到Dialer应用的com.android.dialer进程中的llnCallService服务晌应,通过接收到的parcelableCall对象信息创建本地的android.telecom.Call对象,最后根据此对象更新和显示lnCallActivity通话界面相关信息 。
上一篇: 实例讲解.NET中资源文件的创建与使用