一、TelephonyManager概述
//得到软件版本
getDeviceSoftwareVersion()
//得到设备的ID,IMEI或者MEID
getDeviceId()
//得到位置信息,主要是当前注册小区的位置码
getCellLocation()
//得到附近小区信息
getNeighboringCellInfo()
//得到当前Phone的类型,GSM/CDMA
getCurrentPhoneType()
//得到/proc/cmdline文件当前的内容
getProcCmdLine()
//得到运营商名字
getNetworkOperatorName()
//得到MCC+MNC
getNetworkOperator()
//得到是否漫游的状态
isNetworkRoaming()
//得到网络状态,NETWORK_TYPE_GPRS、NETWORK_TYPE_EDGE、NETWORK_TYPE_CDMA等等
getNetworkType()
//得到SIM卡状态
getSimState()
//得到SIM卡MCC+MNC
getSimOperator()
//得到SIM卡SPN
getSimOperatorName()
//得到SIM卡串号
getSimSerialNumber()
//得到MSISDN
getMsisdn()
//得到语音信箱号码
getVoiceMailNumber()
//得到语音信箱短信条数
getVoiceMessageCount()
//得到语音信箱名称
getVoiceMailAlphaTag()
//得到数据连接状态:DATA_DISCONNECTED、DATA_CONNECTING、DATA_CONNECTED、DATA_SUSPENDED等
getDataState()
//注册监听器监听Phone状态
listen()
//得到所有Phone的信息
getAllCellInfo()
从这些方法来看,TelephonyManager提供了与PhoneInterfaceManager类似的功能,但是又有本质的区别,其共同点是:都向其他模块提供了全面的操作Telephony相关事务的能力,其他模块可以在获取到这两个服务后,对Telephony进行各种操作。而区别在于:1、从本质上来讲,TelephonyManager本质不是一个Service,没有继承任何类,而PhoneInterfaceManager的本质是一个Service
2、从注册方式上来讲,TelephonyManager是在ContextImpl中通过registerService的形式进行注册,而PhoneInterfaceManager是通过ServiceManager进行注册。
3、从获取方式上来讲,需要TelephonyManager服务时,可以通过Context对象的getSystemService()方法来实现,而PhoneInterfaceManager服务需要通过ServiceManager的getService()方法来实现。
下面我们通过代码来进一步论述上面的说明。
二、如何获取TelephonyManager的服务
@ContactsProvider2.java
boolean isPhone() {
if (!mIsPhoneInitialized) {
//创建TelephonyManager对象,并调用isVoiceCapable()方法
mIsPhone = new TelephonyManager(getContext()).isVoiceCapable();
mIsPhoneInitialized = true;
}
return mIsPhone;
}
或者通过TelephonyManager的getDefault()方法来获取TelephonyManager对象:
@TelephonyManager.java
private static TelephonyManager sInstance = new TelephonyManager();
public static TelephonyManager getDefault() {
return sInstance;
}
如果调用者不是系统应用的话,如何获取他的服务呢?
这里就要介绍TelephonyManager的注册过程了。
ContextImpl在初始化时注册了一些常用的Service,其中就包括TelephonyManager:
@ContextImpl.java
registerService(TELEPHONY_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
return new TelephonyManager(ctx.getOuterContext());
}});
经过这样的注册,其他进程就可以通过Context对象的getSystemService()方法来获取其服务,比如:
TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
或者直接调用TelephonyManager的from()方法获取:
@TelephonyManager.java
public static TelephonyManager from(Context context) {
return (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
}
三、TelephonyManager的内部机制
首先我们来看TelephonyManager的继承关系:
public class TelephonyManager {}
看来TelephonyManager并没有继承任何的父类,那么他是如何实现各项功能的呢?
原来, 在TelephonyManager内部,获取到了三个Service的客户端,其中构造函数中获取了TelephonyRegistry的服务:
public TelephonyManager(Context context) {
Context appContext = context.getApplicationContext();
if (appContext != null) {
mContext = appContext;
} else {
mContext = context;
}
if (sRegistry == null) {
//获取TelephonyRegistry的服务
sRegistry = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService( "telephony.registry"));
}
}
然后通过getSubscriberInfo()获取了PhoneSubInfoProxy的服务:
private IPhoneSubInfo getSubscriberInfo() {
//获取的是PhoneSubInfoProxy的服务
return IPhoneSubInfo.Stub.asInterface(ServiceManager.getService("iphonesubinfo"));
}
以及通过getITelephony()获取了PhoneInterfaceManager的服务:
private ITelephony getITelephony() {
return ITelephony.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_SERVICE));
}
TelephonyManager拿到这三个Service之后,就用三个Service提供的服务,以及SystemProperties提供的属性,搭建了自己的public方法集合。
也就是说,TelephonyManager自己并不具备处理事务的能力,而是汇集了其他三个Service的功能,向他的所有请求者提供便利的处理Telephony事务的能力。
下面我们来看通过这三个Service分别扩展了那些功能:
3.1、TelephonyRegistry扩展的方法
也就是说,所有获取TelephonyManager的进程,都可以通过其listen()方法实现Radio状态的监听,TelephonyManager会把监听的请求转发给TelephonyRegistry处理。
@TelephonyManager.java
public void listen(PhoneStateListener listener, int events) {
String pkgForDebug = sContext != null ? sContext.getPackageName() : "<unknown>";
try {
Boolean notifyNow = (getITelephony() != null);
//通过TelephonyRegistry注册监听器
sRegistry.listen(pkgForDebug, listener.callback, events, notifyNow);
} catch (RemoteException ex) {
} catch (NullPointerException ex) {
}
}
3.2、PhoneSubInfoProxy扩展的方法
//得到软件版本信息
public String getDeviceSoftwareVersion() {
return getSubscriberInfo().getDeviceSvn();
}
//得到设备ID
public String getDeviceId() {
return getSubscriberInfo().getDeviceId();
}
//得到SIM卡串号
public String getSimSerialNumber() {
return getSubscriberInfo().getIccSerialNumber();
}
//得到语音信箱
public String getVoiceMailNumber() {
return getSubscriberInfo().getVoiceMailNumber();
}
3.3、PhoneInterfaceManager扩展的方法
//得到位置信息
public CellLocation getCellLocation() {
Bundle bundle = getITelephony().getCellLocation();
if (bundle.isEmpty()) return null;
CellLocation cl = CellLocation.newFromBundle(bundle);
return cl;
}
//得到附近小区信息
public List<NeighboringCellInfo> getNeighboringCellInfo() {
return getITelephony().getNeighboringCellInfo();
}
//得到当前Phone状态
public int getCurrentPhoneType() {
ITelephony telephony = getITelephony();
if (telephony != null) {
return telephony.getActivePhoneType();
} else {
return getPhoneTypeFromProperty();
}
}
//判断是否有SIM卡插入
public boolean hasIccCard() {
return getITelephony().hasIccCard();
}
四、TelephonyManager小结
假如现在有3个模块A、B、C,都需要做一些Phone有关的操作,他么的需求如下:
A模块:需要用到TelephonyRegistry和PhoneSubInfoProxy的服务,那么他就要去分别申请这两个服务的代理对象;
B模块:需要用到TelephonyRegistry和PhoneInterfaceManager服务,他也需要分别申请代理对象。
C模块:需要用到上面的3个服务,那么就需要申请3个代理对象。
对于这样的情况,我们当然可以在每个需要的模块内部分别调用系统接口(ServiceManager.getService)去得到相应的代理对象。这种情况下我们需要调用7次getService方法得到7个SystemService的远程对象。
如果通过TelephonyRegistry的方式去实现呢?
此时我们只需要在3个模块中,分别调用Context的getSystemService方法就能同时得到3个SystemService远程代理对象。而且我们得到的3个TelephonyManager对象是同一个对象,3个模块公用了同一个SystemService。因此,我们实际上只调用了3此getService方法,得到了3个SystemService远程对象。
这个例子说明, TelephonyManager整合3个SystemService的意义就在于减轻系统负担,特别是一些SystemService的负担,提高了效率。
既然TelephonyManager大大减轻了一些SystemService的负担,为什么只整合了3个SystemService呢?或者说,为什么选中了这3个SystemService来整合呢?
我们再来梳理以下TelephonyManager的运行原理。经过TelephonyManager的整合,当我们通过Context去得到TelephonyManager对象时,得到的是同一个TelephonyManager对象,那么我们进一步得到的SystemService也是同一个,此时我们调用TelephonyManager中的方法时,得到的返回值也是完全相同的。
这就说明了,TelephonyManager整合的SystemService,有一个共同特点:这些服务无论谁去调用,方法的返回值都是相同的。比如SIM卡的状态、当前的运营商信息、设备的ID号等。
而对于存在差异的SystemService,由于对于不同的客户端需要返回不同的值,当然就无法放到TelephonyManager中处理了。