已同步更新至个人blog:   dxjia.cn

Uicc框架

UICC框架是Android在4.1引入的,使的对卡的管理控制更加清晰。要了解这个UICC框架,需要从UiccController开始,它是整个UICC框架的开始与控制者,该类被设计为单例,是消息处理类Handler的子类,所以其实现肯定是基于event触发的,其在Phone创建的早期被初始化:

1

2

3

// Instantiate UiccController so that all other classes can just

// call getInstance()

mUiccController = UiccController.make(context, sCommandsInterfaces);

make函数只能被调用一次,以后如果要想获得UiccController对象,只能通过getInstance进行,来看UiccController的构造函数:

1

2

3

4

5

6

7

8

9

publicstatic UiccController make(Context c, CommandsInterface[] ci){

synchronized(mLock){

if(mInstance !=null){

thrownew RuntimeException("MSimUiccController.make() should only be called once");

}

mInstance =new UiccController(c, ci);

return(UiccController)mInstance;

}

}

private UiccController(Context c, CommandsInterface []ci){

if(DBG) log("Creating UiccController");

mContext = c;

mCis = ci;

for(int i =0; i < mCis.length; i++){

Integer index =new Integer(i);

mCis[i].registerForIccStatusChanged(this, EVENT_ICC_STATUS_CHANGED, index);

// TODO remove this once modem correctly notifies the unsols

mCis[i].registerForAvailable(this, EVENT_ICC_STATUS_CHANGED, index);

mCis[i].registerForNotAvailable(this, EVENT_RADIO_UNAVAILABLE, index);

}

}

CommandsInterface即为RILJ实例,这里保存下来就可以直接与RIL进行通信。与此同时,在每个RILJ实例上注册了3个事件,分别是

1

2

3

registerForIccStatusChanged(this,EVENT_ICC_STATUS_CHANGED, index);

registerForAvailable(this,EVENT_ICC_STATUS_CHANGED, index);

registerForNotAvailable(this,EVENT_RADIO_UNAVAILABLE, index);

这里可以看到增加了一个index参数,这个index这里就是指的phoneId,是对双卡的支持,是5.0新增的。增加了这个参数之后,EVENT_ICC_STATUS_CHANGED和EVENT_RADIO_UNAVAILABLE消息上来,UiccController才能分清是从哪个Phone过来的消息,也就是从哪个modem或者说是从哪个卡。。。

再来看看消息处理:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

@Override

publicvoid handleMessage (Message msg){

synchronized(mLock){

Integer index = getCiIndex(msg);

|| index >= mCis.length){

Rlog.e(LOG_TAG,"Invalid index : "+ index +" received with event "+ msg.what);

return;

}

switch(msg.what){

caseEVENT_ICC_STATUS_CHANGED:

if(DBG) log("Received EVENT_ICC_STATUS_CHANGED, calling getIccCardStatus");

mCis[index].getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE, index));

break;

caseEVENT_GET_ICC_STATUS_DONE:

if(DBG) log("Received EVENT_GET_ICC_STATUS_DONE");

AsyncResult ar =(AsyncResult)msg.obj;

onGetIccCardStatusDone(ar, index);

break;

caseEVENT_RADIO_UNAVAILABLE:

if(DBG) log("EVENT_RADIO_UNAVAILABLE, dispose card");

if(mUiccCards[index]!=null){

mUiccCards[index].dispose();

}

mUiccCards[index]=null;

mIccChangedRegistrants.notifyRegistrants(new AsyncResult(null, index,null));

break;

default:

Rlog.e(LOG_TAG," Unknown Event "+ msg.what);

}

}

}

总结如下:

1). 消息到来之后,首先从Message中取出index值,也就是PhoneId;

2). 根据EVENT分发处理,如果是 EVENT_ICC_STATUS_CHANGED消息,对根据index调用对应的RILJ的getIccCardStatus函数,并传递EVENT_GET_ICC_STATUS_DONE,典型的异步处理,当EVENT_GET_ICC_STATUS_DONE返回时,就会从底层获取到了这个index对应的卡的状态,然后调用onGetIccCardStatusDone来更新对应index的卡相关的对象。卡相关的对象都是在这里被创建出来的。具体如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

