[置顶] Android Telephony Phone详解

前言

本文主要讲解Telephony中Phone相关的知识,主要想讲明白三件事情:

  1. Phone是什么?
  2. Phone从哪里来?
  3. Phone有什么作用?

1. Phone是什么

1.1 Phone是一个接口

Phone.java (frameworks\opt\telephony\src\java\com\android\internal\telephony)

    public interface Phone {
    //包含了大量的register/unregister的方法。(监听能力)
    void registerForXXX(Handler h, int what, Object obj);
    void unregisterForXXX(Handler h);
    //大量的get/set的方法。(读取和修改的能力)
    //定义Telephony基础功能的方法(如dial、acceptCall等)
    }

http://blog.csdn.net/linyongan


1.2 Phone家族

这里写图片描述

由上面类图可以看出,PhoneBase是处于一个最重要最关键的位置,是所有关系的中心枢纽。

public abstract class PhoneBase extends Handler implements Phone {

PhoneBase只实现了Phone接口中部分的抽象方法,所以PhoneBase也还是抽象类,它本质上是Handler。
我们常常用到的GSMPhone、CDMAPhone等继承于PhoneBase,并且实现了Phone接口中还未被PhoneBase实现的抽象方法,同样,PhoneBase的子类也都是Handler。

至于PhoneProxy,它是一个代理对象:

    public PhoneProxy(PhoneBase phone) {
        //得到PhoneBase对象
        mActivePhone = phone;
        ......
    }
    //举个例子:
    @Override
    public String getPhoneName() {
        //直接调PhoneBase对象或者它子类的同名方法
        return mActivePhone.getPhoneName();
    }

代理的好处是安全,我在PhoneProxy中定义了哪几个方法,你就只能调用那几个方法,而不会给你得到PhoneBase或者其子类对象,进而操纵所有东西;还有就是对外接口一致,调用方式一致,不用区分是GSMPhone?还是CDMAPhone?或者是IMSPhone?


2. Phone从哪里来

这里写图片描述
(备注:上面时序图中的是谷歌原生的流程,跟高通的有点不一样。)
Android中有三种PhoneFactory:
PhoneFactory.java ——–>用于创建GSMPhone、CDMAPhone、CDMALTEPhone对象;
ImsPhoneFactory.java ——–>用于创建ImsPhone对象;
SipPhoneFactory.java ——–>用于创建SipPhone对象。
其中,GSMPhone、CDMAPhone、CDMALTEPhone对象是在Phone进程启动之后创建的(步骤1~7);
之后,等到ImsService启动之后,就会创建ImsPhone(步骤8~13)。

2.1 Phone进程的启动

在Android中进程名一般对应的是该APP的包名,所以我们可以在源码中找package=”com.android.phone”。
接着你就会在/packages/services/Telephony/AndroidManifest.xml文件中看到:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
        package="com.android.phone"
        coreApp="true"
        android:sharedUserId="android.uid.phone"
        android:sharedUserLabel="@string/phoneAppLabel"
>

再往下翻翻,你就会看到application的名字是PhoneApp,application是最早被创建的,所以PhoneApp.java就是Phone进程启动的入口。

    <application android:name="PhoneApp"
                 //在系统启动之时,ActivityManagerService的systemReady()
                 //会加载所有persistent为true的应用
                 android:persistent="true"
                 android:label="@string/phoneAppLabel"
                 android:icon="@mipmap/ic_launcher_phone"
                 android:allowBackup="false"
                 android:supportsRtl="true"
                 android:usesCleartextTraffic="true">

2.2 Phone对象的初始化

为了创建Phone对象,为了实现Phone接口中所有的抽象方法,有两个问题需要考虑:

  1. 创建Phone对象前,需要先准备什么?
  2. 创建Phone对象后,如何让Phone对象正常工作?

2.2.1 创建Phone对象前,需要先准备什么

我们回过头继续看Phone接口:

