关闭

Android5.0 Telephony框架初步分析--telecomm

标签: android5.0telephonytelecomm
19610人阅读 评论(3) 收藏 举报
分类:

3.2   Telecomm关键类初始化和相互关系

 

3.2.1          Telecomm简述

Android5.0在Telephony的变化又比较大,增加了一个Telecomm模块,它位于界面应用如InCallUI和Phone框架之间,其具体的设计意图尚不明确,从代码分析上来看,流程比原来的架构复杂很多,可能是想把Phone进程独立得更开一些,类似于RIL进程,给应用提供一个扁平的Phone接口,不希望像以前一样,呼叫流程在Phone进程和应用进程的纵深过大,引起函数的耦合性太大,有些相同的函数会在不同的流程执行在不同的进程里面。

其改变前后的关系可用下面的图来简述:


之前函数的调用流程在不同进程间的接口不够平滑,现在添加新的Telecomm层后,整个软件框架就比较有序和易于管理。

 

 

对于telephony相关的部分,从5.0和4.4的代码比较来看,

1)在framework下面,5.0新增了telecomm和ims部分的代码,截图如下,



结合后面的代码分析,我们知道在framework部分的telecomm代码的作用是承上启下的关系,它通过aidl接口,一方面和Phone进行交互,这一层是以InCallService、ConnectionService为代表,它的相关部分会运行在Phone进程;另一方面,和TelecommApp进程下的服务如TelecomService通信,这一层以TelecomManager等为代表,但他们往往运行在应用进程里面。

 

2)在Package目录下,5.0的应用部分InCallUI下没有了manifest文件,Service目录则多了MMS、Telecomm目录,MMS部分为短彩信增加了新的服务流程,telecomm部分则添加了一些关键文件,如作为进程载体的TelecomApp.java文件,作为服务载体的TelecomServiceImpl.java,还有CallsManager、CallActivity等文件,这些文件的关系和作用将在后面逐步分解。。



 

 

 

3.2.2          Phone

 