privatesynchronizedvoid onGetIccCardStatusDone(AsyncResult ar, Integer index){

if(ar.exception !=null){

Rlog.e(LOG_TAG,"Error getting ICC status. "

+"RIL_REQUEST_GET_ICC_STATUS should "

+"never return an error", ar.exception);

return;

}

if(!isValidCardIndex(index)){

Rlog.e(LOG_TAG,"onGetIccCardStatusDone: invalid index : "+ index);

return;

}

IccCardStatus status =(IccCardStatus)ar.result;

if(mUiccCards[index]==null){

//Create new card

mUiccCards[index]=new UiccCard(mContext, mCis[index], status, index);

/*

// Update the UiccCard in base class, so that if someone calls

// UiccManager.getUiccCard(), it will return the default card.

if (index == PhoneConstants.DEFAULT_CARD_INDEX) {

mUiccCard = mUiccCards[index];

}

*/

}else{

//Update already existing card

mUiccCards[index].update(mContext, mCis[index], status);

}

if(DBG) log("Notifying IccChangedRegistrants");

mIccChangedRegistrants.notifyRegistrants(new AsyncResult(null, index,null));

}

从代码的实现可以看出,首先从result中解析出IccCardStatus,然后根据这个值进行UiccCard的创建,如果对应的index的卡 UiccCard已经存在,那么就会调用UiccCard.update来更新其内部的UiccCardApplication,这里提一下这几个类的关系:

UIccController 中根据卡的个数创建对应数量的 UIccCard,而每个UiccCard中又会分别根据自己卡的实际情况创建对应的UiccCardApplication

UiccController 总体控制

UiccCard 具体的卡

UiccCardApplication 具体的卡里的应用【每个UiccCardApplication内部都会根据app_type来创建对应的 IccRecords和IccFileHandler对象作为操作卡上内容的接口】

3). 如果是 EVENT_RADIO_UNAVAILABLE消息,则会销毁对应的UiccCard实例,并notify。

所以总结来看,UiccController就是通过向RIL注册卡状态变化的监听,当底层一有变化时,会通过RIL上报给UiccController,这样就会触发其下发getIccCardStatus来查询卡状态,得到卡状态后更新其内部的UiccCard及UIccCardApplication等。所以phone或者其他state tracker service可以通过UiccController来获取到正确的卡信息。

整个家族树总结如下:

IccardProxy

在我看来IccardProxy是一个有些多余的类,因为其内部实际维护的各种实例都是从UiccController框架中取得的,就连ICC_CARD_STATUS_CHANGED消息,也是通过向UiccControler注册来得到notify,所以卡状态的更新与维护,UiccController永远是第一步的。

通过阅读代码,我感觉IcccardProxy就是一个用来提供给外部使用的接口,可以使得app不用直接操作UiccController,android给出来注释如下:

/**

* @Deprecated use {@link UiccController}.getUiccCard instead.

*

* The Phone App assumes that there is only one icc card, and one icc application

* available at a time. Moreover, it assumes such object (represented with IccCard)

* is available all the time (whether {@link RILConstants#RIL_REQUEST_GET_SIM_STATUS} returned

* or not, whether card has desired application or not, whether there really is a card in the

* slot or not).

*

* UiccController, however, can handle multiple instances of icc objects (multiple

* {@link UiccCardApplication}, multiple {@link IccFileHandler}, multiple {@link IccRecords})

* created and destroyed dynamically during phone operation.

*

* This class implements the IccCard interface that is always available (right after default

* phone object is constructed) to expose the current (based on voice radio technology)

* application on the uicc card, so that external apps won't break.

*/

IccCardProxy在Phone创建的时候被构造,在UiccController初始化之后,

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

// Instantiate UiccController so that all other classes can just

// call getInstance()

mUiccController = UiccController.make(context, sCommandsInterfaces);

