26
2017
09

UICC之UsimFileHandler

一、IccFileHandler的创建过程
上一张知道了IccFileHandler是在UiccCardApplication的构造方法中创建的

    public UiccCardApplication(UiccCard uiccCard,
                        IccCardApplicationStatus as,
                        Context c,
                        CommandsInterface ci) {
     //创建IccFileHandler对象 
        mIccFh = createIccFileHandler(as.app_type);
     //创建IccRecords对象 
        mIccRecords = createIccRecords(as.app_type, mContext, mCi);

    }

来看createIccFileHandler():

    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;
        }
    }

这里可以看出使用了type,也就是IccCardApplicationStatus.app_type来创建不同的IccFileHandler返回。
不同的type也就是代表了不同的SIM卡,可以看到主要有SIM/USIM/CSIM/ISIM几种,他们的不同主要是其内部的数据存储分区不同,
而数据存储分区不同带来的影响就是去读写数据时会有所不同,所以需要根据各自的type来创建对应的IccFileHandler。
那么,这也是说到了IccFileHandler的具体作用了。

二、IccFileHandler的功能
SIM卡的本质是一个文件系统,并且具备不同的分区,而IccFileHandler主要用于从SIM卡读取相应分区的数据。
首先知道,不同的xxFileHandler都是Handler,子类中主要是重写了getEFPath(int efid)方法用于给出各自的分区地址.

 @Override
    protected String getEFPath(int efid) {
        // TODO(): DF_GSM can be 7F20 or 7F21 to handle backward compatibility.
        // Implement this after discussion with OEMs.
        switch(efid) {
        case EF_SMS:
            return MF_SIM + DF_TELECOM;

        case EF_EXT6:
        case EF_MWIS:
        case EF_MBI:
        case EF_SPN:
        case EF_AD:
        case EF_MBDN:
        case EF_PNN:
        case EF_SPDI:
        case EF_SST:
        case EF_CFIS:
        case EF_GID1:
        case EF_GID2:
        case EF_MAILBOX_CPHS:
        case EF_VOICE_MAIL_INDICATOR_CPHS:
        case EF_CFF_CPHS:
        case EF_SPN_CPHS:
        case EF_SPN_SHORT_CPHS:
        case EF_INFO_CPHS:
        case EF_CSP_CPHS:
            return MF_SIM + DF_GSM;
        }
        String path = getCommonIccEFPath(efid);
        if (path == null) {
            Rlog.e(LOG_TAG, "Error: EF Path being returned in null");
        }
        return path;
    }

其他的功能都是在父类中实现:

 public void loadEFLinearFixed(int fileid, int recordNum, Message onLoaded) {}  
    public void loadEFImgLinearFixed(int recordNum, Message onLoaded) {}  
    public void loadEFLinearFixedAll(int fileid, Message onLoaded) {}  
    public void loadEFTransparent(int fileid, Message onLoaded) {}  
    public void loadEFTransparent(int fileid, int size, Message onLoaded) {}  
    public void loadEFImgTransparent(int fileid, int highOffset, int lowOffset, int length, Message onLoaded) {}  
    public void getEFLinearRecordSize(int fileid, Message onLoaded) {}  
    public void updateEFLinearFixed(int fileid, int recordNum, byte[] data, String pin2, Message onComplete) {}  
    public void updateEFTransparent(int fileid, byte[] data, Message onComplete) {} 

从这些方法可以看出,IccFileHandler的主要作用就是提供SIM卡文件系统的读写操作,当调用这些方法时,需要传递要读写的文件系统地址,以及读写完毕后的回调函数,IccFileHandler会在读取完数据之后通知到调用者,并把返回值传递过去。例如:

