现有的手机中使用的卡SIM, USIM,UIM等统称为:UICC——Universal Integrated Circuit Card;
这些卡之间数据结构是有些区别的,先来看看SIM卡的文件结构。
SIM card file system structure:
MF:The root level of the file system is known as the Master file.
DF:Directories are known as Dedicated files and are of a fixed size.
EF:Individual records (or files) are known as Elementary files.
All files are identified as an address (a DWORD value), rather than a filename.
Transparent:
透明结构的EF 由一个字节序列组成。当文件读或更新,字节序列活动是参照相对地
址(OFFSET)进行的,相对地址可表示出起始操作的地址(用字节表示)和读出、更新的
字节数。透明EF 的第一个字节有一个相对地址‘0000’。EF 主体的数据长度在EF 的文件
头中。
Linear Fixed File:
线性固定EF 文件由一个记录长度固定的记录序列组成。第一个记录记录号是1。记录
的长度和记录长度与记录个数的乘积存放在EF 文件头中。
Cyclic:
循环文件用于以时间顺序存储的记录,当所有的记录空间都占用时,新的存储数据将
覆盖最旧的信息。
访问不同的文件类型,使用的方式也将不同。对于USIM,RUIM等卡基本文件结构应该是一致的,局部存储信息的单元,位置不同而已。
手机需要关注的UICC包括:数据读写记录,状态变化管理;Android中是管理UICC的框架代码位于:
frameworks\opt\telephony\src\java\com\android\internal\telephony\uicc\
基本框架类得结构图:
对于不同的卡会有不同的类与之对应,这些类的作用:
UiccController:整个UICC相关信息的控制接口;监控SIM状态变化;
UiccCard:UICC卡代码中对应的抽象;
IccCardStatus:维护UICC卡的状态:CardState & PinState;
UiccCardApplication:UICC具体的一个应用;负责Pin Puk密码设置解锁,数据的读取,存储;
CatService:负责SIM Toolkit相关;
IccConstants:SIM File Address;存储不同数据在Sim卡上的字段地址;SIMRecords等基类;
SIMRecords /RuimRecords:记录SIM卡上的数据;
IccFileHandler:读取SIM数据以及接收读取的结果;
UICC的状态监控是在UiccController中进行的;
UiccController构造函数:
private UiccController(Context c, CommandsInterface ci) { mCi = ci; //注册UICC卡状态变化监听
mCi.registerForIccStatusChanged(this, EVENT_ICC_STATUS_CHANGED, null); //注册RADIO状态变化监听
mCi.registerForOn(this, EVENT_ICC_STATUS_CHANGED, null); mCi.registerForAvailable(this, EVENT_ICC_STATUS_CHANGED, null); mCi.registerForNotAvailable(this, EVENT_RADIO_UNAVAILABLE, null); }
UICC Card状态有变化处理:
public void handleMessage (Message msg) { switch (msg.what) { case EVENT_ICC_STATUS_CHANGED: //UICC状态变化,获取UICC状态
mCi.getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE)); break;
case EVENT_GET_ICC_STATUS_DONE: //UICC状态变化,获取UICC状态返回处理
AsyncResult ar = (AsyncResult)msg.obj; onGetIccCardStatusDone(ar); break; } }
UICC Card处理状态变化:
private synchronized void onGetIccCardStatusDone(AsyncResult ar) { //返回的数据结构IccCardStatus
IccCardStatus status = (IccCardStatus)ar.result; //更新Uicc Card状态 ,若UiccCard未创建则新创建 //新创建也是一样调用UiccCard@update
if (mUiccCard == null) { //Create new card
mUiccCard = new UiccCard(mContext, mCi, status); } else { //Update already existing card
mUiccCard.update(mContext, mCi , status); } }
UICC Card状态更新:
public void update(Context c, CommandsInterface ci, IccCardStatus ics) { synchronized (mLock) { mCardState = ics.mCardState; mUniversalPinState = ics.mUniversalPinState; //update applications UiccApplications构造则新创建 //新创建跟update流程一致
for ( int i = 0; i < mUiccApplications.length; i++) { if (mUiccApplications[i] == null) { //Create newly added Applications
if (i < ics.mApplications.length) { mUiccApplications[i] = new UiccCardApplication(this, ics.mApplications[i], mContext, mCi); } } else if (i >= ics.mApplications.length) { //Delete removed applications
mUiccApplications[i].dispose(); mUiccApplications[i] = null; } else { //Update the rest
mUiccApplications[i].update(ics.mApplications[i], mContext, mCi); } } //STK相关
createAndUpdateCatService(); } }
Uicc Applications更新:
void update (IccCardApplicationStatus as, Context c, CommandsInterface ci) { synchronized (mLock) { //更新type state pin ……
AppType oldAppType = mAppType; AppState oldAppState = mAppState; mAppType = as.app_type; mAppState = as.app_state; …… //APP Type变化更新
if (mAppType != oldAppType) { if (mIccFh != null) { mIccFh.dispose();} if (mIccRecords != null) { mIccRecords.dispose();} mIccFh = createIccFileHandler(as.app_type); mIccRecords = createIccRecords(as.app_type, c, ci); } //APP State变化更新
if (mAppState != oldAppState) { // If the app state turns to APPSTATE_READY, then query FDN status, //as it might have failed in earlier attempt.
if (mAppState == AppState.APPSTATE_READY) { //FDN查询
queryFdn(); //PIN查询
queryPin1State(); } //PIN状态通知
notifyPinLockedRegistrantsIfNeeded(null); //UICC Ready否状态通知
notifyReadyRegistrantsIfNeeded(null); } } }
这里会根据UICC的状态继续下一步的操作:
如果UICC需要PIN解锁,则会发出需要Pin码锁通知;进行UICC pin码输入解锁,然后状态变化,
继续更新UICC Card,Uicc Applications直到UICC状态Ready;
如果UICC已经ready,则发出UICC Ready通知;
状态更新流程如下:
发出UICC Ready的通知是在UiccApplications中,
在接收到UICC Ready的通知后,就可以进行UICC中相关数据的读写;
这个有在IccRecords类中进行,以SimRecors为例:
public SIMRecords(UiccCardApplication app, Context c, CommandsInterface ci) { super(app, c, ci); //电话号码
adnCache = new AdnRecordCache(mFh); //监听UiccApplications 发出Sim Ready通知
mParentApp.registerForReady(this, EVENT_APP_READY, null); }
SIMRecords消息处理:
public void handleMessage(Message msg) { switch (msg.what) { case EVENT_APP_READY: onReady(); break; //IO events 通过IccFileHandler数据读取SIM数据,返回结果处理
case EVENT_GET_IMSI_DONE: …… break; case EVENT_GET_MBI_DONE: …… break; case EVENT_GET_AD_DONE: case EVENT_GET_SPN_DONE: break; …… } }
监听到SIM Ready消息:
public void onReady() { fetchSimRecords(); }
protected void fetchSimRecords() { //通过IccFileHandler向 RIL发送读取数据的消息
mFh.loadEFTransparent(EF_ICCID, obtainMessage(EVENT_GET_ICCID_DONE)); recordsToLoad++; // Record number is subscriber profile
mFh.loadEFLinearFixed(EF_MBI, 1, obtainMessage(EVENT_GET_MBI_DONE)); recordsToLoad++; mFh.loadEFTransparent(EF_AD, obtainMessage(EVENT_GET_AD_DONE)); recordsToLoad++; // Record number is subscriber profile
mFh.loadEFLinearFixed(EF_MWIS, 1, obtainMessage(EVENT_GET_MWIS_DONE)); recordsToLoad++; …… }
IccFileHandler数据读取:
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); }
loadEFTransparent和loadEFLinearFixed,就是针对不同的文件格式,
实际都是调用RIL_JAVA:
void iccIOForApp (int command, int fileid, String path, int p1, int p2, int p3, String data, String pin2, String aid, Message result) { …… }
RIL_iccIOForApp函数的参数含义:
command:读写更新……操作命令
final int COMMAND_READ_BINARY = 0xb0;
final int COMMAND_UPDATE_BINARY = 0xd6;
final int COMMAND_READ_RECORD = 0xb2;
final int COMMAND_UPDATE_RECORD = 0xdc;
final int COMMAND_SEEK = 0xa2;
final int COMMAND_GET_RESPONSE = 0xc0;
……
fileid:数据字段在SIM文件系统中的地址 :例如Plmn:0x6F30
path: 此数据字段上级所有目录地址:
例如Plmn的Path:MF + DF_GSM = "0x3F000x7F20"
地址字段都需要根据UICC文件系统结构,地址决定
p1:
p2:
p3:
data:
pin2:
aid: 由UICC传递上来的
result:回调Message
从3GPP SIM相关协议可以看到,P1,P2,P3等这些参数的含义:
S:stands for data sent by the ME
R:stands for data received by the ME
Offset is coded on 2 bytes where P1 gives thehigh order byte and P2 the low order byte.
'00 00' means no offset and reading/updating starts with the first byte
'00 01' means that reading/updating starts with the second byte.