已经更新至个人blog:http://dxjia.cn/2015/07/android-5-0-phone-init-analysis/

persistent属性

要想了解phone的框架,首先需要了解android app的persistent属性。在AndroidManifest.xml定义中,application有这么一个属性android:persistent,被android:persistent=”true”修饰的应用会在系统启动之后被AM(ActivityManagerService)启动。

AM首先在systemready后去PM(PackageManagerService)中查找设置了android:persistent的应用,代码如下:

  1. public void systemReady(final Runnable goingCallback) {
  2. ............
  3.  
  4. synchronized (this) {
  5. if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
  6. try {
  7. List apps = AppGlobals.getPackageManager().
  8. getPersistentApplications(STOCK_PM_FLAGS);
  9. if (apps != null) {
  10. int N = apps.size();
  11. int i;
  12. for (i=0; i<N; i++) {
  13. ApplicationInfo info
  14. = (ApplicationInfo)apps.get(i);
  15. if (info != null &&
  16. !info.packageName.equals("android")) {
  17. addAppLocked(info, false, null /* ABI override */);
  18. }
  19. }
  20. }
  21. } catch (RemoteException ex) {
  22. // pm is in same process, this will never happen.
  23. }
  24. }
  25. ........
  26.  
  27. }

addAppLocked方法会检测应用是否有起来,如果没有将启动,这样persist属性的应用就跑起来了。注意上面的判断

!info.packageName.equals("android")

因为name为android的persist app是 framework-res,所以排除在外。

Android5.0中有persistent=true的模块有下面几个:

  1. ./packages/services/Telecomm/AndroidManifest.xml: android:persistent="true"
  2.  
  3. ./packages/services/Telephony/AndroidManifest.xml: android:persistent="true"
  4.  
  5. ./packages/apps/Nfc/AndroidManifest.xml: android:persistent="true"
  6.  
  7. ./frameworks/base/packages/FakeOemFeatures/AndroidManifest.xml: android:persistent="true"
  8.  
  9. ./frameworks/base/packages/Keyguard/AndroidManifest.xml: android:persistent="true"
  10.  
  11. ./frameworks/base/packages/SystemUI/AndroidManifest.xml: android:persistent="true"
  12.  
  13. ./frameworks/base/core/res/AndroidManifest.xml: android:persistent="true"
  14.  
  15. ./hardware/intel/common/utils/ituxd/AndroidManifest.xml: <application android:name=".ituxdApp" android:persistent="true">

跟Telephony框架有关的就是前面两个,Telecom和Telephony,这两个模块的代码位置及在手机中实际编译出的apk如下:

  1. ./packages/services/Telecom priv-app/Telecom.apk
  2.  
  3. ./packages/services/Telephony priv-app/Teleservice.apk

Teleservice模块包含全部的PhoneApp及整个Phone框架,而Telecom主要是一些call相关的receiver、activity以及service等,看起来google是想把应用与Phone框架分的开一些,原先都是在一起的,只是从目前5.0的代码来看,还只是弄了一小部分,估计是在进行中,可能后续版本这里还会有变化。

Phone框架/PhoneApp

从Teleservice模块的manifest.xml里可以看出其实他目前还是叫做PhoneApp,这主要是因为之前一直以来Phone框架跟phone app是揉在一起的,估计等以后版本将Telecom和Telephony分的更开的时候,这里就不会叫这个名字啦。

  1. <application android:name="PhoneApp"
  2.  
  3. android:persistent="true"
  4.  
  5. android:label="@string/phoneAppLabel"
  6.  
  7. android:icon="@mipmap/ic_launcher_phone"
  8.  
  9. android:allowBackup="false"
  10.  
  11. android:supportsRtl="true">

我们先从PhoneApp的启动看起

PhoneApp.java

(packages\services\telephony\src\com\android\phone)

  1. public void onCreate() {
  2.  
  3. if (UserHandle.myUserId() == 0) {
  4.  
  5. // We are running as the primary user, so should bring up the
  6.  
  7. // global phone state.
  8.  
  9. mPhoneGlobals = new PhoneGlobals(this);
  10.  
  11. mPhoneGlobals.onCreate();
  12.  
  13. mTelephonyGlobals = new TelephonyGlobals(this);
  14.  
  15. mTelephonyGlobals.onCreate();
  16.  
  17. }
  18.  
  19. }

TelephonyGlobals 是5.0新增的,初步来看是跟账户控制以及tty有关的。后面再来研究他的作用,先看PhoneGloabals。

PhoneGloabals

PhoneGlobals继承ContextWrapper,而且还是单例,提供全局性的信息,包含的信息很多,下面是其内部管理的关键实例,这些实例相互关联才构建其手机的通信功能。

private static PhoneGlobals sMe;

CallController callController;

