Android 5.0 Uicc框架分析
已同步更新至个人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框架分析的更多相关文章
- Android 7.0 UICC 分析(四)
本文讲解SIMRecords /frameworks/opt/telephony/src/java/com/android/internal/telephony/uicc/SIMRecords.jav ...
- Android 7.0 UICC 分析(二)
本文讲解UiccCard类 /frameworks/opt/telephony/src/java/com/android/internal/telephony/uicc/UiccCard.java U ...
- Android 7.0 UICC 分析(三)
本文讲解UICCCardApplication /frameworks/opt/telephony/src/java/com/android/internal/telephony/uicc/UiccC ...
- Android/Linux下CGroup框架分析及其使用
1 cgroup介绍 CGroup是control group的简称,它为Linux kernel提供一种任务聚集和划分的机制,可以限制.记录.隔离进程组(process groups)所使用的资源( ...
- Android 7.0 IMS框架详解
本文主要讲解IP Multimedia Subsystem (IMS)在Android 7.0上由谷歌Android实现的部分内容.从APP侧一直到Telephony Framework,是不区分CS ...
- Android 7.0 UICC 分析(一)
UICC(Universal Intergrated Circuit Card) 框架 * Following is class diagram for uicc classes: * * UiccC ...
- Android 5.0 双卡信息管理分析
首先,如前面的博文所讲的,Android5.0开始支持双卡了.另外,对于双卡的卡信息的管理,也有了实现,尽管还不是完全彻底完整,如卡的slot id, display name,iccid,color ...
- Android 5.0 Phone初始化分析
已经更新至个人blog:http://dxjia.cn/2015/07/android-5-0-phone-init-analysis/ persistent属性 要想了解phone的框架,首先需要了 ...
- Android 9.0 关机流程分析
极力推荐文章:欢迎收藏 Android 干货分享 阅读五分钟,每日十点,和您一起终身学习,这里是程序员Android 本篇文章主要介绍 Android 开发中的部分知识点,通过阅读本篇文章,您将收获以 ...
随机推荐
- Log4net日志GUI配置工具
关于log4net的配置文章在园子里真的很多,但是有关GUI界面配置的文章确定太少,改写了一个以前很早的工具 以前的那个有很多的问题,这个基本的大的问题没有,可能一个小问题还是需要修改下,基本功能肯定 ...
- Storm启动流程简介
storm启动流程 storm是一个流行的开源的,分布式实时处理框架,关于storm的基本介绍可以参加这篇官方文档.大致的拓扑结构如图所示: 其中Nimbus是一个后台 ...
- Windows 8.1 应用再出发 - 创建我的第一个应用
转眼间Windows 8.1已经发布了四个多月,之前因为开发需要对Windows 8.1新特性进行过零散的学习和使用,一直没有静下心来系统的学习过.近日部门有几名新同事加入,需要进行Windows 商 ...
- 关于用CSS3画图形的一些思考
众所周知,用CSS3的圆角.转换可以画出各种不同的形状,制作不同的图案,早些前先驱者已画出经典的叮当猫,iphone手机等展示CSS3的强大实力,趁最近有空我也对CSS3进行了一些实践,颇有收获. 用 ...
- linux-01Red Hat Enterprise Linux 7(RHEL7)配置静态IP地址
为方便在学习linux readhat7,在本地安装安装了虚拟机.为能够用win7连接虚拟机的linux远程客户端操作,则需要虚拟机和win本地的网络互通: 操作如下:1.本地配置ip地址 : 2.验 ...
- 大熊君说说JS与设计模式之------命令模式Command
一,总体概要 1,笔者浅谈 日常生活中,我们在看电视的时候,通过遥控器选择我们喜欢的频道时,此时我们就是客户端的角色,遥控器的按钮相当于客户请求,而具体执行的对象就是命令对象, 命令模式把一个请求或者 ...
- linux下svn定时更新项目
方法一.用shell脚本定时更新项目 1.进入网站的根目录,假设项目位置放在/var/www/test cd /var/www/test 2.建立脚本文件update.sh,分两步进行.首先利 ...
- C#修改文件权限
用户名的格式为:Local MachineName\AccountName 机器名可通过System.Environment.MachineName获取. 获取一个文件的权限(帐号)列表 FileSe ...
- [游戏学习28] MFC 时钟
>_<:这是一个时钟小程序 >_<:通过调用获得系统时间然后经过计算得出当前时间,然后再以3个圆环表示时分秒. >_<:TAO_CLOCK.h class CMyA ...
- 微软发布Windows 10:连Windows 7都能免费升级了
今日凌晨,微软宣布Windows 10发布. Windows10 整体重置了 Window8 的设计,恢复了原有的开始菜单设置,系统新增了虚拟桌面功能,搜索框加分类项的形式替代原有的两栏式控制面板界面 ...