欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

拨号流程分析(第三篇)

程序员文章站 2024-03-07 14:36:45
...

本文基于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的消息通知
}

注意:

  1. 在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
  2. 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通话界面相关信息 。