CallManager mCM;

CallNotifier notifier;

CallerInfoCache callerInfoCache;

NotificationMgr notificationMgr;

Phone phone;

PhoneInterfaceManager phoneMgr;

看其被PhoneApp.java调用的onCreate函数。

创建Phones

  1. // Initialize the telephony framework
  2.  
  3. PhoneFactory.makeDefaultPhones(this);

使用工厂模式,创建phones,PhoneFactory提供的都是static方法,所有都是直接静态调用。

首先看看PhoneFactory维护的本地变量:

  1. static private PhoneProxy[] sProxyPhones = null;
  2.  
  3. static private PhoneProxy sProxyPhone = null;
  4.  
  5. static private CommandsInterface[] sCommandsInterfaces = null;
  6.  
  7. static private ProxyController mProxyController;
  8.  
  9. static private UiccController mUiccController;
  10.  
  11. static private CommandsInterface sCommandsInterface = null;
  12.  
  13. static private SubInfoRecordUpdater sSubInfoRecordUpdater = null;
  14.  
  15. static private boolean sMadeDefaults = false;
  16.  
  17. static private PhoneNotifier sPhoneNotifier;

可以注意到黄色部分,跟以往的版本比较,已经变成了数组了,这是因为google开始支持双卡啦。。。啦啦啦。。。

1、             等待底层socket就绪,也就是init创建rild的socket完成;使用for循环+sleep的方式;

2、             创建PhoneNotifier以及获取network mode和cdma subscription;

3、             获取Phone个数设置

/* In case of multi SIM mode two instances of PhoneProxy, RIL are created,

where as in single SIM mode only instance. isMultiSimEnabled() function checks

whether it is single SIM or multi SIM mode */

int numPhones = TelephonyManager.getDefault().getPhoneCount();

int[] networkModes = new int[numPhones];

sProxyPhones = new PhoneProxy[numPhones];

sCommandsInterfaces = new RIL[numPhones];

getPhoneCount()从setting设置中取

  1. /**
  2.  
  3. * Returns the multi SIM variant
  4.  
  5. * Returns DSDS for Dual SIM Dual Standby
  6.  
  7. * Returns DSDA for Dual SIM Dual Active
  8.  
  9. * Returns TSTS for Triple SIM Triple Standby
  10.  
  11. * Returns UNKNOWN for others
  12.  
  13. */
  14.  
  15. /** {@hide} */
  16.  
  17. public MultiSimVariants getMultiSimConfiguration() {
  18.  
  19. String mSimConfig =
  20.  
  21. SystemProperties.get(TelephonyProperties.PROPERTY_MULTI_SIM_CONFIG);
  22.  
  23. if (mSimConfig.equals("dsds")) {
  24.  
  25. return MultiSimVariants.DSDS;
  26.  
  27. } else if (mSimConfig.equals("dsda")) {
  28.  
  29. return MultiSimVariants.DSDA;
  30.  
  31. } else if (mSimConfig.equals("tsts")) {
  32.  
  33. return MultiSimVariants.TSTS;
  34.  
  35. } else {
  36.  
  37. return MultiSimVariants.UNKNOWN;
  38.  
  39. }
  40.  
  41. }

4、             创建对应数量的RIL实例

  1. for (int i = 0; i < numPhones; i++) {
  2.  
  3. //reads the system properties and makes commandsinterface
  4.  
  5. sCommandsInterfaces[i] = new RIL(context, networkModes[i], cdmaSubscription, i);
  6.  
  7. }

5、             初始化 SubscriptionController 和UiccControler,这两个都是单例设计,这里创建好后,别的地方只需要getInstance进行调用

6、             创建Phone实例并保存

  1. for (int i = 0; i < numPhones; i++) {
  2.  
  3. PhoneBase phone = null;
  4.  
  5. int phoneType = TelephonyManager.getPhoneType(networkModes[i]);
  6.  
  7. if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
  8.  
  9. phone = new GSMPhone(context,
  10.  
  11. sCommandsInterfaces[i], sPhoneNotifier, i);
  12.  
  13. } else if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
  14.  
  15. phone = new CDMALTEPhone(context,
  16.  
  17. sCommandsInterfaces[i], sPhoneNotifier, i);
  18.  
  19. }
  20.  
  21. Rlog.i(LOG_TAG, "Creating Phone with type = " + phoneType + " sub = " + i);
  22.  
  23. sProxyPhones[i] = new PhoneProxy(phone);
  24.  
  25. }

7、             创建ProxyControler

mProxyController = ProxyController.getInstance(context, sProxyPhones,

mUiccController, sCommandsInterfaces);

