前言

TelephonyManager主要提供Telephony相关信息的查询/修改功能,以及Phone状态监听功能,封装的方法主要是提供给APP上层使用。
TelephonyManager.java 在frameworks\base\telephony\java\android\telephony目录下。

1. TelephonyManager整体结构

从TelephonyManager导入的文件中可以发现有四个接口

  1. import com.android.internal.telecom.ITelecomService;
  2. import com.android.internal.telephony.IPhoneSubInfo;
  3. import com.android.internal.telephony.ITelephony;
  4. import com.android.internal.telephony.ITelephonyRegistry;

分别对应下面这几个AIDL接口:

  1. \frameworks\base\telecomm\java\com\android\internal\telecom\ITelecomService.aidl
  2. \frameworks\base\telephony\java\com\android\internal\telephony\IPhoneSubInfo.aidl
  3. \frameworks\base\telephony\java\com\android\internal\telephony\ITelephony.aidl
  4. \frameworks\base\telephony\java\com\android\internal\telephony\ITelephonyRegistry.aidl

以可以猜测到,在TelephonyManager中可以得到这四种Service。
通过所有文件中搜索”extends 接口名.Stub”,如”extends ITelephony.Stub”,可以找到是哪些类实现了上面四个AIDL接口中的方法,整理可得:

在TelephonyManager中搜索”接口名.Stub.asInterface”,如”ITelephony.Stub.asInterface”,可以找到这四个Service的名字,整理可得:

  1. private ITelecomService getTelecomService() {
  2. return ITelecomService.Stub.asInterface(ServiceManager.getService(TELECOM_SERVICE));
  3. }
  1. @UnsupportedAppUsage
  2. private IPhoneSubInfo getSubscriberInfo() {
  3. // get it each time because that process crashes a lot
  4. return IPhoneSubInfo.Stub.asInterface(ServiceManager.getService("iphonesubinfo"));
  5. }
  1. private ITelephony getITelephony() {
  2. return ITelephony.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_SERVICE));
  3. }
  1. private ITelephonyRegistry getTelephonyRegistry() {
  2. return ITelephonyRegistry.Stub.asInterface(ServiceManager.getService("telephony.registry"));
  3. }

好了,下面分别对这四种Service进行分析:

1.1 TelecomServiceImpl—Telecom Service(TelecomLoaderService.java注册)

服务端TelecomServiceImpl中有mBinderImpl实现了ITelecomService接口中的方法

  1. public class TelecomServiceImpl {
  2. private final ITelecomService.Stub mBinderImpl = new ITelecomService.Stub() {
  3. ...
  4. }
  5. }

在TelecomLoaderService.java中,TelecomServiceImpl把自己注册到ServiceManager中,

  1. ServiceManager.addService(Context.TELECOM_SERVICE, service);

所以在TelephonyManager中可以通过ServiceManager得到Telecom Service

  1. private ITelecomService getTelecomService() {
  2. //得到TelecomServiceImpl的代理对象
  3. return ITelecomService.Stub.asInterface(ServiceManager.getService(Context.TELECOM_SERVICE));
  4. }

其实Telecom Service的最常用客户端是TelecomManager.java。而在TelephonyManager中由于无法得到CallManager对象,所以只能依赖Telecom Service获取Call State。

  1. /**
  2. * Returns one of the following constants that represents the current state of all
  3. * phone calls.
  4. *
  5. * {@link TelephonyManager#CALL_STATE_RINGING}
  6. * {@link TelephonyManager#CALL_STATE_OFFHOOK}
  7. * {@link TelephonyManager#CALL_STATE_IDLE}
  8. */
  9. public int getCallState() {
  10. try {
  11. ITelecomService telecom = getTelecomService();
  12. if (telecom != null) {
  13. return telecom.getCallState();
  14. }
  15. } catch (RemoteException e) {
  16. Log.e(TAG, "Error calling ITelecomService#getCallState", e);
  17. }
  18. return CALL_STATE_IDLE;
  19. }

所以,总体上来说,虽然TelecomManager得到了Telecom Service,但其实作用不大。相反,Telecom Service中会反过来得到TelephonyManager对象,进一步实现自己的方法,如在TelecomServiceImpl.java中:

  1. public String getVoiceMailNumber(PhoneAccountHandle accountHandle, String callingPackage) {
  2. ...
  3. return getTelephonyManager().getVoiceMailNumber(subId);
  4. ...
  5. }
  6.  
  7. private TelephonyManager getTelephonyManager() {
  8. return (TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE);
  9. }

