android telephony 之 UICC 卡数据读写及 UICC 框架结构
我们平时听过的卡 SIM, USIM, UIM等统称为:UICC — Universal Integrated Circuit Card
说白了,UICC就是 各种类型 SIM 卡的一个抽象,有一整套框架来对 UICC 卡进行管理,包括数据的增删改查,包括 SIM 卡的状态变化等~
在UiccController 的类注释中有详细的介绍,以 UICC 为中心的结构,可以从这个类展开学习: 详见:frameworks/opt/telephony/src/java/com/android/internal/telephony/uicc/UiccController.java
基本框架类结构图:
这里涉及到几个类:
1.UiccController:整个UICC相关信息的控制接口;监控SIM状态变化;
2.UiccConstants: 这些FileHandler 都实现了这个接口,这个接口通过名称就可以看出来,是一些常量值,存储不同数据在Sim卡上的字段地址,就是 SIM 卡的一些 MF,DF,EF的16进制的值,在读取 SIM 存储的内容的时候,都是通过这些常量值来读取的。
3.UiccCard:是对 SIM 卡的一个抽象,包括一些 CardState 等,(1)通过 UiccCardApplication 来控制每张卡(针对多卡手机就是多张卡),(2)还有 CatService, SIM 卡通过 这个类和 Stk进行交互以及消息传递,他们都是运行在Phone 进程的,至于Stk是什么,后续再说,先了解有这么个东西就好。
4.UiccCardApplication:进到这个类中,从一些变量的定义以及构造函数中可以明显的看出,这个类就是控制 SIM 卡的一些内容的,比如卡的状态,IccRecords(SIM卡上的内容的抽象类)等,在构造函数中会创建:IccFileHandler,和IccRecords,因为这2个内容都是和卡息息相关的,都要根据卡的类型创建不同的类。
5.IccCardStatus:维护UICC卡的状态:CardState & PinState;
6.IccFileHandler:读取SIM数据以及接收读取的结果 SIMFileHandler, RuimFileHandler,UsimFileHandler,CsimFileHandler,IsimFileHandler (1)第二行都是继承了IccFileHandler,从类名上来看就可以知道,这些FileHandler 都是继承自 Handler,那么就是说通过消息机制来进行数据处理的。 (2)子类都重写了 getEfPath(int efid) 这个方法,根据不同的卡,取不同的 ef path,至于ef path 是什么意思,后续会有说到。
7.records:记录SIM卡上的数据 IccRecords:基类 SIMRecords,RuimRecords,IsimUiccRecords (1)可以看到,第二行都是继承第一行的,也是一个Handler,也实现了IccConstants这个接口。那么一定也是通过 消息机制来处理数据的。 (2)IccRecords 存储的就是卡上的内容,比如:卡的手机号,IMSI,voiceMail,spn等等,这些专有名词,先有个了解,后续会有说明; (3)对于 SIM 卡准备好以后,这里有篇文章可以参考,是读取SIM卡内容的例子:http://blog.csdn.net/guoleimail/article/details/7082156
8.IccUtils:里面一般全是静态方法,主要用来码制转换
9.CatService: 之所以把这个类拿出来单独放到这里,是因为和 Stk 交互都是通过这个类进行的,Stk 平时用的少,就是 SIM 卡工具包这个应用,Android 原生就有的应用,是运营商相关的。这个应用我改过很多次,用处不大,但是在天朝,必须有,因为很多运营商和银行(有特定银行的SIM卡)都需要这个和客户进行合作。
从代码层面分析一下UICC框架:
UiccController 本身是一个Handler,因为它 extends Handler,构造函数如下:
private UiccController(Context c, CommandsInterface ci) { if (DBG) log("Creating UiccController"); mContext = c; mCi = ci; mCi.registerForIccStatusChanged(this, EVENT_ICC_STATUS_CHANGED, null); mCi.registerForAvailable(this, EVENT_ICC_STATUS_CHANGED, null); mCi.registerForNotAvailable(this, EVENT_RADIO_UNAVAILABLE, null); mCi.registerForIccRefresh(this, EVENT_REFRESH, null); }
构造函数会注册一些监听,然后在handleMessage处理回调:
public void handleMessage (Message msg) { synchronized (mLock) { switch (msg.what) { case EVENT_ICC_STATUS_CHANGED: if (DBG) log("Received EVENT_ICC_STATUS_CHANGED, calling getIccCardStatus"); mCi.getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE)); break; case EVENT_GET_ICC_STATUS_DONE: if (DBG) log("Received EVENT_GET_ICC_STATUS_DONE"); AsyncResult ar = (AsyncResult)msg.obj; onGetIccCardStatusDone(ar); break; case EVENT_RADIO_UNAVAILABLE: if (DBG) log("EVENT_RADIO_UNAVAILABLE "); disposeCard(mUiccCard); mUiccCard = null; mIccChangedRegistrants.notifyRegistrants(); break; case EVENT_REFRESH: ar = (AsyncResult)msg.obj; if (DBG) log("Sim REFRESH received"); if (ar.exception == null) { handleRefresh((IccRefreshResponse)ar.result); } else { log ("Exception on refresh " + ar.exception); } break; default: Rlog.e(LOG_TAG, " Unknown Event " + msg.what); } } }
从上面可以看到:当收到 EVENT_ICC_STATUS_CHANGED 时,马上会发一个命令,去查询IccCardStatus:
mCi.getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE));
然后执行完毕会收到:EVENT_GET_ICC_STATUS_DONE
然后在:onGetIccCardStatusDone() 这个函数中 处理,创建UiccCard,或者更新card的状态
然后在UiccCard的 update() 方法中,会创建 UiccCardApplication 或者 更新 UiccCardApplication 的状态;
在UiccCardApplication 中,如果 UICC 需要PIN 解锁,则会发出需要PIN码 锁通知,进行 pin 码输入解锁,然后状态变化,继续更新 uicc card,uiccApplications 直到 UICC 状态 Ready,如果 UICC ready,则发出 UICC ready通知:
具体的流程可以参考下面的流程图:
其实读取UICC CARD数据有几个地方,那么在UiccCardApplication update()中就涉及到了读取 SIM 卡数据的地方:具体流程看下面:
UiccCardApplication 在创建的时候,还有更新的时候就会创建 IccFileHandler,IccRecords(注意这2个类都是父类,他们有具体的子类)
mIccFh = createIccFileHandler(as.app_type); mIccRecords = createIccRecords(as.app_type, mContext, mCi);
那么 IccRecords 呢,我们具体一下,拿 SIMRecords 为例,
public SIMRecords(UiccCardApplication app, Context c, CommandsInterface ci) { super(app, c, ci); mAdnCache = new AdnRecordCache(mFh); mVmConfig = new VoiceMailConstants(); mSpnOverride = new SpnOverride(); mRecordsRequested = false; // No load request is made till SIM ready // recordsToLoad is set to 0 because no requests are made yet mRecordsToLoad = 0; // Start off by setting empty state resetRecords(); mParentApp.registerForReady(this, EVENT_APP_READY, null); if (DBG) log("SIMRecords X ctor this=" + this); }
这里会注册一个 EVENT_APP_READY,mParentApp 就是 UiccCardApplication,
public void registerForReady(Handler h, int what, Object obj) { synchronized (mLock) { Registrant r = new Registrant (h, what, obj); mReadyRegistrants.add(r); notifyReadyRegistrantsIfNeeded(r); } }
可以看到,注册到监听器中了。 那么什么时候通知的呢?
在UiccCardApplication 的 update() 方法,有一句这个:notifyReadyRegistrantsIfNeeded(null); 然后看下这个方法:
/** * Notifies specified registrant, assume mLock is held. * * @param r Registrant to be notified. If null - all registrants will be notified */ private void notifyReadyRegistrantsIfNeeded(Registrant r) { if (mDestroyed) { return; } if (mAppState == AppState.APPSTATE_READY) { if (mPin1State == PinState.PINSTATE_ENABLED_NOT_VERIFIED || mPin1State == PinState.PINSTATE_ENABLED_BLOCKED || mPin1State == PinState.PINSTATE_ENABLED_PERM_BLOCKED) { loge("Sanity check failed! APPSTATE is ready while PIN1 is not verified!!!"); // Don't notify if application is in insane state return; } if (r == null) { if (DBG) log("Notifying registrants: READY"); mReadyRegistrants.notifyRegistrants(); } else { if (DBG) log("Notifying 1 registrant: READY"); r.notifyRegistrant(new AsyncResult(null, null, null)); } } }
很明显了吧,当 state 是 READY 的时候,会 notify 注册过的监听。
那么收到 SIM READY 的消息后的处理,见 SIMRecords,handleMessage() 方法,
case EVENT_APP_READY: onReady(); break;
然后执行 fetchSimRecords(),那么所有的读取SIM卡的操作都是在这里了,这里截取一些来看看:
mFh.loadEFTransparent(EF_ICCID, obtainMessage(EVENT_GET_ICCID_DONE));mFh.loadEFLinearFixed(EF_MBI, 1, obtainMessage(EVENT_GET_MBI_DONE));
这里可以看到,他们调用了不同的方法,原因就是因为 SIM 卡有不用的文件类型,前一篇文章可以看到。上面的2种方法对应了2种文件类型。
/** * Load a SIM Transparent EF * * @param fileid EF id * @param onLoaded * * ((AsyncResult)(onLoaded.obj)).result is the byte[] * */ public void loadEFTransparent(int fileid, Message onLoaded) { Message response = obtainMessage(EVENT_GET_BINARY_SIZE_DONE, fileid, 0, onLoaded); mCi.iccIOForApp(COMMAND_GET_RESPONSE, fileid, getEFPath(fileid), 0, 0, GET_RESPONSE_EF_SIZE_BYTES, null, null, mAid, response); }
可以看到,最后调用到了这里,mCi 是 CommandsInterface 的实例。
@Override public void iccIOForApp (int command, int fileid, String path, int p1, int p2, int p3, String data, String pin2, String aid, Message result) { //Note: This RIL request has not been renamed to ICC, // but this request is also valid for SIM and RUIM RILRequest rr = RILRequest.obtain(RIL_REQUEST_SIM_IO, result); rr.mParcel.writeInt(command); rr.mParcel.writeInt(fileid); rr.mParcel.writeString(path); rr.mParcel.writeInt(p1); rr.mParcel.writeInt(p2); rr.mParcel.writeInt(p3); rr.mParcel.writeString(data); rr.mParcel.writeString(pin2); rr.mParcel.writeString(aid); if (RILJ_LOGD) riljLog(rr.serialString() + "> iccIO: " + requestToString(rr.mRequest) + " 0x" + Integer.toHexString(command) + " 0x" + Integer.toHexString(fileid) + " " + " path: " + path + "," + p1 + "," + p2 + "," + p3 + " aid: " + aid); send(rr); }
然后 在 processSolicited () 这个函数中收到请求回来的消息,然后发回给调用的地方。这样一整个读取 SIM 卡数据的过程就完毕了。
参考:http://www.netfoucs.com/article/songjinshi/60650.html
更多相关文章
- C语言函数的递归(上)
- android Application Component研究之ContentProvider
- [置顶] Android事件总线还能怎么玩?
- Android账户同步备份机制
- Android应用程序组件Content Provider简要介绍和学习计划
- Android应用程序绑定服务(bindService)的过程源码分析
- android外存储的状态
- Android(安卓)HAL的被调用流程
- android Activity类的使用