SIM卡文件介绍及加载

一.卡文件系统

卡文件结构
MF(Master File):3F00,只有一个
DF(Dedicated File):比如DF_GSM为7F20,DF_TELECOM为7F10
EF(Elementary File):按文件类型分为Transparent EF,Linear fixed EF,Cyclic EF
具体参考3GPP11.11,下载链接https://www.3gpp.org/ftp/Specs/archive/11_series/11.11/1111-8e0.zip

二.传输协议

分为五层,物理层,数据链路层,传输层,USAT层,应用层
物理层:IO口传输二进制数据
数据链路层:定义了两种传输方式,T=0是基于字节传输。T=1是基于块传输,均是半双工
T=0:终端向UICC发送一个字节头,该字节头由CLA,INS,P1,P2,P3组成,UICC再返回一个结果
T=1 块结构
在这里插入图片描述
传输层:APDU到TPDU的映射关系,以及TPDU与卡如何完成数据交互
USAT层:使用应用状态字来指示
应用层:包括UICC的文件系统,以及UICC的安全机制,应用交互机制
具体参考http://t.zoukankan.com/a-lai-p-8034795.html

三.AT指令读写卡文件

//读写卡文件at+crsm
176: READ BINARY
178: READ RECORD
192: GET RESPONSE
214: UPDATE BINARY
220: UPDATE RECORD
242: STATUS
比如读取EF_MSISDN 0x6F40,保存有电话号码
at+crsm=192,28480 //返回的值由第15-16知道长度为1c, 即28, 62开头代表为USIM, 0000开头代表为SIM
at+crsm=178,28480,1,4,28 //读取具体的值
at+crsm=178,28480,1,4,28 “XXXXX” //写

//IMSI EF_6F07 ,mccmnc
AT+CRSM=176,28423,0,0,9 //查询
AT+CRSM=214,28423,0,0,9 “XXX” //写 ,正常卡会报错Memory failure

//SPN EF_6F46
AT+CRSM=176,28486,0,0,17 //读
AT+CRSM=214,28486,0,0,17 “XXX” //写

四.开机加载卡文件源码分析

当SIM卡状态为READY时,UiccCardApplication调用notifyReadyRegistrantsIfNeeded广播到SIMRecords, SIMRecords处理EVENT_APP_READY消息,那么是如何知道处理的是EVENT_APP_READY消息呢,查看notifyReadyRegistrantsIfNeeded方法

 mReadyRegistrants.notifyRegistrants();

全局搜索mReadyRegistrants.add可以知道会在SIMRecords中注册

mParentApp.registerForReady(this, EVENT_APP_READY, null);

查看EVENT_APP_READY的处理调用onReady–>fetchSimRecords,通过loadEFTransparent加载透明文件,通过loadEFLinearFixed加载二进制固定文件