; i < numPhones; i++){

PhoneBase phone =null;

int phoneType = TelephonyManager.getPhoneType(networkModes[i]);

if(phoneType == PhoneConstants.PHONE_TYPE_GSM){

phone =new GSMPhone(context,

sCommandsInterfaces[i], sPhoneNotifier, i);

}elseif(phoneType == PhoneConstants.PHONE_TYPE_CDMA){

phone =new CDMALTEPhone(context,

sCommandsInterfaces[i], sPhoneNotifier, i);

}

Rlog.i(LOG_TAG,"Creating Phone with type = "+ phoneType +" sub = "+ i);

sProxyPhones[i]=newPhoneProxy(phone);

}

上面的l17行,通过phone创建的PhoneProxy代理类实例内部会创建IccCardProxy。

mIccCardProxy = new IccCardProxy(mActivePhone.getContext(), mCommandsInterface, mActivePhone.getPhoneId());

这里也可以看出,IccCardProxy实例的个数是与Phone的个数相对应的,有2个phone就会有两个IccCardProxy对象,而UiccController里的UiccCard对象是跟卡动态关联的。所以,app如果通过phoneproxy.getIccCard是可以随时拿到IccCardProxy对象的,这样就不会发生获取不到卡状态的问题。也就是说APP是不会直接操作UiccController的,都是通过IccCardProxy来进行。

先来看看他的构造函数:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

public IccCardProxy(Context context, CommandsInterface ci){

log("Creating");

mContext = context;

mCi = ci;

mCdmaSSM = CdmaSubscriptionSourceManager.getInstance(context,

ci,this, EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED,null);

mUiccController = UiccController.getInstance();

mUiccController.registerForIccChanged(this, EVENT_ICC_CHANGED,null);

ci.registerForOn(this,EVENT_RADIO_ON,null);

ci.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_UNAVAILABLE,null);

setExternalState(State.NOT_READY);

}

public IccCardProxy(Context context, CommandsInterface ci,int cardIndex){

this(context, ci);

mCardIndex = cardIndex;

resetProperties();

setExternalState(State.NOT_READY,false);

}

黄色高亮的是几个关键函数。

首先IccCardProxy会向UiccController中注册ICC_CARD_STATUS_CHANGED消息,也就是在UiccController在更新完自己内部的UiccCard之后会notify IccCardProxy来让IccCardProxy更新自己内部的UiccCard实例等,但这里有个问题,就是UiccController虽是单例的,但其内部的UiccCard却可能会是多个的(多卡的情况下),而这里registerForIccChanged,注册EVENT时,却没有指定phoneid,那么UiccController无论哪个卡有更新都会来notify,单卡的情况下无所谓,但双卡的情况下就会引入多余notify,是一个可以考虑改进的地方。

另外,重置properties,这里使用系统属性记录卡的状态

1

2

3

4

5

6

7

8

void resetProperties(){

if(mCurrentAppType == UiccController.APP_FAM_3GPP){

log("update icc_operator_numeric="+"");

setSystemProperty(PROPERTY_ICC_OPERATOR_NUMERIC, mCardIndex,"");

setSystemProperty(PROPERTY_ICC_OPERATOR_ISO_COUNTRY, mCardIndex,"");

setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, mCardIndex,"");

}

}

1

2

3

4

privatevoid setSystemProperty(String property,int slotId, String value){

long[] subId = SubscriptionController.getInstance().getSubId(slotId);

], value);

}

TelephonyManager.setTelephonyProperty 这里不再贴了,说一下其记录property来支持双卡的方法:android使用同一个key,同时保存两个卡的属性值,值之间使用","分隔,顺序以phoneId从小到大排序。使用时取出后将","分隔转换为数组直接取下标即可。

总结:UiccController负责对卡槽的卡实时实例化或销毁对象,IccCardProxy监听UiccController里的变化并及时更新自己内部的状态,Phone实例通过getIccCard得到IccCardProxy实例来获取各种卡状态,Phone再通过service形式将这些接口暴露给应用层。