    public interface Phone {
    //包含了大量的register/unregister的方法。
    void registerForXXX(Handler h, int what, Object obj);
    void unregisterForXXX(Handler h);
    //大量的get/set的方法。
    //定义Telephony基础功能的方法(如dial、acceptCall等)
    }

所以,Phone对象必须拥有的能力:与RIL交互的能力(为了实现Telephony基础功能和大量的get/set的方法),通知上层APP的能力(监听到事件之后,需要继续上报该消息)。
如何拥有这两种能力?最简单的方法当然把拥有这两种能力的对象传递进来!
看看PhoneBase参数最少的构造方法:

    protected PhoneBase(String name, PhoneNotifier notifier, Context context, CommandsInterface ci) {
        this(name, notifier, context, ci, false);
    }

可以看出,创建GSMPhone、CDMAPhone、ImsPhone等PhoneBase子类对象时,需要先得到两个比较重要的对象:DefaultPhoneNotifier(实现了PhoneNotifier接口中大量notify的方法)和RIL(实现了CommandsInterface,RILJ拥有跟Qcril、modem交互的能力)。

GSMPhone/CDMALTEPhone具体的创建过程:
在PhoneFactory.java的makeDefaultPhone方法中(时序图中的步骤3)

    public static void makeDefaultPhone(Context context) {
        ......
        //创建DefaultPhoneNotifier对象。
        sPhoneNotifier = new DefaultPhoneNotifier();

        //根据待机模式计算出要创建Phone对象的数量
        int numPhones = TelephonyManager.getDefault().getPhoneCount();
        //创建networkMode、PhoneProxy、RIL的数组,用于存储对应的对象
        int[] networkModes = new int[numPhones];
        sProxyPhones = new PhoneProxy[numPhones];
        sCommandsInterfaces = new RIL[numPhones];

        for (int i = 0; i < numPhones; i++) {
            // reads the system properties and makes commandsinterface
            // Get preferred network type.
            networkModes[i] = RILConstants.PREFERRED_NETWORK_MODE;
            Rlog.i(LOG_TAG, "Network Mode set to " + Integer.toString(networkModes[i]));
            //创建RIL,此时的i对应的是PhoneID。
            sCommandsInterfaces[i] = new RIL(context, networkModes[i],
                            cdmaSubscription, i);
        }
        ......
        for (int i = 0; i < numPhones; i++) {
            PhoneBase phone = null;
            //先得到Phone的类型
            int phoneType = TelephonyManager.getPhoneType(networkModes[i]);
            //根据不用的类型,创建不同的Phone对象
            if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
                phone = new GSMPhone(context,
                        sCommandsInterfaces[i], sPhoneNotifier, i);
            } else if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
                phone = new CDMALTEPhone(context,
                        sCommandsInterfaces[i], sPhoneNotifier, i);
            }
            Rlog.i(LOG_TAG, "Creating Phone with type = " + phoneType + " sub = " + i);
            //创建Phone对象后也要对应地创建一个PhoneProxy对象
            sProxyPhones[i] = new PhoneProxy(phone);
        }
        .....
        // Start monitoring after defaults have been made.
        // Default phone must be ready before ImsPhone is created
        // because ImsService might need it when it is being opened.
        for (int i = 0; i < numPhones; i++) {
            //在方法的最后,开始监听ImsService
            //如果ImsService已启动,进而执行创建ImsPhone对象
            sProxyPhones[i].startMonitoringImsService();
        }
    }

2.2.2 创建Phone对象后,如何让它正常工作

从Phone接口就可以看到,每一个Phone对象都拥有大量的register方法,所以每创建一个Phone对象之后,都用把Phone对象传递进CallManager.java的registerPhone()方法中。
PhoneGlobals.java的onCreate()方法中

    public void onCreate() {
        if (mCM == null) {
            // Initialize the telephony framework
            //先创建Phone对象
            PhoneFactory.makeDefaultPhones(this);
            mCM = CallManager.getInstance();
            for (Phone phone : PhoneFactory.getPhones()) {
                //把新创建的Phone对象传递进来
                mCM.registerPhone(phone);
            }
        }
    }