protected void fetchSimRecords() {
        mRecordsRequested = true;
        if (DBG) log("fetchSimRecords " + mRecordsToLoad);
        mCi.getIMSIForApp(mParentApp.getAid(), obtainMessage(EVENT_GET_IMSI_DONE));
        mRecordsToLoad++;
        mFh.loadEFTransparent(EF_ICCID, obtainMessage(EVENT_GET_ICCID_DONE));
        mRecordsToLoad++;
        // FIXME should examine EF[MSISDN]'s capability configuration
        // to determine which is the voice/data/fax line
        new AdnRecordLoader(mFh).loadFromEF(EF_MSISDN, getExtFromEf(EF_MSISDN), 1,
                    obtainMessage(EVENT_GET_MSISDN_DONE));
        mRecordsToLoad++;
        // Record number is subscriber profile
        mFh.loadEFLinearFixed(EF_MBI, 1, obtainMessage(EVENT_GET_MBI_DONE));
        mRecordsToLoad++;
        mFh.loadEFTransparent(EF_AD, obtainMessage(EVENT_GET_AD_DONE));
        mRecordsToLoad++;
        // Record number is subscriber profile
        mFh.loadEFLinearFixed(EF_MWIS, 1, obtainMessage(EVENT_GET_MWIS_DONE));
        mRecordsToLoad++;
        // Also load CPHS-style voice mail indicator, which stores
        // the same info as EF[MWIS]. If both exist, both are updated
        // but the EF[MWIS] data is preferred
        // Please note this must be loaded after EF[MWIS]
        mFh.loadEFTransparent(
                EF_VOICE_MAIL_INDICATOR_CPHS,
                obtainMessage(EVENT_GET_VOICE_MAIL_INDICATOR_CPHS_DONE));
        mRecordsToLoad++;
        // Same goes for Call Forward Status indicator: fetch both
        // EF[CFIS] and CPHS-EF, with EF[CFIS] preferred.
        loadCallForwardingRecords();
        getSpnFsm(true, null);
        mFh.loadEFTransparent(EF_SPDI, obtainMessage(EVENT_GET_SPDI_DONE));
        mRecordsToLoad++;
        mFh.loadEFLinearFixed(EF_PNN, 1, obtainMessage(EVENT_GET_PNN_DONE));
        mRecordsToLoad++;
        mFh.loadEFTransparent(EF_SST, obtainMessage(EVENT_GET_SST_DONE));
        mRecordsToLoad++;
        mFh.loadEFTransparent(EF_INFO_CPHS, obtainMessage(EVENT_GET_INFO_CPHS_DONE));
        mRecordsToLoad++;
        mFh.loadEFTransparent(EF_CSP_CPHS,obtainMessage(EVENT_GET_CSP_CPHS_DONE));
        mRecordsToLoad++;
        mFh.loadEFTransparent(EF_GID1, obtainMessage(EVENT_GET_GID1_DONE));
        mRecordsToLoad++;
        mFh.loadEFTransparent(EF_GID2, obtainMessage(EVENT_GET_GID2_DONE));
        mRecordsToLoad++;

        mFh.loadEFTransparent(EF_PLMN_W_ACT, obtainMessage(EVENT_GET_PLMN_W_ACT_DONE));
        mRecordsToLoad++;
        mFh.loadEFTransparent(EF_OPLMN_W_ACT, obtainMessage(EVENT_GET_OPLMN_W_ACT_DONE));
        mRecordsToLoad++;
        mFh.loadEFTransparent(EF_HPLMN_W_ACT, obtainMessage(EVENT_GET_HPLMN_W_ACT_DONE));
        mRecordsToLoad++;
        mFh.loadEFTransparent(EF_EHPLMN, obtainMessage(EVENT_GET_EHPLMN_DONE));
        mRecordsToLoad++;
        mFh.loadEFTransparent(EF_FPLMN, obtainMessage(
                    EVENT_GET_FPLMN_DONE, HANDLER_ACTION_NONE, -1));
        mRecordsToLoad++;
        loadEfLiAndEfPl();
        mFh.getEFLinearRecordSize(EF_SMS, obtainMessage(EVENT_GET_SMS_RECORD_SIZE_DONE));
        // XXX should seek instead of examining them all
        if (false) { // XXX
            mFh.loadEFLinearFixedAll(EF_SMS, obtainMessage(EVENT_GET_ALL_SMS_DONE));
            mRecordsToLoad++;
        }
        if (CRASH_RIL) {
            String sms = "0107912160130310f20404d0110041007030208054832b0120"
                         + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
                         + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
                         + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
                         + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
                         + "ffffffffffffffffffffffffffffff";
            byte[] ba = IccUtils.hexStringToBytes(sms);

            mFh.updateEFLinearFixed(EF_SMS, 1, ba, null,
                            obtainMessage(EVENT_MARK_SMS_READ_DONE, 1));
        }
        if (DBG) log("fetchSimRecords " + mRecordsToLoad + " requested: " + mRecordsRequested);//总共加载的卡文件
    }

查看loadEFTransparent,通过SIMIO管道发送GET_RESPONSE命令获取文件的大小,回调EVENT_GET_BINARY_SIZE_DONE

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

关键打印iccIO,command为命令,fileId是对应的卡文件,从log可以看出获取到的卡文件结果
在这里插入图片描述
查看EVENT_GET_BINARY_SIZE_DONE的处理,获取到大小后,发送READ_BINARY,回调EVENT_READ_BINARY_DONE