1.2 PhoneSubInfoController— “iphonesubinfo” Service(PhoneSubInfoController中注册)

服务端PhoneSubInfoController继承自IPhoneSubInfo.Stub

  1. public class PhoneSubInfoController extends IPhoneSubInfo.Stub {...}

在创建Default Phone对象之后,ProxyController对象在PhoneFactory.java的makeDefaultPhone()中被初始化

  1. public static void makeDefaultPhone(Context context) {
  2. ...
  3. //先初始化ProxyController
  4. mProxyController = ProxyController.getInstance(context, sProxyPhones,
  5. mUiccController, sCommandsInterfaces);
  6. ...
  7. }
  8.  
  9. private ProxyController(Context context, PhoneProxy[] phoneProxy, UiccController uiccController,
  10. CommandsInterface[] ci) {
  11. ...
  12. //在ProxyController的构造方法中初始化了PhoneSubInfoController对象
  13. mPhoneSubInfoController = new PhoneSubInfoController(mContext, mPhones);
  14. ...
  15. }
  16.  
  17. public PhoneSubInfoController(Context context, Phone[] phone) {
  18. mPhone = phone;
  19. if (ServiceManager.getService("iphonesubinfo") == null) {
  20. //将PhoneSubInfoController实例注册到ServiceManager中
  21. ServiceManager.addService("iphonesubinfo", this);
  22. }
  23. mContext = context;
  24. mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
  25. }

所以在TelephonyManager中可以通过ServiceManager得到”iphonesubinfo” Service

  1. private IPhoneSubInfo getSubscriberInfo() {
  2. // get it each time because that process crashes a lot
  3. return IPhoneSubInfo.Stub.asInterface(ServiceManager.getService("iphonesubinfo"));
  4. }

通过”iphonesubinfo” Service可以得到software version、deviceID、VoiceMail Number等信息,TelephonyManager在这里只是对这些方法进一步封装,这些方法具体的实现,最后还是通过Phone实例和IsimRecords实例来完成的。
以getMsisdn()方法为例,最常见的调用方式如下:

  1. PhoneInterfaceManager.init

备注:在Android N中已删除PhoneSubInfo.java和PhoneSubInfoProxy.java,所以流程变得简单了。

1.3 PhoneInterfaceManager—Telephony Service(PhoneInterfaceManager中注册)

TelephonyManager依赖Telephony Service实现了大部分的方法。
PhoneInterfaceManager继承自ITelephony.Stub

  1. public class PhoneInterfaceManager extends ITelephony.Stub {

PhoneInterfaceManager.java在 packages\services\telephony\src\com\android\phone目录下,显然它是运行在Phone进程中的。
在Phone进程启动时,Default Phone对象创建完之后,PhoneInterfaceManager对象在/packages/services/Telephony/src/com/android/phone/PhoneGlobals.java的onCreate()中被初始化:

  1. public void onCreate() {
  2. ...
  3. phoneMgr = PhoneInterfaceManager.init(this, PhoneFactory.getDefaultPhone());
  4. ...
  5. }
  6.  
  7. /* package */ static PhoneInterfaceManager init(PhoneGlobals app, Phone phone) {
  8. synchronized (PhoneInterfaceManager.class) {
  9. if (sInstance == null) {
  10. //初始化PhoneInterfaceManager
  11. sInstance = new PhoneInterfaceManager(app, phone);
  12. } else {
  13. Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance);
  14. }
  15. return sInstance;
  16. }
  17. }

在PhoneInterfaceManager的构造方法中:

  1. private PhoneInterfaceManager(PhoneGlobals app, Phone phone) {
  2. //得到一些关键类
  3. mApp = app;
  4. mPhone = phone;
  5. mCM = PhoneGlobals.getInstance().mCM;
  6. mUserManager = (UserManager) app.getSystemService(Context.USER_SERVICE);
  7. mAppOps = (AppOpsManager)app.getSystemService(Context.APP_OPS_SERVICE);
  8. mMainThreadHandler = new MainThreadHandler();
  9. mTelephonySharedPreferences =
  10. PreferenceManager.getDefaultSharedPreferences(mPhone.getContext());
  11. mSubscriptionController = SubscriptionController.getInstance();
  12.  
  13. publish();
  14. }
  15.  
  16. private void publish() {
  17. //将PhoneInterfaceManager实例注册到ServiceManager中
  18. ServiceManager.addService("phone", this);
  19. }

