关闭

Telephony之TelephonyRegistry(原)

标签: TelephonyAndroidFramework
2150人阅读 评论(1) 收藏 举报
分类:

一、TelephonyRegistry概述


        TelephonyRegistry的作用是检测当前Radio的状态,包括通话、短信、数据连接等状态,当这些状态发生改变时,通知所有向他注册过的客户端。也就是说,他负责Radio状态的通知。
        本质上来讲,TelephonyRegistry是一个Service,在开机时通过SystemServer添加到ServiceManager中:
        telephonyRegistry = new TelephonyRegistry(context);
        ServiceManager.addService("telephony.registry", telephonyRegistry);
        从这个注册过程中看到,该Service注册的名字为“telephony.registry”

        下面来看TelephonyRegistry的通知机制。


二、TelephonyRegistry通知机制


        既然TelephonyRegistry负责状态的通知,那么就要完成两个工作:
        1、从Radio拿到通知;
        2、将通知发送给相应的监听者;

        下面分别来介绍。


2.1、从Radio拿到通知消息


        TelephonyRegistry的通知,是从他的客户端得到的,这个客户端就是DefaultPhoneNotifier。下面来看如何从客户端得到通知的。
        在《Telephony之进程与实体》一文中介绍过,创建GSMPhone时需要传递两个重要参数,其中一个是RILJ对象,另一个就是DefaultPhoneNotifier对象,这里的DefaultPhoneNotifier就是TelephonyRegistry的Client。
        我们来看DefaultPhoneNotifier的构造函数:
        protected DefaultPhoneNotifier() {
            mRegistry = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService("telephony.registry"));
        }
        在这里,我们看到DefaultPhoneNotifier从ServiceManager中获取了TelephonyRegistry的服务,也就是说,DefaultPhoneNotifier是TelephonyRegistry的Client
        这样一来,DefaultPhoneNotifier就将RILJ与TelephonyRegistry联系起来了,当RILJ接收到RIL上报的Phone状态时,就会通过DefaultPhoneNotifier发送给TelephonyRegistry
        比如对于通话状态的改变,我们在《Telephony之GsmCallTracker》中介绍过,GsmCallTracker会通过以下方式将状态发送给GSMPhone:
        @GsmCallTracker.java
        private void updatePhoneState() {
            PhoneConstants.State oldState = mState;
            if (mRingingCall.isRinging()) {
                mState = PhoneConstants.State.RINGING;
            } else if (mPendingMO != null ||
                    !(mForegroundCall.isIdle() && mBackgroundCall.isIdle())) {
                mState = PhoneConstants.State.OFFHOOK;
            } else {
                mState = PhoneConstants.State.IDLE;
            }

            if (mState == PhoneConstants.State.IDLE && oldState != mState) {
                mVoiceCallEndedRegistrants.notifyRegistrants(
                        new AsyncResult(null, null, null));
            } else if (oldState == PhoneConstants.State.IDLE && oldState != mState) {
                mVoiceCallStartedRegistrants.notifyRegistrants (
                        new AsyncResult(null, null, null));
            }

            if (mState != oldState) {
                //给GSMPhone发送通知
                mPhone.notifyPhoneStateChanged();
            }
        }
        上面的mPhone对象就是GSMPhone,接下来我们将会看到,GSMPhone会把通知转交给DefaultPhoneNotifier来处理:
        @GSMPhone.java
        void notifyPhoneStateChanged() {
            mNotifier.notifyPhoneState(this);
        }
        这里的mNotifier就是创建GSMPhone对象时传递的DefaultPhoneNotifier对象,这样的话,就将通知发送到了DefaultPhoneNotifier内部:
        @DefaultPhoneNotifier.java
        public void notifyPhoneState(Phone sender) {
            Call ringingCall = sender.getRingingCall();
            String incomingNumber = "";
            if (ringingCall != null && ringingCall.getEarliestConnection() != null){
                incomingNumber = ringingCall.getEarliestConnection().getAddress();
            }
            try {
                //将状态发送给TelephonyRegistry
                mRegistry.notifyCallState(convertCallState(sender.getState()), incomingNumber);
            } catch (RemoteException ex) {
            }
        }
        从这里我们看到,DefaultPhoneNotifier将当前GsmCallTracker中的状态(sender.getState())通过convertCallState()转换后,传递给TelephonyRegistry,这个转换的作用就是,将GsmCallTracker中的IDLE、RINGING、OFFHOOK状态转换为TelephonyManager中的对应状态:
        @TelephonyManager.java
        public static final int CALL_STATE_IDLE = 0;
        public static final int CALL_STATE_RINGING = 1;
        public static final int CALL_STATE_OFFHOOK = 2;

        就这样,DefaultPhoneNotifier作为TelephonyRegistry的Client,将当前通话状态传递给了TelephonyRegistry。


2.2、将消息发送给其他客户端


        TelephonyRegistry拿到相应的通知后,是如何将消息发送给其他客户端呢?我们继续用通话状态来跟踪这一流程。

        在TelephonyRegistry拿到消息后,需要向两个渠道分发消息,一个是通过系统广播,另一个是向自己注册的监听者。我们主要来看向监听者发送消息的流程。


