现有的手机中使用的卡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.