1、概述
关于信号更新,从很早个人未看相关代码时,一直以为很简单。ril上报信号,UI进行更新就行。话是这么说,但Google设计方案可没那么简单,就好比如1+1=2,Google非得这么告诉你x+y=2,其实这是体现出代码设计的重要性。而关于信号更新流程,设计中就使用到了代理模式,观察者模式。另本文档主要集中讲解信号的数据收到后,如何在APP上拿到。也就是讲解信号数据的来龙去脉,方便后面针对相关问题快速定位分析。
2、类图
从整体的框架结构来看,信号更新流程可分析3层,自下到上分别为应用层、接口层、服务层。
应用层用于接收到信号后,进行对应用的逻辑处理,如UI显示这些;
接口层用于注册事件回调,提供观察者服务,这一层的实现是由AIDL机制完成。
服务层则收到信号数据的来源,然后上报给接口层处理。
而贯穿这三个层次的数据是SingnalStringth类,里面存放着各制式下的信号值,三个层次都是围绕着这个类进行传递和处理。
而关于信号更新流程的处理逻辑,也可分析三个步骤,分别为初始化、注册、回调。
初始化其实分三部分进行,分别为服务层,接口层及应用层,也就是对应的ServiceStateTracker、TelephonyRegistry和MobileSignalController的初始化,这三个层都是跨进程,所以才分别进行初始化。
注册是应用层向接口层的事件注册,应用层无需跟服务层有交互,由接口层进行代理,这个设计就就使用到了观察者模式。
回调是由服务层向接口层提供信号数据,然后由接口层根据谁注册了相关事件进行分发。
关键类的功能作用:
Phone.java 电话类的功能。
GsmCdmaPhone.java 继承Phone的类。
PhoneFactory.java Phone的工厂类,创建GsmCdmaPhone。
DefaultPhoneNotifier.java 用于回调TelephonyRegistry的相关函数。
ServiceStateTracker.java 与RIL通信的类,重点是维护信号,网络信息,小区信息等。
SignalStrength.java 信号类
TelephonyRegistry.java 一个AIDL服务,提供系统接口。
TelephonyManager.java 为TelephonyRegistry提供间接接口,相当于TelephonyRegistry的代理。
Record.java 保存callback的类
MobileSignalController.java 状态栏信号管理的一个类
MobilePhoneStateListener.java 是MobileSignalController的子类,用于实现函数回调,事件注册的类。
3、流程说明
3.1 初始化
3.1.1 ServiceStateTracker初始化
是由GsmCdmaPhone来完成,而GsmCdmaPhone的初始化是由packages\services\Telephony来完成。具体见如下时序图。
在ServiceStateTracker初始化过程中,有个重要的对象sPhoneNotifier,其作用是用于跟接口层TelephonyRegistry交互,完成数据上报。具体的上报调用流程是ServiceStateTracker调用Phone,Phone再调用PhoneNotifier,PhoneNotifier最后调用TelephonyRegistry。
3.1.2 TelephonyRegistry初始化
TelephonyRegistry其实是个AIDL服务,对外提供接口,是一个可以跨进程调用的服务。竟然是AIDL服务,这个一般都是在SystemServer.java里面进行初始化。具体见如下代码:
TelePhonyManager其实是TelephonyRegistry的一个代理,动作全由它来帮对接转发。
3.1.3 MobileSignalController初始化
MobileSignalController的初始化是由SystemUI的一个APK来完成,SystemUI就是状态栏。此APK因了解的不多,暂讲解到这。
3.2 注册
应用层通过注册一些想要知道Telephony的变化事件来执行相关的功能处理。在注册中,应用层必须要实现一个类来继承PhoneStateListener,然后把此类生成的对象注册到TelephonyRegistry里。时序图如下。
在MobileSignalController里来继承PhoneStateListener的类是MobilePhoneStateListener,代码实现见如下图:
然后在MobileSignalController的构造函数里完成初始化动作,代码实现见如下图:
最后再调用注册函数来完成事件注册,代码实现见如下图,其中mPhone是TelePhonyManager的对象。
通过TelephonyRegistry.java可以了解到,notifySignalStrengthForPhoneId函数里会调用r.callback.onSignalStrengthsChanged,其中callback就是MobilePhoneStateListener。
3.3 回调
信号的数据是从RIL上报到ServiceStateTracker,ServiceStateTracker数据收到后,通过调用GsmCdmaPhone的对象,然后再调用PhoneNotifier的对象发给TelephonyRegistry,TelephonyRegistry最后就分发给注册者MobilePhoneStateListener。
关于回调的流程见如下时序图的6~16步骤:
EVENT_GET_SIGNAL_STRENGTH这个消息一开始是由初始化ServiceStateTracker对象时,向ril请求,见如下图:
ril查询完成后,回调此消息,执行的代码如下:
然后就执行onSignalStrengthResult,见上面的时序图。但此时还会调用queueNextSignalStrengthPoll,此函数作用是20S后,重新查询信号,一直循环下去。
另信号也有主动上报,在ServiceStateTracker构造函数里向RIL注册了EVENT_SIGNAL_STRENGTH_UPDATE此消息,回调后同样执行onSignalStrengthResult,不再分析。
4、案例分析
RIL上报的信号很强,但为何UI上显示空三角?
了解了上面流程后,信号多少是直接在UI显示多少等级的原理,我们就可以直接查看MobileSignalController类里面的onSignalStrengthsChanged函数。
1、先确认此函数打印的信号是否为有效值。一般此处正常,确认这一步只是用来确定信号的强度如何而已。
2、再进入到updateTelephony函数里,见如下代码,信号满足更新前得卡是注册上的状态,见connected。
关于connected的条件是hasService() && mSignalStrength != null,mSignalStrength这个好了解,是从ServiceStateTracker.java获取,为NULL的话,连更新的机会都没有。
接下来看下hasService的函数内容如下,可以看到其中一个条件是voice必须为STATE_IN_SERVICE。如果voice不是STATE_IN_SERVICE,可以从data的状态进一步来判断。
而voice或data是如何变成STATE_IN_SERVICE的状态呢,通过ServiceStateTracker可以看到,ril上报的状态如果是REG_STATE_HOME或REG_STATE_ROAMING就修改成了STATE_IN_SERVICE
所以信号为何显示空三角,还需要重点确认步骤2的一些状态值。
5、总结
本文档其实细节的内容讲的不多,根据时序图去跟踪即可,重点是讲了框架,总结下优秀的代码实现过程,方便后面借鉴学习。