在PhoneInterfaceManager初始化的时候,把自己注册成SystemServer,这样客户端(如TelephonyManager)则可以通过ServiceManager把它取出来。

  1. private ITelephony getITelephony() {
  2. //得到PhoneInterfaceManager的代理对象
  3. return ITelephony.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_SERVICE));
  4. }

PhoneInterfaceManager中的方法,最后还是通过Phone实例来实现。
以isImsRegistered()方法为例,最常见的调用方式如下:

1.4 TelephonyRegistry—“telephony.registry” Service(SystemServer.java中注册)

TelephonyRegistry继承自ITelephonyRegistry.Stub

  1. class TelephonyRegistry extends ITelephonyRegistry.Stub {

在SystemServer.java中,

  1. telephonyRegistry = new TelephonyRegistry(context);
  2. //将TelephonyRegistry实例注册到ServiceManager中
  3. ServiceManager.addService("telephony.registry", telephonyRegistry);

所以在TelephonyManager中可以通过ServiceManager得到”telephony.registry” Service

  1. if (sRegistry == null) {
  2. sRegistry = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(
  3. "telephony.registry"));
  4. }

TelephonyManager主要利用”telephony.registry” Service实现listen()方法,实现对Phone状态的监听的功能

  1. public void listen(PhoneStateListener listener, int events) {
  2. if (mContext == null) return;
  3. try {
  4. Boolean notifyNow = (getITelephony() != null);
  5. sRegistry.listenForSubscriber(listener.mSubId, getOpPackageName(),
  6. listener.callback, events, notifyNow);
  7. } catch (RemoteException ex) {
  8. // system process dead
  9. } catch (NullPointerException ex) {
  10. // system process dead
  11. }
  12. }

关于TelephonyRegistry,后续的文章会详细讲,目前先不用太关注。

2. 如何得到TelephonyManager对象

1、 假如没有Context,可以通过:

  1. private static TelephonyManager sInstance = new TelephonyManager();
  2. public static TelephonyManager getDefault() {
  3. return sInstance;
  4. }

2、如果能得到Context对象,可以通过:

  1. //注意,这是从SystemService中取
  2. TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
  3. //或者
  4. TelephonyManager mTelephonyManager = TelephonyManager.from(context);

3. 其他重要方法

TelephonyManager还提供了两个其他比较重要的方法:

  1. /**
  2. * Gets the telephony property.
  3. *
  4. * @hide
  5. */
  6. public static String getTelephonyProperty(int phoneId, String property, String defaultVal) {
  7. String propVal = null;
  8. //根据key获取到value
  9. String prop = SystemProperties.get(property);
  10. if ((prop != null) && (prop.length() > 0)) {
  11. //将value分割成字符串数组
  12. String values[] = prop.split(",");
  13. if ((phoneId >= 0) && (phoneId < values.length) && (values[phoneId] != null)) {
  14. //取出phoneId对应的value
  15. propVal = values[phoneId];
  16. }
  17. }
  18. return propVal == null ? defaultVal : propVal;
  19. }
  20.  
  21. /**
  22. * Sets the telephony property with the value specified.
  23. *
  24. * @hide
  25. */
  26. public static void setTelephonyProperty(int phoneId, String property, String value) {
  27. ...
  28. }

这样子就可以实现对于同一个key,不同phoneId可以存储不同的值。

————————————————
版权声明:本文为CSDN博主「linyongan」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/linyongan/article/details/52104394