由CallManager来管理这些Phone对象并且为它们注册监听事件(步骤7和13)。

    // list of registered phones, which are PhoneBase objs
    private final ArrayList<Phone> mPhones;

    public boolean registerPhone(Phone phone) {
        Phone basePhone = getPhoneBase(phone);

        if (basePhone != null && !mPhones.contains(basePhone)) {

            if (DBG) {
                Rlog.d(LOG_TAG, "registerPhone(" +
                        phone.getPhoneName() + " " + phone + ")");
            }

            if (mPhones.isEmpty()) {
                mDefaultPhone = basePhone;
            }
            //管理Phone对象
            mPhones.add(basePhone);
            mRingingCalls.add(basePhone.getRingingCall());
            mBackgroundCalls.add(basePhone.getBackgroundCall());
            mForegroundCalls.add(basePhone.getForegroundCall());
            //为Phone对象注册监听事件
            registerForPhoneStates(basePhone);
            return true;
        }
        return false;
    }

    private void registerForPhoneStates(Phone phone) {
        ......
        phone.registerForDisconnect(handler, EVENT_DISCONNECT,mRegistrantidentifier);
        phone.registerForIncomingRing(handler, EVENT_INCOMING_RING,mRegistrantidentifier);
        ......
    }

3. Phone有什么作用

在第二小节已经讲了Phone拥有跟RIl交互的能力和上报消息的能力,所以Phone的作用就是:
1.及时上报消息给APP(Call状态变化、Service状态变化、新来电等等)

    void notifyPhoneStateChanged(){}
    void notifyNewRingingConnection(Connection c) {}
    void notifyDisconnect(Connection cn) {}
    void notifyUnknownConnection(Connection cn) {}
    void notifySuppServiceFailed(SuppService code) {}
    void notifyServiceStateChanged(ServiceState ss) {}
    void notifyLocationChanged() {}
    void notifyCallForwardingIndicator() {}
    ......

2.间接地为APP提供跟RIL交互的服务。

    public void getCallWaiting(Message onComplete) {}

    public void setCallWaiting(boolean enable, Message onComplete) {}
    ......

好了,本文就暂时写到这里。

本页内容版权归属为原作者,如有侵犯您的权益,请通知我们删除。

Android混淆心得 - 2016-07-23 14:07:36