ProxyController也是5.0新增的,其作用是进行双卡控制,内部实例化对icccard dct(data connction) phonebook 以及sms这些跟卡关系比较密切的功能,如下:

  1. mDctController = DctController.makeDctController((PhoneProxy[])phoneProxy);
  2.  
  3. mUiccPhoneBookController = new UiccPhoneBookController(mProxyPhones);
  4.  
  5. mPhoneSubInfoController = new PhoneSubInfoController(mProxyPhones);
  6.  
  7. mUiccSmsController = new UiccSmsController(mProxyPhones);

这些实例化的功能,会在各自的构造函数中将接口addService到servicemanager以便供APP调用。实现机制为binder.

比如UiccSmsController,这里是单实例的,但其内部接口使用带subid参数的方式来支持双卡

8、             获取默认SMS应用

主动调用一次 SmsApplication.getDefaultSmsApplication(context, true );注意第二个参数为true,也就是如果手机没有获取到默认sms app,那么会尝试去设定一个。设定的规则如下:

l  首先尝试从用户指定的默认app,对应的系统setting key为:sms_default_application;

l  其次看是否有goole的官方 默认sms app;

l  如果以上两个都没有,那么就从PM中获取所有注册有完整sms有关的broadcast receiver的app,从中找一个优先级最高的,并将其设定为default app。

9、             监控短信DefaultApp的变动

10、          监控Subscription的变化,跟卡有关,用来控制整个FW层对双卡的区分。相关的几个类是:SubscriptionManager  SubscriptionController  SubInfoRecordUpdater,管理default subid等,其default的策略是第一个检测到的可用卡id;当然接口是Public的,也就是暴漏出来的,是可以在需要的地方进行setDefault来改变这个设定的,这些值最终都保存在setting数据库中。

至此,PhoneFactory.makeDefaultPhones完成,接下来再回到PhoneGlobals...

Default Phone

  1. // Get the default phone
  2.  
  3. phone = PhoneFactory.getDefaultPhone();

上面创建完phones之后,接下来就取出一个defaultphone,这里说明一下default phone的设定,第一次在PhoneFactory中创建出phones之后,将实例保存在数组里,

  1. static private PhoneProxy[] sProxyPhones = null; //用于保存创建的Phones
  2.  
  3. static private PhoneProxy sProxyPhone = null; //用于保存default phone

sProxyPhone的第一次赋值只是简单的取 sProxyPhones[0],但PhoneFactory提供了接口,可以对这个default phone进行设定,接口为:setDefaultSubscription(int subId);当然其内部会根据双模的具体情况进行决策,比如如果是双模单通(同一时间只有一个active),那么default phone会自动设定成那个active的,如果是双模双通,那么就设定成参数值int subId(当然会将subid转换为对应的Phoneid,也就是sProxyPhones数组下标)。

创建关键实例

接下来在PhoneGlobals中会创建很多关键实例,依次是:

  1. CallManager
  2.  
  3. NotificationMgr
  4.  
  5. PowerManager
  6.  
  7. KeyguardManager
  8.  
  9. CallLogger
  10.  
  11. CallGatewayManager
  12.  
  13. CallController
  14.  
  15. CallerInfoCache
  16.  
  17. BluetoothManager
  18.  
  19. PhoneInterfaceManager
  20.  
  21. CallNotifier

双模通道打通

上面的初始化过程结束之后,其实已经对各模建立起来各自的通道,尽管5.0还没有彻底支持完整,还是以一个例子来描述流程:

PhoneInterfaceManager是一个servcie,App可以远程访问其内部的接口,以下面这个接口为例,其实现中提供了一些以subId作为参数的接口,subId是long型,你可以把它看作是卡的身份标识,具体获得过程以后再分析。

public void toggleRadioOnOffForSubscriber(long subId) {

enforceModifyPermission();

getPhone(subId).setRadioPower(!isRadioOnForSubscriber(subId));

}

GetPhone(subId)会获取到对应的Phone实例,并调用对应phone实例的setRadioPower接口,可惜这里5.0还没有进行实现,只是简单的return defaultPhone

// returns phone associated with the subId.

// getPhone(0) returns default phone in single SIM mode.

private Phone getPhone(long subId) {

// FIXME: hack for the moment

return mPhone;

// return PhoneUtils.getPhoneForSubscriber(subId);

}

前面说过了,会根据phone的个数创建对应数量的RILJ实例,也就是CommandInterface实例,并传给具体的Phone实例,CDMAPhone或GsmPhone,而各自的RILJ实例在初始化的时候,又会自动链接上RILD的对应socket[RILD由init进程启动,并根据init.rc里的设定创建对应的socket],那么各自的phone跟底层modem的通信就建立了。