Android 5.0 Uicc框架分析的更多相关文章

  1. Android 7.0 UICC 分析(四)

    本文讲解SIMRecords /frameworks/opt/telephony/src/java/com/android/internal/telephony/uicc/SIMRecords.jav ...

  2. Android 7.0 UICC 分析(二)

    本文讲解UiccCard类 /frameworks/opt/telephony/src/java/com/android/internal/telephony/uicc/UiccCard.java U ...

  3. Android 7.0 UICC 分析(三)

    本文讲解UICCCardApplication /frameworks/opt/telephony/src/java/com/android/internal/telephony/uicc/UiccC ...

  4. Android/Linux下CGroup框架分析及其使用

    1 cgroup介绍 CGroup是control group的简称,它为Linux kernel提供一种任务聚集和划分的机制,可以限制.记录.隔离进程组(process groups)所使用的资源( ...

  5. Android 7.0 IMS框架详解

    本文主要讲解IP Multimedia Subsystem (IMS)在Android 7.0上由谷歌Android实现的部分内容.从APP侧一直到Telephony Framework,是不区分CS ...

  6. Android 7.0 UICC 分析(一)

    UICC(Universal Intergrated Circuit Card) 框架 * Following is class diagram for uicc classes: * * UiccC ...

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

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

  8. Android 5.0 Phone初始化分析

    已经更新至个人blog:http://dxjia.cn/2015/07/android-5-0-phone-init-analysis/ persistent属性 要想了解phone的框架,首先需要了 ...

  9. Android 9.0 关机流程分析

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

随机推荐

  1. IOS block使用中碰到的一个小坑

    1.先上段代码       __block typeof(self) tmpSelf = self; [tableview addLegendHeaderWithRefreshingBlock:^() ...

  2. pro生成sln

    跳转到对应的工程目录,通过执行如下的命令:qmake -tp vc 命令实现

  3. Dynamic CRM 2013学习笔记(三十四)自定义审批流5 - 自动邮件通知

    审批过程中,经常要求自动发邮件:审批中要通知下一个审批人进行审批:审批完通知申请人已审批完:被拒绝后,要通知已批准的人和申请人.下面详细介绍如何实现一个自动发邮件的插件:   1. 根据审批状态来确定 ...

  4. ubuntu adobe flash player 安装

    常规做法 1.先更新sudo apt-get update 2. sudo apt-get install flashplugin-installer 这次却卡在downloading这里 下不去.无 ...

  5. [游戏模版6] Win32 graph

    >_<:there in the MyPaint(...) function respectively use Ellipse(...) draw ellipse, use RoundRe ...

  6. easyui使用技巧

    1.自定义datagrid字体大小 通过formatter改变字体大小,然后在列中使用: 如下: function formatFontSize(value){ return'<span sty ...

  7. SVN中Branch的创建与合并

    在使用源代码版本控制工具时,最佳实践是一直保持一个主干版本.但是为了应付实际开发中的各种情况,适时的开辟一些分支也是很有必要的.比如在持续开发新功能的同时,需要发布一个新版本,那么就需要从开发主干中建 ...

  8. Git常用操作命令与图解

    Git 是一个很强大的分布式版本控制系统.它不但适用于管理大型开源软件的源代码,管理私人的文档和源代码也有很多优势. Git常用操作命令: 1) 远程仓库相关命令 检出仓库:$ git clone g ...

  9. Java基础的一次总结

    二重循环核心:就是我们学习的内容中,一直在不停的打印图形,当我们写完所有打印图形的题目后,我们站在一个 新的高度上来看二重循环解决图形问题的时候.我们知道核心思想就是:就是寻找外层循环变量i和内层循环 ...

  10. [BTS]The join order has been enforced because a local join hint is used.;Duplicate key was ignored.".

    在一个客户的BizTalk Server 2013 R2环境中会报如下的ERROR,查找相关资料后,先试试停掉所有Trace. Log Name:      ApplicationSource:    ...