我们平时听过的卡 SIM, USIM, UIM等统称为:UICC — Universal Integrated Circuit Card

说白了,UICC就是 各种类型 SIM 卡的一个抽象,有一整套框架来对 UICC 卡进行管理,包括数据的增删改查,包括 SIM 卡的状态变化等~


代码结构,在这个包下有一整套框架: frameworks\opt\telephony\src\java\com\android\internal\telephony\uicc\

在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


更多相关文章

  1. C语言函数的递归(上)
  2. android Application Component研究之ContentProvider
  3. [置顶] Android事件总线还能怎么玩?
  4. Android账户同步备份机制
  5. Android应用程序组件Content Provider简要介绍和学习计划
  6. Android应用程序绑定服务(bindService)的过程源码分析
  7. android外存储的状态
  8. Android(安卓)HAL的被调用流程
  9. android Activity类的使用

随机推荐

  1. Android(安卓)多进程通信之几个基本问题
  2. Android百度地图开发(五):图层
  3. Android(安卓)Binder原理(三)系统服务的注
  4. Android跨进程通信IPC之3——关于"JNI"的
  5. 【Android布局】在程序中设置android:gra
  6. Android音频开发(1):音频基础知识
  7. 静态和动态设置EditText的属性大全
  8. android:layout_gravity 和 android:grav
  9. android跨进程通信IPC之12——Binder的补
  10. android 设置id的三种方法的区别