一.卡文件系统
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发送