size = ((data[RESPONSE_DATA_FILE_SIZE_1] & 0xff) << 8)
                       + (data[RESPONSE_DATA_FILE_SIZE_2] & 0xff);
                mCi.iccIOForApp(COMMAND_READ_BINARY, fileid, getEFPath(fileid),
                                0, 0, size, null, null, mAid,
                                obtainMessage(EVENT_READ_BINARY_DONE,
                                              fileid, 0, response));

查看EVENT_READ_BINARY_DONE的处理

sendResult(response, result.payload, null);
response.sendToTarget();

返回到SIMRecords中的fetchSimRecords,调用loadEFTransparent会回调EVENT_GET_XXX,最终都会调用onRecordLoaded
有pin或者puk锁或者网络锁时调用onLockedAllRecordsLoaded,无锁时调用onAllRecordsLoaded

 protected void onRecordLoaded() {
        // One record loaded successfully or failed, In either case
        // we need to update the recordsToLoad count
        mRecordsToLoad -= 1;
        if (DBG) log("onRecordLoaded " + mRecordsToLoad + " requested: " + mRecordsRequested);

        if (getRecordsLoaded()) {
            onAllRecordsLoaded();
        } else if (getLockedRecordsLoaded() || getNetworkLockedRecordsLoaded()) {
            onLockedAllRecordsLoaded();
        } else if (mRecordsToLoad < 0) {
            loge("recordsToLoad <0, programmer error suspected");
            mRecordsToLoad = 0;
        }
    }

先查看onLockedAllRecordsLoaded,会通过mLockedRecordsLoadedRegistrants和mNetworkLockedRecordsLoadedRegistrants发送到UiccProfile,分别回调EVENT_ICC_LOCKED和EVENT_NETWORK_LOCKED

private void onLockedAllRecordsLoaded() {
        setSimLanguageFromEF();
        setVoiceCallForwardingFlagFromSimRecords();
        if (mLockedRecordsReqReason == LOCKED_RECORDS_REQ_REASON_LOCKED) {
            mLockedRecordsLoadedRegistrants.notifyRegistrants(new AsyncResult(null, null, null));
        } else if (mLockedRecordsReqReason == LOCKED_RECORDS_REQ_REASON_NETWORK_LOCKED) {
            mNetworkLockedRecordsLoadedRegistrants.notifyRegistrants(
                    new AsyncResult(null, null, null));
        } else {
            loge("onLockedAllRecordsLoaded: unexpected mLockedRecordsReqReason "
                    + mLockedRecordsReqReason);
        }
    }

private void registerCurrAppEvents() {
        // In case of locked, only listen to the current application.
        if (mIccRecords != null) {
            mIccRecords.registerForLockedRecordsLoaded(mHandler, EVENT_ICC_LOCKED, null);
            mIccRecords.registerForNetworkLockedRecordsLoaded(mHandler, EVENT_NETWORK_LOCKED, null);
        }
    }

查看onAllRecordsLoaded,设置一些属性后,通过mRecordsLoadedRegistrants发送通知,CatService,DcTracker,GsmCdmaPhone,ServiceStateTracker,UiccProfile会进行监听

mRecordsLoadedRegistrants.notifyRegistrants

查看线性固定文件加载loadEFLinearFixed,与上面类似,先通过GET_RESPONSE(0xC0)获取大小,再通过READ_RECORD(0xb2)获取数据

五.总结

log关键字`

fetchSimRecords|SIM_IO

流程:透明文件时先GET_RESPONSE(0xC0)获取大小,再READ_BINARY(0xb0)获取数据
线性固定文件时先GET_RESPONSE(0xC0)获取大小,再READ_RECORD(0xb2)获取数据

卡文件加载完成后,pin/puk锁会通过mLockedRecordsLoadedRegistrants发送到UiccProfile,网格锁通过mNetworkLockedRecordsLoadedRegistrants发送到UiccProfile,无锁的卡通过mRecordsLoadedRegistrants发送

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值