最近在做Android应用的混淆,踩了一些坑,这里记录分享下个人的心得。 混淆介绍 首先先简单说一下什么是混淆和混淆的作用,其实这个搜索下可以找到一堆官方的说法等等,这里简单口语叙述一下,混淆就是把代码替换成a、b、c基本字母组成的代码,比如一个方法名为:function(),混淆后可能会被替换成a()。 混淆的好处: 代码混淆后阅读性降低,反编译后破译程序难度提高 混淆后字节数减少,减少了应用了体积 前者只能说有一点作用,后者则需要看代码的数量 当然不能忽视混淆的缺点: 混淆后,测试不充分可能导致某些功
[编写高质量iOS代码的52个有效方法](二)对象 参考书籍:《Effective Objective-C 2.0》 【英】 Matt Galloway 先睹为快 6.理解“属性”这一概念 7.在对象内部尽量直接访问实例变量 8.理解“对象等同性”这一概念 9.以“类簇模式”隐藏实现细节 10.在既有类中使用关联对象存放自定义数据 目录 编写高质量iOS代码的52个有效方法二对象 先睹为快 目录 第6条理解属性这一概念 第7条在对象内部尽量直接访问实例变量 第8条理解对象等同性这一概念 第9条以类簇模式隐
插件其实是Apk安装包,如果要使用必须先要安装和解析,以便知道插件Apk的相关信息。而从Demo中我们知道插件的安装和卸载是通过调用PluginManager的installPackage()和deletePackage()来实现的。就先从PluginManager.installPackage()开始分析插件Apk的安装过程。 第一步:PluginManager. getInstance().installPackage(apkPath,flag); 此函数中只是调用了mPluginManager.in
分享截屏已经是很多游戏应用必备的功能了,找到了一个国内的插件,虽然用起来还行,但是,还是想吐槽下,跟老外的插件比,真的有差距啊有差距啊有差距啊,啊啊啊。 ShareSDK的官方网站:http://www.mob.com/,使用插件需要注册账号获得key,不过,至少现在是免费的。 我的Unity版本是5.3,xcode版本是7.2,ShareSDK版本是2.7.4 Unity sdk下载地址:https://github.com/MobClub/New-Unity-For-ShareSDK 新建一个简单的工
正常情况下启动一个Activity,首先需要在AndroidManifest文件中声明,其次需要把该应用安装到手机系统中。 而插件apk是没有正在安装到手机系统中的,也就按照正常的启动流程插件Activity是不能启动的。另外插件apk的类需要加载进来是需要指定ClassLoader。前面的文章也大概讲过,当启动一个插件Activity时,先是用预定义的代理Activity替换目标Activity(及插件Activity)去启动,当AMS处理完回调到应用空间时(及回到运行Activity的进程空间时)再用
从DroidPlugin的官方文档中我们知道。 2 在AndroidManifest.xml中使用插件的com.morgoo.droidplugin.PluginApplication: 或者在自定义的Application的onCreate()函数中,调用PluginHelper.getInstance().applicationOnCreate(getBaseContext()); 在Application的attachBaseContext()函数中,调用 PluginHelper.getInsta

字母雨的实现 - 2016-07-23 14:07:06

有段时间没写博文了,前段时间比较忙,这几天闲下来,想着写点东西,脑袋一下就闪过以前学习Android的时候见到的别人实现的黑客帝国的字母雨效果,当时对于小菜鸟的自己,那叫一个膜拜啊,时隔几年,自己实现一下,算是对以前的自己一个交代吧。 先看效果: 一、实现原理 在实现过程中,主要考虑整个界面由若干个字母组成的子母线条组成,这样的话把固定数量的字母封装成一个字母线条,而每个字母又封装成一个对象,这样的话,就形成了如下组成效果: 字母对象--》字母线条对象--》界面效果 每个字母都应该知道自己的位置坐标,自己

DroidPlugin源码分析Hook过程 - 2016-07-23 14:07:06

插件运行环境初始化过程中我们知道,Hook的初始化是在PluginHelper的initPlugin函数中通过调用PluginProcessManager.installHook来实现的。而在分析DroidPlugin Hook过程之前需要先简单了解一下Java的动态代理。 Java动态代理与之相关的一个类Proxy,一个接口InvocationHandler,一个函数invoke他们之间的关系。就通过DroidPlugin 的BinderHook类的部分代码来解释一下他们的关系; abstract cl
大概半年之前,看过鸿洋大神的一篇博客 Android 自定义控件玩转字体变色 打造炫酷ViewPager指示器 他说大概想了32秒就知道了实现思路,这深深的刺痛了我。最近又看了一遍,决定做点什么 我要自定义的控件是一个盖世英雄, 它不仅仅是一个 Loading控件 ,同时还支持 进度条 (ProgressBar) 功能 。 它会在你需要的时候出现, 它支持 left , top , right , bottom 四个方向加载(变色),最重要的是,它可以是 文字 ,也可以是 图片 ,能够满足开发者一切需求。
首先为权限: uses-permission android:name="android.permission.INTERNET" / uses-permission android:name="com.android.vending.BILLING" / uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" / uses-permission android:name="android.permission.AC