/** * Load a record from a SIM Linear Fixed EF * * @param fileid EF id * @param path Path of the EF on the card * @param recordNum 1-based (not 0-based) record number * @param onLoaded * * ((AsyncResult)(onLoaded.obj)).result is the byte[] * */
    public void loadEFLinearFixed(int fileid, String path, int recordNum, Message onLoaded) {
        String efPath = (path == null) ? getEFPath(fileid) : path;
    //先去读取Record的Size 
        Message response
                = obtainMessage(EVENT_GET_RECORD_SIZE_DONE,
                        new LoadLinearFixedContext(fileid, recordNum, efPath, onLoaded));
    //调用RILJ向Modem读取SIM卡当前分区的长度 
        mCi.iccIOForApp(COMMAND_GET_RESPONSE, fileid, efPath,
                        0, 0, GET_RESPONSE_EF_SIZE_BYTES, null, null, mAid, response);
    }

可以看出,最终也是通过RIL去读取的数据。在这个读取过程中,并不是直接去读取相应的分区内容,而是先读取当前分区记录的长度,当拿到长度之后再去读取当前记录的具体内容:

@Override
    public void handleMessage(Message msg) {


        try {
            switch (msg.what) {

             case EVENT_GET_RECORD_SIZE_DONE:
                ar = (AsyncResult)msg.obj;
                lc = (LoadLinearFixedContext) ar.userObj;
                result = (IccIoResult) ar.result;
                response = lc.mOnLoaded;

                if (processException(response, (AsyncResult) msg.obj)) {
                    break;
                }

                data = result.payload;
                path = lc.mPath;

                if (TYPE_EF != data[RESPONSE_DATA_FILE_TYPE]) {
                    throw new IccFileTypeMismatch();
                }

                if (EF_TYPE_LINEAR_FIXED != data[RESPONSE_DATA_STRUCTURE]) {
                    throw new IccFileTypeMismatch();
                }

        //得到当前记录的长度
                lc.mRecordSize = data[RESPONSE_DATA_RECORD_LENGTH] & 0xFF;

                size = ((data[RESPONSE_DATA_FILE_SIZE_1] & 0xff) << 8)
                       + (data[RESPONSE_DATA_FILE_SIZE_2] & 0xff);

                lc.mCountRecords = size / lc.mRecordSize;

                 if (lc.mLoadAll) {
                     lc.results = new ArrayList<byte[]>(lc.mCountRecords);
                 }

                 if (path == null) {
                     path = getEFPath(lc.mEfid);
                 }

        //加上要读取的记录长度再次读取数据 
                 mCi.iccIOForApp(COMMAND_READ_RECORD, lc.mEfid, path,
                         lc.mRecordNum,
                         READ_RECORD_MODE_ABSOLUTE,
                         lc.mRecordSize, null, null, mAid,
                         obtainMessage(EVENT_READ_RECORD_DONE, lc));
                 break;

这里看到,IccFileHandler拿到记录的长度后,再次通过iccIOForApp()去读取记录的数据,然后就可以拿到真正需要的数据了:

            case EVENT_READ_RECORD_DONE:  
                ar = (AsyncResult)msg.obj; 
                lc = (LoadLinearFixedContext) ar.userObj; 
                result = (IccIoResult) ar.result; 
                response = lc.mOnLoaded; 
                if (processException(response, (AsyncResult) msg.obj)) {  
                    break; 
                }  

                if (!lc.mLoadAll) {  
                    sendResult(response, result.payload, null); 
                } else {  
                    lc.results.add(result.payload); 
                    lc.mRecordNum++; 

                    //循环读取记录  
                    if (lc.mRecordNum > lc.mCountRecords) {  
                        sendResult(response, lc.results, null); 
                    } else {  
                        mCi.iccIOForApp(COMMAND_READ_RECORD, lc.mEfid, getEFPath(lc.mEfid),  
                                lc.mRecordNum,  
                                READ_RECORD_MODE_ABSOLUTE,  
                                lc.mRecordSize, null, null, mAid,  
                                obtainMessage(EVENT_READ_RECORD_DONE, lc)); 
                    }  
                }  

                break; 


        }} catch (Exception exc) {  
        }  
}  

在读取记录过程中仍然是循环读取的过程,直到记录完全读取完毕(lc.mRecordNum > lc.mCountRecords)后,才调用sendResult()方法将记录数据发送给当初的请求者。

上一篇:Android自定义控件之下拉刷新加载更多 下一篇:Android中的消息机制