Android 5.0 Phone初始化分析的更多相关文章

  1. Android编译系统环境过程初始化分析【转】

    本文转载自:http://blog.csdn.net/luoshengyang/article/details/18928789 Android源代码在编译之前,要先对编译环境进行初始化,其中最主要就 ...

  2. Android 4.0 Camera架构分析之Camera初始化

    Android Camera 采用C/S架构,client 与server两个独立的线程之间使用Binder通信,这已经是众所周知的了.这里将介绍Camera从设备开机,到进入相机应用是如何完成初始化 ...

  3. Android 5.0 双卡信息管理分析

    首先,如前面的博文所讲的,Android5.0开始支持双卡了.另外,对于双卡的卡信息的管理,也有了实现,尽管还不是完全彻底完整,如卡的slot id, display name,iccid,color ...

  4. Android 9.0 关机流程分析

    极力推荐文章:欢迎收藏 Android 干货分享 阅读五分钟,每日十点,和您一起终身学习,这里是程序员Android 本篇文章主要介绍 Android 开发中的部分知识点,通过阅读本篇文章,您将收获以 ...

  5. Android 5.0 Uicc框架分析

    已同步更新至个人blog:   dxjia.cn Uicc框架 UICC框架是Android在4.1引入的,使的对卡的管理控制更加清晰.要了解这个UICC框架,需要从UiccController开始, ...

  6. Android 8.1 源码_启动篇(二) -- 深入研究 zygote(转 Android 9.0 分析)

    前言 在Android中,zygote是整个系统创建新进程的核心进程.zygote进程在内部会先启动Dalvik虚拟机,继而加载一些必要的系统资源和系统类,最后进入一种监听状态.在之后的运作中,当其他 ...

  7. Android 7.0 启动篇 — init原理(二)(转 Android 9.0 分析)

    ========================================================          ================================== ...

  8. Android 5.0 怎样正确启用isLoggable(二)__原理分析

    前置文章 <Android 5.0 怎样正确启用isLoggable(一)__使用具体解释> 概要 在上文<Android 5.0 怎样正确启用isLoggable(一)__使用具体 ...

  9. 对Android 8.0以上版本通知点击无效的一次分析

    版权声明:本文为xing_star原创文章,转载请注明出处! 本文同步自http://javaexception.com/archives/178 对Android 8.0以上版本通知点击无效的一次分 ...

随机推荐

  1. 潭州课堂25班:Ph201805201 第十二课 new方法,定制属性访问,描述符与装饰器 (课堂笔记)

    1,new方法: 类每次实例化时都会创建一个新的对象, class Textcls: # cls 是指类本身, def __new__(cls, *args, **kwargs): # 在 __ini ...

  2. 闪烁的LED灯

    /* Main.c file generated by New Project wizard * * Created: 周五 五月 5 2017 * Processor: 80C31 * Compil ...

  3. apache hbase 发布1.0.0版本

    今天apache发布了最新的hbase 1.0.0,下图是版本变迁历史: 详情参考: https://blogs.apache.org/hbase/entry/start_of_a_new_era

  4. IIS远程发布(Web Deploy)

    作为开发人员,我们之前发布应用很可能是拷贝开发环境上发布好的代码文件到应用服务器硬盘中,然后在IIS中部署网站. 但是今天我们讲的是如果直接在我们的开发环境通过VS远程发布网站到应用服务器上,这将极大 ...

  5. 关于RabbitMQ关键性问题的总结

    摘要:本篇是本人对RabbitMQ使用的关键性问题进行的总结,如性能上限.数据存储.集群等, 具体的RabbitMQ概念.安装.使用方法.SpringAMQP配置,假设读者已有了基础. 1.      ...

  6. 在AngularJS中实现一个延迟加载的Directive

    所谓的延迟加载通常是:直到用户交互时才加载.如何实现延迟加载呢? 需要搞清楚三个方面: 1.html元素的哪个属性需要延迟加载?2.需要对数据源的哪个字段进行延迟加载?3.通过什么事件来触发延迟加载? ...

  7. Directx11代码下载

    很多年前的代码,看还有朋友需要,上传到百度网盘了 https://pan.baidu.com/s/1pnGFt84htvdXeK86pvyR8Q https://pan.baidu.com/s/1zT ...

  8. iOS开发-NSDate获取当前时区时间

    NSDate Date默认显示的是格林尼治所在地的标准时间(GMT),转换为中国时区需要加上八个小时,针对与这个情况你可以直接在获取时间之后加上八个小时,也可以转换到当前时区,都很简单,代码参考如下: ...

  9. python的匿名函数lambda解释及用法

    lambda函数的语法只包含一个语句,如下:    lambda arg1,arg2,.....argn:expression(主要是看下面的例子)代码示例: #-*- coding:utf-8 -* ...

  10. spark run using IDE / Maven

    来自:http://stackoverflow.com/questions/26892389/org-apache-spark-sparkexception-job-aborted-due-to-st ...