現有的手機中使用的卡SIM, USIM,UIM等統稱為:UICC——Universal Integrated Circuit Card;
這些卡之間數據結構是有些區別的,先來看看SIM卡的文件結構。
一 Sim文件系統數據結構
1 sim卡文件系統
SIM card file system structure:
2 文件結構
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.
3 文件類型
Transparent:
透明結構的EF 由一個字節序列組成。當文件讀或更新,字節序列活動是參照相對地
址(OFFSET)進行的,相對地址可表示出起始操作的地址(用字節表示)和讀出、更新的
字節數。透明EF 的第一個字節有一個相對地址‘0000’。EF 主體的數據長度在EF 的文件
頭中。
Linear Fixed File:
線性固定EF 文件由一個記錄長度固定的記錄序列組成。第一個記錄記錄號是1。記錄
的長度和記錄長度與記錄個數的乘積存放在EF 文件頭中。
Cyclic:
循環文件用於以時間順序存儲的記錄,當所有的記錄空間都占用時,新的存儲數據將
覆蓋最舊的信息。
訪問不同的文件類型,使用的方式也將不同。對於USIM,RUIM等卡基本文件結構應該是一致的,局部存儲信息的單元,位置不同而已。
二 UICC卡數據讀寫
1 UICC框架類結構
手機需要關注的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數據以及接收讀取的結果;
2 UICC 框架執行流程
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通知;
狀態更新流程如下:
3 UICC數據讀取過程
發出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.