Android 5.0 Uicc框架分析

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


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

synchronized
(mLock)
{

if
(mInstance !=
null)
{

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

}

mInstance =
new UiccController(c, ci);

return
(UiccController)mInstance;

}

}

?

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

public
void handleMessage (Message msg)
{

synchronized
(mLock)
{

Integer index = getCiIndex(msg);

?

if
(index <
0
|| index >= mCis.length)
{

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

return;

}

?

switch
(msg.what)
{

case
EVENT_ICC_STATUS_CHANGED:

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

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

break;

case
EVENT_GET_ICC_STATUS_DONE:

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

AsyncResult ar =
(AsyncResult)msg.obj;

onGetIccCardStatusDone(ar, index);

break;

case
EVENT_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


private
synchronized
void 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);

?

for
(int i =
0; 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);

}
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);

?

sProxyPhones[i]
=
new
PhoneProxy(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


private
void setSystemProperty(String property,
int slotId, String value)
{

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

TelephonyManager.setTelephonyProperty(property, subId[0], value);

}

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

?

?

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

时间: 2024-08-02 06:50:43

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

Android Bitmap 开源图片框架分析(精华三)

主要介绍这三个框架,都挺有名的,其他的框架估计也差不多了 Android-Universal-Image-Loaderhttps://github.com/nostra13/Android-Universal-Image-Loader ImageLoaderhttps://github.com/novoda/ImageLoader Volley(综合框架,包含图片部分)https://github.com/mcxiaoke/android-volley 扯淡时间,可以跳过这段这些开源框架的源码还

Android Bitmap 开源图片框架分析(精华四)

disk缓存主要难点在于内存缓存,disk缓存其实比较简单,就是图片加载完成后把图片文件存到本地方便下次使用 同样,先贴一下官方主页的介绍(主页地址见文章最开始处)和内存缓存差不多,根据算法不同提供了几种类别,可以自行通过ImageLoaderConfiguration.discCache(..)设置<ignore_js_op> 硬盘缓存,保存是以文件的形式框架提供了4种类型,具体算法规则不同,看名字我们大概也能知道对应意思 UnlimitedDiscCache                

Android Bitmap 开源图片框架分析(精华五)

本帖最后由 boredream 于 2014-5-27 09:07 编辑 ImageLoader和Volley图片部分还包括其他大部分图片框架,基本上图片处理都差不多,区别仅在于部分优化了,而优化方面UIL即Universal-Image-Loader框架做的最好,所以这部分章节算是温习一下图片处理以及寻找下其他框架里面一些不一样的图片处理方式(只关注图片方面) 首先是ImageLoaderhttps://github.com/novoda/ImageLoader主要还是分析图片加载的核心代码部

Android 7.0 UICC 分析(二)

本文讲解UiccCard类 /frameworks/opt/telephony/src/java/com/android/internal/telephony/uicc/UiccCard.java UICCController 类的onGetIccCardStatusDone() 方法,根据获取的SIM状态信息IccCardStatus 创建或更新UiccCard 对象: private synchronized void onGetIccCardStatusDone(AsyncResult a

kaifyou Android 7.0 UICC 分析(四)

本文讲解SIMRecords /frameworks/opt/telephony/src/java/com/android/internal/telephony/uicc/SIMRecords.java 构造方法: public SIMRecords(UiccCardApplication app, Context c, CommandsInterface ci) { super(app, c, ci); mAdnCache = new AdnRecordCache(mFh); mVmConfi

Android 5.0 双卡信息管理分析

首先,如前面的博文所讲的,Android5.0开始支持双卡了.另外,对于双卡的卡信息的管理,也有了实现,尽管还不是完全彻底完整,如卡的slot id, display name,iccid,color等,其设计思路竟然跟之前接触到的一个平台是一样的,都是同不同颜色来标识不同的卡,让用户一目了然,只是5.0的实现目前还局限在FW框架里,应用层的实现还没有,相信,等到5.1或者再之后的版本中,我们就可以在setting里看到对卡表示颜色.名称等进行设置的功能菜单啦. 下面进入正题,来分析Androi

Android 6.0 Overview Screen实现原理

Android 4.0中添加了一个很有用的特性,那就是overView Screen功能,也就是最近任务预览功能.这个功能提供了一个列表试图,方便用户简单快捷地了解到最近使用的app或者最近进行的任务.这个功能和iOS的最近任务在界面上很相似.在android 5.0中,这个任务得到了进一步的加强,在android 5.0之前overView Screen中显示的任务快照是不可以配置的,但是在android 5.0中是可以配置的,开发者可以指定那些activity以什么样的形式,什么UI风格显示

Android Small插件化框架源码分析

Android Small插件化框架源码分析 目录 概述 Small如何使用 插件加载流程 待改进的地方 一.概述 Small是一个写得非常简洁的插件化框架,工程源码位置:https://github.com/wequick/Small 插件化的方案,说到底要解决的核心问题只有三个: 1.1 插件类的加载 这个问题的解决和其它插件化框架的解决方法差不多.Android的类是由DexClassLoader加载的,通过反射可以将插件包动态加载进去.Small的gradle插件生成的是.so包,在初始

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

前置文章 <Android 5.0 怎样正确启用isLoggable(一)__使用具体解释> 概要 在上文<Android 5.0 怎样正确启用isLoggable(一)__使用具体解释>中分析了isLoggable的用法,本文主要分析isLoggable实现原理以及user版系统root后永久enable isLoggable的原理,并使用脚本自己主动设置isLoggable相关属性. 本文来自http://blog.csdn.net/yihongyuelan 转载请务必注明出处