2.2.1、监听者如何注册监听


        首先我们来看如何成为TelephonyRegistry的监听者。
        由于TelephonyRegistry提供关于Radio的多种状态监测,包括通话、信号、呼叫转移、数据连接等状态,所以在向其申请监听时,需要说明监听那种状态,可以通过调用listen()方法来实现,我们来看这个接口:
        @TelephonyRegistry.java
        public void listen(String pkgForDebug, IPhoneStateListener callback, int events, boolean notifyNow) {
            //获取调用者的UID,监测权限
            int callerUid = UserHandle.getCallingUserId();
            int myUid = UserHandle.myUserId();

            if (events != 0) {
                //权限监测
                checkListenerPermission(events);

                synchronized (mRecords) {
                    Record r = null;
                    find_and_add: {
                          //获取当前监听者信息
                          IBinder b = callback.asBinder();
                          final int N = mRecords.size();
                          for (int i = 0; i < N; i++) {
                              r = mRecords.get(i);
                              if (b == r.binder) {
                                  break find_and_add;
                              }
                          }
                          r = new Record();
                          r.binder = b;
                          r.callback = callback;
                          r.pkgForDebug = pkgForDebug;
                          r.callerUid = callerUid;
                          //添加当前的监听者信息
                          mRecords.add(r);
                    }
                    int send = events & (events ^ r.events);
                    r.events = events;
                    //需要立刻通知
                    if (notifyNow) {
                        if ((events & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) {
                            //监听通话状态
                            try {
                                r.callback.onServiceStateChanged(new ServiceState(mServiceState));
                            } catch (RemoteException ex) {
                                remove(r.binder);
                            }
                        }
                        if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTH) != 0) {
                            //监听信号改变
                            try {
                                int gsmSignalStrength = mSignalStrength.getGsmSignalStrength();
                                r.callback.onSignalStrengthChanged((gsmSignalStrength == 99 ? -1 : gsmSignalStrength));
                            } catch (RemoteException ex) {
                                remove(r.binder);
                            }
                        }
                        if ((events & PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR) != 0) {
                            try {
                                r.callback.onMessageWaitingIndicatorChanged(mMessageWaiting);
                            } catch (RemoteException ex) {
                                remove(r.binder);
                            }
                        }
                        if ((events & PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR) != 0) {
                            //监听呼叫转移状态
                            try {
                                r.callback.onCallForwardingIndicatorChanged(mCallForwarding);
                            } catch (RemoteException ex) {
                                remove(r.binder);
                            }
                        }
                    }
                }
            } else {
                remove(callback.asBinder());
            }
        }
        上面的listen()操作,依次完成了如下动作:
        1、通过checkListenerPermission()监测调用者是否有监听权限
        2、将调用者的相关信息保存在mRecords中,保存信息包含IBinder、回调方法、UID、监听事件等
        3、如果listen的同时要求立刻通知,则会立刻调用监听者的回调方法。

        经过这些操作,客户端就完成了对TelephonyRegistry的监听注册,等待接收通知。


2.2.2、如何将消息通知到监听者


        接下来我们分析,如何将通知发送给监听者。
        在2.1中我们知道,DefaultPhoneNotifier作为TelephonyRegistry的Client,通过notifyCallState()将当前通话状态通知到TelephonyRegistry,我们来看这个方法:
        @TelephonyRegistry.java
        public void notifyCallState(int state, String incomingNumber) {
            //权限检查
            if (!checkNotifyPermission("notifyCallState()")) {
                return;
            }
            synchronized (mRecords) {
                mCallState = state;
                mCallIncomingNumber = incomingNumber;
                for (Record r : mRecords) {
                    if ((r.events & PhoneStateListener.LISTEN_CALL_STATE) != 0) {
                        try {
                            //通知所有监听者
                            r.callback.onCallStateChanged(state, incomingNumber);
                        } catch (RemoteException ex) {
                            mRemoveList.add(r.binder);
                        }
                    }
                }
                //发送广播通知
                handleRemoveListLocked();
            }
            broadcastCallStateChanged(state, incomingNumber);
        }
        在上面这个方法中,先对通知者进行权限检查,然后在mRecords中查找曾经注册了该事件的监听者,并调用他们的回调方法。最后,又将该消息发送到系统广播中。

        至此,通话状态改变的消息就从GsmCallTracker通过DefaultPhoneNotifier传递给了TelephonyRegistry,并从此扩散。类似的,对于数据连接状态来说,将会由DcTracker通过DefaultPhoneNotifier传递给TelephonyRegistry,然后进行扩散。


2.3、小结


        从以上的分析中我们看到,TelephonyRegistry作为一个Service,成为他的Client后可以拥有两种功能:
        1、Client可以将当前Radio的状态发送给TelephonyRegistry,比如GsmCallTracker;
        2、Client可以向TelephonyRegistry申请监听Radio的相关状态,比如TelephonyManager;

        或者可以这样理解,TelephonyRegistry作为一个中介,转发和扩散有关Radio的状态。如下图所示:


1
1

猜你在找
深度学习基础与TensorFlow实践
【在线峰会】前端开发重点难点技术剖析与创新实践
【在线峰会】一天掌握物联网全栈开发之道
【在线峰会】如何高质高效的进行Android技术开发
机器学习40天精英计划
Python数据挖掘与分析速成班
微信小程序开发实战
JFinal极速开发企业实战
备战2017软考 系统集成项目管理工程师 学习套餐
Python大型网络爬虫项目开发实战(全套)
查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:259766次
    • 积分:3170
    • 等级:
    • 排名:第10162名
    • 原创:63篇
    • 转载:0篇
    • 译文:0篇
    • 评论:95条
    最新评论