一.UICC概念
UICC:Universal Integrated Circuit Card,通用集成电路卡。主要用于存储用户信息、鉴权密钥、短消、付费方式等信息。俗称电话卡,SIM卡。
二.UICC种类
UICC引入了多应用平台的概念,实现了多个逻辑应用(即逻辑模块)同时运行的多通道机制。一个UICC可同时包含多种不同的逻辑模块,根据当前终端所支持的无线接入网络的类型,来选择使用相应的逻辑模块。
UICC根据逻辑模块的不同,分为以下几种不同的电话卡:
1.SIM:如用户标识模块(Subscriber Identity Module)
早期的SIM卡类型,支持GSM网络。
2.USIM:通用用户标识模块(Universal Subscriber Identity Module,)
升级版的SIM卡,支持UMTS(WCMDA,TD-SCDMA)和GSM网络,(联通卡,移动卡)
3.RUIM:可移动用户身份模块(Removable User Identity Module)
在SIM卡基础上做了扩展,专门为网络而设计,不再只是针对GSM,比如:CDMA.R-UIM卡支持在CDMA和GSM网络之间的漫游。支持CDMA和GSM网络(电信卡)
4.ISIM:IP多媒体业务标识模块(IP Multi Media Service Identity Module,)
存储了IMS(IP多媒体系统)专用的用户数据,主要由IMS运营商所提供。
5.CSIM:CDMA用户识别模块(CDMA Subscriber Identity Module )
三.UICC框架
Android4.1之后的版本都使用了UICC框架来管理和控制SIM卡。
UICC的大体框架图如下:
UICC框架中涉及的类主要有以下几个:
UiccController | 设计为单例模式。监听RIL中的SIM卡状态,并把SIM卡状态的变化通知给其他类。在UICC框架中,它属于核心部分,除了分发SIM卡状态变化,还对外提供接口用于获取UiccCard,IccFileHandler,IccRecords,UiccCardApplication的对象。 |
UiccCard | 它代表了具体的卡,一个UiccCard对象就表示了一张SIM卡(PhoneID)。SIM卡中的状态或者数据都可以在这里获取,比如,属性mCardState保存了SIM卡状态,mUniversalPinState保存了PIN码状态,mCatService代表了STK应用信息,mUiccApplications中包含了SIM卡的具体数据。。。等等,总结起来,UiccCard是SIM卡的大管家,它既代表了SIM卡,又控制了UiccApplications,CatService的生命周期。 |
UiccCardApplication | 顾名思义,这是SIM卡应用(不是STK)。应该是对应了上面说到的逻辑模块,一张SIM卡可以有多个逻辑模块,也就有多个UiccCardApplication对象。它控制了IccRecords和IccFileHandler的生命周期。而IccRecords和IccFileHandler都是读取和保存SIM卡中具体数据的操作类 |
IccFileHandler | 读取SIM卡中(逻辑模块)的文件系统,也就是SIM卡中的具体数据。根据UICC卡的种类不同,衍生了几个对应的子类SIMFileHandler,UsimFileHandler,RuimFileHandler,CsimFileHandler,IsimFileHandler |
IccRecords | 模板类,通过IccFileHandler来操作SIM卡中的文件系统个,获取并保存SIM中的具体数据,根据UICC卡的种类不同,衍生了几个对应的子类SIMRecords,RuimRecords,IsimUiccRecords |
IccCardProxy | 封装了对UICC的一系列操作(状态和数据),并对外提供接口。一张卡(PhoneID)对应一个IccCardProxy对象。实际上,我们可以使用UiccController获取其他的对象从而实现对UICC的操作,但是需要传入一系列参数并保证卡状态正确,否则需要做判断处理,使用IccCardProxy操作只需要知道PhoneID。并发出ACTION_SIM_STATE_CHANGED广播,通知应用层以及没有监听UiccController得知SIM卡状态发生变化的其他类。 |
AdnRecordCache |
UICC框架中主动读取SIM卡状态的时序图:
结合框架图和时序图,下面大致说一下各个类的工作细节。从上到下的顺序:
RIL.java->UiccController.java->UiccCard.java->UiccCardApplication.java->IccCardProxy.java
UICC框架中把SIM卡的状态分为以下几种:
IccCardStatus.java
public static enum CardState {
CARDSTATE_ABSENT,
CARDSTATE_PRESENT,
CARDSTATE_ERROR;
}
UiccController监听RIL中的状态变化,其中几个比较重要的监听事件如下:
EVENT_ICC_STATUS_CHANGED SIM卡状态
EVENT_GET_ICC_STATUS_DONE 主动查询SIM卡状态
EVENT_RADIO_UNAVAILABLE Modem没启动或者错误
EVENT_SIM_REFRESH SIM卡刷新
EVENT_ICC_STATUS_CHANGED,EVENT_RADIO_UNAVAILABLE,EVENT_SIM_REFRESH 都是RIL主动上报的事件消息。其中EVENT_ICC_STATUS_CHANGED这个事件消息的上报结果中并不会携带结果,也就是说得到这个状态,只能判断到当前SIM卡状态变化了,但是当前是CARDSTATE_ABSENT,CARDSTATE_PRESENT,CARDSTATE_ERROR中的哪一个,你还需要查询一遍,所以就有了以下的这个状态监听:
EVENT_GET_ICC_STATUS_DONE 主动查询SIM卡状态
这就是有了EVENT_ICC_STATUS_CHANGED事件消息后还需要设置EVENT_GET_ICC_STATUS_DONE事件消息的原因。
1.RIL.java
RIL中处理SIM卡状态相关的事件消息。其中,在处理EVENT_GET_ICC_STATUS_DONE 主动查询SIM卡状态的时候,查询结果被封装在IccCardStatus类中并交由其监听类UiccController中处理(ret):
case RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED:
if (RILJ_LOGD) unsljLog(response);
if (mIccStatusChangedRegistrants != null) {
mIccStatusChangedRegistrants.notifyRegistrants();
}
break;
case RIL_UNSOL_SIM_REFRESH:
if (RILJ_LOGD) unsljLogRet(response, ret);
if (mIccRefreshRegistrants != null) {
mIccRefreshRegistrants.notifyRegistrants(
new AsyncResult (null, ret, null));
}
break;
case RIL_REQUEST_GET_ICC_APPLICATION_STATUS: ret = responseIccCardStatus(p); break;
case RIL_REQUEST_GET_SIM_STATUS: ret = responseIccCardStatus(p); break;
......
private Object responseIccCardStatus(Parcel p) {
IccCardApplicationStatus appStatus;
IccCardStatus cardStatus = new IccCardStatus();
cardStatus.setCardState(p.readInt());
......
return cardStatus;
}
2.UiccController.java
UiccController中处理SIM卡状态相关的事件消息:
case EVENT_GET_ICC_STATUS_DONE:
if (DBG) log("Received EVENT_GET_ICC_STATUS_DONE");
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;
case EVENT_SIM_REFRESH:
if (DBG) log("Received EVENT_SIM_REFRESH");
onSimRefresh(ar, index);
如时序图所示,主要分析主动查询SIM卡状态时的流程:为每一张SIM卡初始化一个UiccCards对象并赋初值。
private synchronized void onGetIccCardStatusDone(AsyncResult ar, Integer index) {
......
if(this.mUiccCards[index.intValue()] == null) {
this.mUiccCards[index.intValue()] = new UiccCard(this.mContext, this.mCis[index.intValue()], status, index.intValue());
} else {
this.mUiccCards[index.intValue()].update(this.mContext, this.mCis[index.intValue()], status);
}
......
}
3.UiccCard.java
一个UiccCard对象就表示了一张SIM卡(PhoneID),其控制了UiccApplications,CatService的生命周期。参见UiccCard.java—->createAndUpdateCatService()和update()
4.UiccApplications.java
UiccApplications代表了SIM卡中的逻辑模块,根据当前SIM中的逻辑模块的多少产生多少个UiccApplications对象,上限为8个。控制着IccRecords和IccFileHandler的生命周期,此外还包含了一系列对SIM卡数据的操作,比如pin码,puk码,是否锁卡,是否支持FDN等等,系统文件的读取则交由IccFileHandler处理。
根据UICC卡的种类不同,创建相应的模板类IccRecords:
if (type == AppType.APPTYPE_USIM || type == AppType.APPTYPE_SIM) {
return new SIMRecords(this, c, ci);
} else if (type == AppType.APPTYPE_RUIM || type == AppType.APPTYPE_CSIM){
return new RuimRecords(this, c, ci);
} else if (type == AppType.APPTYPE_ISIM) {
return new IsimUiccRecords(this, c, ci);
} else {
// Unknown app type (maybe detection is still in progress)
return null;
}
根据UICC卡的种类不同,创建相应的系统文件读取类IccFileHandler:
private IccFileHandler createIccFileHandler(AppType type) {
switch (type) {
case APPTYPE_SIM:
return new SIMFileHandler(this, mAid, mCi);
case APPTYPE_RUIM:
return new RuimFileHandler(this, mAid, mCi);
case APPTYPE_USIM:
return new UsimFileHandler(this, mAid, mCi);
case APPTYPE_CSIM:
return new CsimFileHandler(this, mAid, mCi);
case APPTYPE_ISIM:
return new IsimFileHandler(this, mAid, mCi);
default:
return null;
}
}
这里忽略IccRecords和IccFileHandler中对SIM卡内存的数据操作……
5.IccCardProxy.java
IccCardProxy设计的初衷就是对外提供接口。因为UiccCards对象的创建跟卡状态相关,会动态创建和销毁,如果外部通过UiccCards来获取卡的状态以及其他信息,不可避免的会出现错误,因此,android就设计了IccCardProxy,无论卡状态如何变化,它的对象都存在,其初始化在TelephonyComponentFactory.java中:
public IccCardProxy makeIccCardProxy(Context context, CommandsInterface ci, int phoneId) {
return new IccCardProxy(context, ci, phoneId);
}
Phone对象创建时,先创建了UiccController对象,之后根据mPhoneId创建了IccCardProxy对象,意味着有几个卡槽就有几个IccCardProxy对象。
mIccCardProxy = mTelephonyComponentFactory.makeIccCardProxy(mContext, mCi, mPhoneId);
如果我们需要操作SIM卡相关,正确的姿势应该是通过Phone对象获取IccCardProxy对象,通过IccCardProxy暴露出的接口来实现对SIM卡相关的操作。
@Override
public IccCard getIccCard() {
return mIccCardProxy;
}
Android对IccCardProxy的设计初衷的注释如下:
* 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.
以上就是对UICC框架的一些简单总结,细节方面待以后使用到的时候再去研究。