Android Telephony分析(四) ---- TelephonyManager详解的更多相关文章

  1. Android Telephony分析(五) ---- TelephonyRegistry详解

    本文紧接着上一篇文章<Android Telephony分析(四) —- TelephonyManager详解 >的1.4小节.从TelephonyRegistry的大部分方法中: 可以看 ...

  2. Android Telephony分析(三) ---- RILJ详解

    前言 本文主要讲解RILJ工作原理,以便更好地分析代码,分析业务的流程.这里说的RILJ指的是RIL.java (frameworks\opt\telephony\src\java\com\andro ...

  3. Android Telephony分析(二) ---- RegistrantList详解

    前言 本文主要讲解RegistrantList的原理,以及如何快速分析RegistrantList相关的代码流程.在Telephony模块中,在RIL.Tracker(ServiceStateTrac ...

  4. Android Telephony分析(一) ---- Phone详解

    目录: Phone的继承关系与PhoneFactory(GsmCdmaPhone.ImsPhone.SipPhone) Phone进程的启动 Phone对象的初始化(DefaultPhoneNotif ...

  5. Android Telephony分析(六) ---- 接口扩展(实践篇)

    本文将结合前面五篇文章所讲解的知识,综合起来,实现一个接口扩展的功能.如果还没有阅读过前面五篇文章的内容,请先阅读:<Android Telephony分析(一) — Phone详解 >& ...

  6. Android项目刮刮奖详解(四)

    Android项目刮刮奖详解(三) 前言 上一期我们已经是完成了刮刮卡的基本功能,本期就是给我们的项目增加个功能以及美化一番 目标 增加功能 用户刮卡刮到一定程度的时候,清除遮盖层 在遮盖层放张图片, ...

  7. Android Telephony分析(七) ---- 接口扩展(异步转同步)

    本文是基于上一篇<Android Telephony分析(六) —- 接口扩展(实践篇)>来写的.上一篇介绍的接口扩展的方法需要实现两部分代码:1. 从APP至RIL,发送请求:2. 从R ...

  8. [Android新手区] SQLite 操作详解--SQL语法

    该文章完全摘自转自:北大青鸟[Android新手区] SQLite 操作详解--SQL语法  :http://home.bdqn.cn/thread-49363-1-1.html SQLite库可以解 ...

  9. 【Android 应用开发】Ubuntu 下 Android Studio 开发工具使用详解 (旧版本 | 仅作参考)

    . 基本上可以导入项目开始使用了 ... . 作者 : 万境绝尘 转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/21035637 ...

随机推荐

  1. Github代码管理教程

    https://desktop.github.com/ 目录 Create and use a repository Start and manage a new branch Make change ...

  2. 关于python语言学习心得

    最近又开始学习代码了,中途停顿了2个月左右,学习贵在坚持. 内心非常渴望学会一门编程语言,对代码目前来说还不是排斥. 也没有什么好的学习方法,只是按照同事说的,买了一本书籍来,边看边练习,先熟悉它的语 ...

  3. 拾遗:Qemu/KVM

    WinXP: #!/bin/bash name=winxp qemu-system-x86_64 \ -enable-kvm \ -cpu host -smp ,sockets=,cores=,thr ...

  4. 20140702 赋值构造函数的形参为什么一定用引用。string类的赋值运算函数的注意点

    1.复制构造函数为什么一定要用引用,而不是用值 类名::复制构造函数(类名&引用名) 传递引用,可以避免复制,如果一个数据相当大的化,进行复制会浪费很多时间的. 类名::复制构造函数(类名 变 ...

  5. Redis数据结构之字符串-SDS

    C语言中,传统的字符串表示是以空字符结尾的字符数组,Redis的字符串没有直接使用该表示,而是选择构建了一种名为简单动态字符串(simple dynamic string, SDS)的抽象类型. 在R ...

  6. 两种接口传送数据协议(xml和json)

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/tianyazaiheruan/article/details/37659983    规范性接口开发 ...

  7. C# 获取系统开机时间

    原文:C# 获取系统开机时间 ///         ///  获取系统开机时间          ///         ///         private DateTime GetComput ...

  8. 结对编程项目报告--四则运算CORE

    <!doctype html> sw_lab2.mdhtml {overflow-x: initial !important;}#write, body { height: auto; } ...

  9. oracle中的round()方法的用法

    [oracle中的round()方法的用法] Round( ) 函数 传回一个数值,该数值是按照指定的小数位元数进行四舍五入运算的结果 oracle一般常用于计算表空间内存还有多少空间 语法 ROUN ...

  10. Keil5-建立第一个STM32工程

    此致:特别感谢作者Lomo-chen所写的文章给我的帮助,我尝试做了一下,成功了,今天整理一下. 一.建立文件夹: 1.在桌面或其他盘建立一个文件夹,此处名称为Test,用来存放工程程序. 2.在Te ...