android telephony 之 UICC 卡数据读写及 UICC 框架结构


我们平时听过的卡 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


基本框架类结构图:
android telephony 之 UICC 卡数据读写及 UICC 框架结构_第1张图片



这里涉及到几个类:

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通知:


具体的流程可以参考下面的流程图:

android telephony 之 UICC 卡数据读写及 UICC 框架结构_第2张图片



其实读取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


你可能感兴趣的:(android,telephony)