新增加的Phone.java在 (frameworks\base\telecomm\java\android\telecom)目录下,其功能是“A unified virtual device providing a means of voice (and other) communication on a device.”,即作为一个虚拟设备提供通信服务。其类型如下,

           public final class Phone {

它的方法的实现主要依赖3个辅助类,InCallAdapter、Listener、Call,即呼叫适配器、监听器、控制器(?),后续展开分析。

 

对于Phone的初始化,我们可以通过其方法的调用关系找到,例如查找internalAddCall,我们就会发现调用者为InCallService,其中mPhone即为Phone的实例,

case MSG_ADD_CALL:

mPhone.internalAddCall((ParcelableCall) msg.obj);

 

在InCallService里,当收到MSG_SET_IN_CALL_ADAPTER消息时,会创建一个Phone实例,同时也顺带创建了一个InCallAdapter实例,

                case MSG_SET_IN_CALL_ADAPTER:

                    mPhone = new Phone(new InCallAdapter((IInCallAdapter) msg.obj));

                    onPhoneCreated(mPhone);

                    break;          

 

MSG_SET_IN_CALL_ADAPTER消息是setInCallAdapter发出的,它是在InCallService里实现的AIDL接口类的服务端方法,

    private final class InCallServiceBinder extends IInCallService.Stub {

        @Override

        public void setInCallAdapter(IInCallAdapter inCallAdapter) {

            mHandler.obtainMessage(MSG_SET_IN_CALL_ADAPTER, inCallAdapter).sendToTarget();

        }          

 

根据我们对service的了解,它在service类创建时创建binder,在service的onbind()方法里将binder实例传入,执行客户端的ServiceConnection的onServiceConnected方法,所以在客户端,onConnected被执行,之后就是客户端InCallController里的setInCallAdapter被调用,再间接调用前面提到的服务端的setInCallAdapter。

private class InCallServiceConnection implements ServiceConnection {

        /** {@inheritDoc} */

        @Override public void onServiceConnected(ComponentName name, IBinder service) {

            Log.d(this, "onServiceConnected: %s", name);

            onConnected(name, service);

        } 

 

    private void onConnected(ComponentName componentName, IBinder service) {

        ThreadUtil.checkOnMainThread();

 

        Log.i(this, "onConnected to %s", componentName);

 

        IInCallService inCallService = IInCallService.Stub.asInterface(service);

 

        try {

            inCallService.setInCallAdapter(new InCallAdapter(CallsManager.getInstance(),

                    mCallIdMapper));

            mInCallServices.put(componentName, inCallService);

        } catch (RemoteException e) {

            Log.e(this, e, "Failed to set the in-call adapter.");

            return;

        }

 

所以可以看出,Phone的实例就是在InCallService服务启动过程中创建的。

 

 

3.2.3          InCallController

 

 

 

前面提到的binder的客户端InCallController位于(packages\services\telecomm\src \com\android\server\telecom)目录,根据manifest文件,它运行在TelecomApp这个应用里,这是一个的5.0新进程。又根据android:persistent="true"这个属性我们知道,TelecomApp是开机自启动的。

 

InCallController是在CallsManager的构造函数里创建的,CallsManager又是在TelecomApp的onCreate方法里面创建的,

    public void onCreate() {

        super.onCreate();

 

        if (UserHandle.myUserId() == UserHandle.USER_OWNER) {

            // Note: This style of initialization mimics what will be performed once Telecom is

            // moved

            // to run in the system service. The emphasis is on ensuring that initialization of all

            // telecom classes happens in one place without relying on Singleton initialization.

            mMissedCallNotifier = new MissedCallNotifier(this);

            mPhoneAccountRegistrar = new PhoneAccountRegistrar(this);

 

            mCallsManager = new CallsManager(this, mMissedCallNotifier, mPhoneAccountRegistrar);

            CallsManager.initialize(mCallsManager);

 

            mTelecomService = new TelecomServiceImpl(mMissedCallNotifier, mPhoneAccountRegistrar,

                    mCallsManager, this);

            ServiceManager.addService(Context.TELECOM_SERVICE, mTelecomService);

 

            // Start the BluetoothPhoneService

            BluetoothPhoneService.start(this);

        }

    }          

 

所以,TelecomApp进程的启动过程中,创建了InCallController和CallsManager两个实例,这两个类实例相互关联。

 

 

3.2.4          TelecomService

 

实际上,并不存在TelecomService这个名字的文件或类名,但存在一个这样的服务。

 

还是在TelecomApp的onCreate方法里(如上),创建了一个TelecomServiceImpl类实例,它对应于TelecomService服务的AIDL服务端,它就是以类实例为服务端,并不像service里面bind的binder接口,并且,它被作为一个服务添加到serviceManager里面。

mTelecomService = new TelecomServiceImpl(mMissedCallNotifier, mPhoneAccountRegistrar,

                    mCallsManager, this);

ServiceManager.addService(Context.TELECOM_SERVICE, mTelecomService);

 

public class TelecomServiceImpl extends ITelecomService.Stub {          

TelecomServiceImpl依赖CallsManager、PhoneAccountRegistrar、MissedCallNotifier这几个类完成相应的功能。

 

TelecomService的客户端是TelecomManager,它通过getTelecomService获取到服务端接口,然后通过这个接口使用服务端的远程接口,

     private ITelecomService getTelecomService() {

        return ITelecomService.Stub.asInterface(ServiceManager.getService(Context.TELECOM_SERVICE));

    }

 

 

3.2.5          TelecomManager

 

TelecomManager在framework的android.telecom包里面,它在ContextImpl被创建,并加入到注册列表里,属于系统级的服务,

        registerService(TELECOM_SERVICE, new ServiceFetcher() {

                public Object createService(ContextImpl ctx) {

                    return new TelecomManager(ctx.getOuterContext());

                }});

 

应用要获取其实例,可以通过其from方法,也可以直接通过context.getSystemService(Context.TELECOM_SERVICE)这个语句来获取。

 

TelecomManager的功能则主要是对TelecomService提供的远程接口的封装,然后提供给应用使用,例如对于showInCallScreen方法,在TelecomServiceImpl提供服务端的远程方法,TelecomManager提供客户端访问接口,DialpadFragment等应用组件使用这个接口,

 TelecomServiceImpl.java (packages\services\telecomm\src\com\android\server\telecom):    public void showInCallScreen(boolean showDialpad)

 

TelecomManager.java (frameworks\base\telecomm\java\android\telecom):    public void showInCallScreen(boolean showDialpad)

 

DialpadFragment.java (packages\apps\dialer\src\com\android\dialer\dialpad):        getTelecomManager().showInCallScreen(showDialpad);

DialtactsActivity.java (packages\apps\dialer\src\com\android\dialer):            getTelecomManager().showInCallScreen(false);

 

虽然TelecomServiceImpl是在packages目录下,TelecomManager在frameworks目录下,但前者作为一个服务,在单独进程里为后者提供服务,后者则为应用进程提供接口服务,再通过binder进程通信访问前者的服务,其关系如下,



 

 

3.2.6          InCallService

 

InCallService是一个抽象类,继承于service,它由四大部分组成,Handler、InCallServiceBinder、VideoCall以及自身的一些方法,其中InCallServiceBinder是aidl接口的服务端实现,对应于前面提到的InCallController,它主要是给当前服务发送消息,Handler接收并处理这些消息,如前面提到,Handler会创建一个Telecom的Phone实例,然后使用Phone的接口处理应用请求,所以实际上,客户端的请求实际上是Phone来完成的,至于Phone是如何实现功能的,请参见Phone的分析,这里的几个关键类的相互关系如下,


 

InCallService的主要功能是给应用提供管理Phone call的途径,它的子类InCallServiceImpl完成真正服务实例的创建,当存在呼叫连接时,它bind到Telecomm,并接受呼叫状态的更新。

 

服务实例的绑定过程是在InCallController里面完成的,

     private void bind() {…

            Intent serviceIntent = new Intent(InCallService.SERVICE_INTERFACE);

            for (ResolveInfo entry : packageManager.queryIntentServices(serviceIntent, 0)) {

                    InCallServiceConnection inCallServiceConnection = new InCallServiceConnection();

                    ComponentName componentName = new ComponentName(serviceInfo.packageName,

                            serviceInfo.name);

                        Intent intent = new Intent(InCallService.SERVICE_INTERFACE);

                        intent.setComponent(componentName);

 

                        if (mContext.bindServiceAsUser(intent, inCallServiceConnection,

                                Context.BIND_AUTO_CREATE, UserHandle.CURRENT)) {

                            mServiceConnections.put(componentName, inCallServiceConnection);

                        }

…}

这个bind()是在onCallAdded()里被调用的,onCallAdded被调用的地方有两个:

CallsManager.java (packages\services\telecomm\src\com\android\server\telecom):            listener.onCallAdded(call);

Phone.java (frameworks\base\telecomm\java\android\telecom):            listener.onCallAdded(this, call);

 

他们都是通过listener的方式被调用的,通过分析两个类的listener,发现Phone的监听器主要在应用文件中注册,

CallList.java (packages\apps\incallui\src\com\android\incallui):        mPhone.addListener(mPhoneListener);

InCallPresenter.java (packages\apps\incallui\src\com\android\incallui):        mPhone.addListener(mPhoneListener);

 

CallsManager的监听器在其构造函数中注册,并且监听器类型为CallsManagerListener,

    private final Set<CallsManagerListener> mListeners = Collections.newSetFromMap(            new ConcurrentHashMap<CallsManagerListener, Boolean>(16, 0.9f, 1));

 

     CallsManager(Context context, MissedCallNotifier missedCallNotifier,

             PhoneAccountRegistrar phoneAccountRegistrar) {…

        mCallLogManager = new CallLogManager(context);

        mInCallController = new InCallController(context);

 

        mListeners.add(statusBarNotifier);

        mListeners.add(mCallLogManager);

        mListeners.add(mPhoneStateBroadcaster);

        mListeners.add(mInCallController);

        mListeners.add(mRinger);

…}

所以是CallsManager. addCall调用了InCallController的onCallAdded。addCall则会被来电、去电、会议电话等接口方法调用,如startOutgoingCall。

 

所以当有通话连接要产生时,会启动InCallService服务,其大致过程如下:







如果觉得我的文章对您有用,请打赏。您的支持是对我莫大的认可


2
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:160551次
    • 积分:2535
    • 等级:
    • 排名:第15126名
    • 原创:92篇
    • 转载:20篇
    • 译文:0篇
    • 评论:55条
    最新评论