一.卡文件系统


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 linenew AdnRecordLoader(mFh).loadFromEF(EF_MSISDN, getExtFromEf(EF_MSISDN), 1,obtainMessage(EVENT_GET_MSISDN_DONE));mRecordsToLoad++;// Record number is subscriber profilemFh.loadEFLinearFixed(EF_MBI, 1, obtainMessage(EVENT_GET_MBI_DONE));mRecordsToLoad++;mFh.loadEFTransparent(EF_AD, obtainMessage(EVENT_GET_AD_DONE));mRecordsToLoad++;// Record number is subscriber profilemFh.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 allif (false) { // XXXmFh.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 countmRecordsToLoad -= 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发送

SIM卡文件介绍及加载相关推荐

  1. jQuery EasyUI动态添加控件或者ajax加载页面后不能自动渲染问题的解决方法

    博客分类: jquery-easyui jQueryAjax框架HTML  现象: AJAX返回的html无法做到自动渲染为EasyUI的样式.比如:class="easyui-layout ...

  2. WebBrowser控件判断完全加载中DocumentCompleted和Navigated的关系

    WebBrowser控件判断完全加载使用DocumentCompleted是有问题的,所以有人提出可以使用判断DocumentCompleted的次数和Navigated次数完全相等的方法判断是否完全 ...

  3. 安卓案例:列表控件上拉加载更多

    文章目录 一.上拉加载更多使用场景 二.案例演示 - 列表控件上拉加载更多 (一)运行效果 (二)涉及知识点 (三)实现步骤 1.创建安卓应用[ListViewLoadMore] 2.将背景图片拷贝到 ...

  4. Cesium教程 (1) 界面介绍-3dtiles加载-更改鼠标操作设置

    Cesium教程 (1) 界面介绍-3dtiles加载-更改鼠标操作设置 目录 1. 界面介绍 (7-8互换位置) 2. 数据转3dtiles 3. 代码详解 4. 其他 1. 界面介绍 (7-8互换 ...

  5. wps office oa控件 痕迹_WPS加载项案例应用回顾

    2019年的武汉,金山办公召开了第一届开发者大会,主题是「开放赋能.合作共赢」,2020年8月29至30日,在重庆召开了第二届,主题是「在一起.创未来」.上一届发布了WPS PC Office的WPS ...

  6. asp.net读取用户控件,自定义加载用户控件

    1.自定义加载用户控件 ceshi.aspx页面 <html><body> <div id="divControls" runat="ser ...

  7. windowmediaplayer控件出现未能加载的问题

    问题:当添加windowsmediaplayer控件后,有时候会发现窗体设计器出现下图提示,无法加载设计器. 方法:将该方案的属性中生成选项下的平台目标选择anycpu(自己莫名将平台目标设置成X64 ...

  8. FlexCell控件初始化以及加载数据集[原创]

    '================================写在之前的话 抱歉,一直没有时间,所以FlexCell作者给我的几种加载数据集方法的代码一直没有发出来. 同时再次感谢FlexCell ...

  9. 树的懒加载怎么用ajax调接口,ElementUI tree树形控件的懒加载使用

    先看效果: image 1.界面中: :data="treeData" :props="defaultProps" :load="loadNode&q ...

最新文章

  1. Centos6.4 + PHP5.5.11 + Mysql5.6.16 + Tnginx2.02 + Memcached1.4.17 + Redis2.2
  2. 我思故我在之编程规范及编程思想篇
  3. 【Java7】练习:选角色,挑苹果,员工类,换心脏,斗地主,发工资,客户信息管理软件,开发团队调度系统
  4. 数据库-优化-子查询优化
  5. unity 里调试native code
  6. Java 8的烹调方式– Lambda项目
  7. android studio入门
  8. Vue项目开发过程中解决跨域问题(vue.config.js结合axios)
  9. 使用Cloud Studio在线编写、调试和管理Spring Boot应用
  10. (四)Java中的多线程之间实现同步+多线程并发同步
  11. Linux SSh scp使用【远程文件/目录的传输】
  12. C# 构造函数base()实例演示
  13. 设计思想之高内聚低耦合
  14. 小 Mil 来了!Milvus 智能问答机器人上线
  15. JS实现放大镜特效原理解析
  16. 使用 C++ 开发出 【飞机大战】
  17. html css javascript七夕情人节表白网页【樱花雨3D相册】超好看
  18. ae手机版特效软件如何制作抖音热门视频飞天特效的教程
  19. 【Nodejs】留言板案例
  20. spring源码分析系列(一)

热门文章

  1. python3:类和实例
  2. Linux C/C++后台开发高级架构师进阶指南-剑指腾讯T9
  3. window.open被浏览器拦截的问题(附解决方案)
  4. 基于.net的C# Windows Media Player控件
  5. Python-函数的初始
  6. 部署Zabbix集中监控系统
  7. 阿里云轻量应用服务器应用镜像——WordPress 4.8.1
  8. pandas数据分析使用技巧
  9. Vue3教程:结合 Ant-Design-of-Vue 实践 Composition API
  10. Aptina公司在台湾成立工程中心