Telephony-Uicc双卡管理
来源:互联网 发布:淘宝发布宝贝商家编码 编辑:程序博客网 时间:2024/06/05 02:44
这个部分原稿来自于Android6.0的phone应用源码分析(9)——UICC卡管理
1.2 UiccController
UiccController负责了所有有关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数据以及接收读取的结果;
它们之间的关系大致如下图所示:
UiccController在APP构造phones的过程中被创建,从其构造函数来看,它注册监听了RILJ的多个状态变化。对UICC卡状态的变化、Radio的状态进行了监听。
private UiccController(Context c, CommandsInterface []ci) {
if (DBG) log(“Creating UiccController”);
mContext = c;
mCis = ci;
for (int i = 0; i < mCis.length; i++) {
Integer index = new Integer(i);
mCis[i].registerForIccStatusChanged(this, EVENT_ICC_STATUS_CHANGED, index);
//注册RADIO状态变化监听
if (DECRYPT_STATE.equals(SystemProperties.get(“vold.decrypt”))) {
mCis[i].registerForAvailable(this, EVENT_ICC_STATUS_CHANGED, index);
} else {
mCis[i].registerForOn(this, EVENT_ICC_STATUS_CHANGED, index);
}
mCis[i].registerForNotAvailable(this, EVENT_RADIO_UNAVAILABLE, index);
mCis[i].registerForIccRefresh(this, EVENT_SIM_REFRESH, index);
}
}
从上面UiccController的私有构造函数可以看出,UiccController是一个单例,它管理了所有的Uicc卡。这里首先假设底层UICC卡状态发生变化。则根据前几章的经验,RILJ将广播消息给此消息的所有注册者,这里当然是UiccController啦。UiccController在其handleMessage()中被处理。
public void handleMessage (Message msg) {
synchronized (mLock) {
Integer index = getCiIndex(msg);
if (index < 0 || index >= mCis.length) { Rlog.e(LOG_TAG, "Invalid index : " + index + " received with event " + msg.what); return; } AsyncResult ar = (AsyncResult)msg.obj; switch (msg.what) { case EVENT_ICC_STATUS_CHANGED://这里,第一次进入这里 if (DBG) log("Received EVENT_ICC_STATUS_CHANGED, calling getIccCardStatus"); mCis[index].getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE, index)); break; case EVENT_GET_ICC_STATUS_DONE://查询好状态后进入这里 if (DBG) log("Received EVENT_GET_ICC_STATUS_DONE"); onGetIccCardStatusDone(ar, index); break; case EVENT_RADIO_UNAVAILABLE: if (DBG) log("EVENT_RADIO_UNAVAILABLE, dispose card"); if (mUiccCards[index] != null) { mUiccCards[index].dispose(); } mUiccCards[index] = null; mIccChangedRegistrants.notifyRegistrants(new AsyncResult(null, index, null)); break; case EVENT_SIM_REFRESH: if (DBG) log("Received EVENT_SIM_REFRESH"); onSimRefresh(ar, index); break; default: Rlog.e(LOG_TAG, " Unknown Event " + msg.what); } }}
可以看到,UiccController在接受到RILJ手机卡状态变化的广播后,首先是调用RILJ的方法查询了UICC卡状态,然后再回到UiccController.handlerMessage提取出UICC卡的状态。这种处理方式与之前CallTracker对来电的处理基本上是一致的,这里就不再详述了。直接进入对卡状态的处理:
private synchronized void onGetIccCardStatusDone(AsyncResult ar, Integer index) {
if (ar.exception != null) {
Rlog.e(LOG_TAG,”Error getting ICC status. ”
+ “RIL_REQUEST_GET_ICC_STATUS should ”
+ “never return an error”, ar.exception);
return;
}
if (!isValidCardIndex(index)) {
Rlog.e(LOG_TAG,”onGetIccCardStatusDone: invalid index : ” + index);
return;
}
IccCardStatus status = (IccCardStatus)ar.result; if (mUiccCards[index] == null) { //如果没有卡记录,则是新卡,需要new一个UiccCard mUiccCards[index] = new UiccCard(mContext, mCis[index], status, index); } else { //如果已经存在,跟新已有的UiccCard就行了 mUiccCards[index].update(mContext, mCis[index] , status); } if (DBG) log("Notifying IccChangedRegistrants"); mIccChangedRegistrants.notifyRegistrants(new AsyncResult(null, index, null));}
这里重点关注UiccCard状态的update。
public void update(Context c, CommandsInterface ci, IccCardStatus ics) {
synchronized (mLock) {
CardState oldState = mCardState;
mCardState = ics.mCardState;
mUniversalPinState = ics.mUniversalPinState;
mGsmUmtsSubscriptionAppIndex = ics.mGsmUmtsSubscriptionAppIndex;
mCdmaSubscriptionAppIndex = ics.mCdmaSubscriptionAppIndex;
mImsSubscriptionAppIndex = ics.mImsSubscriptionAppIndex;
mContext = c;
mCi = ci;
//触发UiccApplication的状态更新 if (DBG) log(ics.mApplications.length + " applications"); for ( int i = 0; i < mUiccApplications.length; i++) { if (mUiccApplications[i] == null) { //新应用 if (i < ics.mApplications.length) { mUiccApplications[i] = new UiccCardApplication(this, ics.mApplications[i], mContext, mCi); } } else if (i >= ics.mApplications.length) { //移除应用 mUiccApplications[i].dispose(); mUiccApplications[i] = null; } else { //更新已有的应用状态 mUiccApplications[i].update(ics.mApplications[i], mContext, mCi); } } createAndUpdateCatService(); // Reload the carrier privilege rules if necessary. log("Before privilege rules: " + mCarrierPrivilegeRules + " : " + mCardState); if (mCarrierPrivilegeRules == null && mCardState == CardState.CARDSTATE_PRESENT) { mCarrierPrivilegeRules = new UiccCarrierPrivilegeRules(this, mHandler.obtainMessage(EVENT_CARRIER_PRIVILIGES_LOADED)); } else if (mCarrierPrivilegeRules != null && mCardState != CardState.CARDSTATE_PRESENT) { mCarrierPrivilegeRules = null; } sanitizeApplicationIndexes(); RadioState radioState = mCi.getRadioState(); if (DBG) log("update: radioState=" + radioState + " mLastRadioState=" + mLastRadioState); // No notifications while radio is off or we just powering up if (radioState == RadioState.RADIO_ON && mLastRadioState == RadioState.RADIO_ON) { if (oldState != CardState.CARDSTATE_ABSENT && mCardState == CardState.CARDSTATE_ABSENT) { if (DBG) log("update: notify card removed"); mAbsentRegistrants.notifyRegistrants(); mHandler.sendMessage(mHandler.obtainMessage(EVENT_CARD_REMOVED, null)); } else if (oldState == CardState.CARDSTATE_ABSENT && mCardState != CardState.CARDSTATE_ABSENT) { if (DBG) log("update: notify card added"); mHandler.sendMessage(mHandler.obtainMessage(EVENT_CARD_ADDED, null)); } } mLastRadioState = radioState; }}
可以看到UiccCard根据RILJ上报的卡状态消息,更新了自己,并导致了UiccCardApplication.update()。
void update (IccCardApplicationStatus as, Context c, CommandsInterface ci) {
synchronized (mLock) {
if (mDestroyed) {
loge(“Application updated after destroyed! Fix me!”);
return;
}
if (DBG) log(mAppType + " update. New " + as); mContext = c; mCi = ci; AppType oldAppType = mAppType; AppState oldAppState = mAppState; PersoSubState oldPersoSubState = mPersoSubState; mAppType = as.app_type; mAuthContext = getAuthContext(mAppType); mAppState = as.app_state; mPersoSubState = as.perso_substate; mAid = as.aid; mAppLabel = as.app_label; mPin1Replaced = (as.pin1_replaced != 0); mPin1State = as.pin1; mPin2State = as.pin2; 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); } if (mPersoSubState != oldPersoSubState && mPersoSubState == PersoSubState.PERSOSUBSTATE_SIM_NETWORK) { notifyNetworkLockedRegistrantsIfNeeded(null); } if (mAppState != oldAppState) { if (DBG) log(oldAppType + " changed state: " + oldAppState + " -> " + mAppState); // 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) { queryFdn(); //查询fdn queryPin1State();//查询Pin1功能开关是否开启 } notifyPinLockedRegistrantsIfNeeded(null); notifyReadyRegistrantsIfNeeded(null); } }}
UiccApplication首先是更新了自己的状态。特别需要注意update的最后一个大if。当已经确认状态发生变化时,又对mAppState做了检测,在mAppState==AppState.APPSTATE_READY的情况下又查询了fdn和pin。难道AppState.APPSTATE_READY并不代表pin已经通过了么?查看notifyPinLockedRegistrantsIfNeeded(),notifyReadyRegistrantsIfNeeded()。也许将这段代码等价有下面的代码更容易理解(这里一定要仔细阅读前面的状态说明)。
queryPin1State只是为了查询pin码功能是否开启,处理mIccLockEnabled 和mPin1State两个状态不一致的情况
/* REMOVE when mIccLockEnabled is not needed/
private void onQueryFacilityLock(AsyncResult ar) {
synchronized (mLock) {
if(ar.exception != null) {
if (DBG) log(“Error in querying facility lock:” + ar.exception);
return;
}
int[] ints = (int[])ar.result; if(ints.length != 0) { if (DBG) log("Query facility lock : " + ints[0]); mIccLockEnabled = (ints[0] != 0); if (mIccLockEnabled) { mPinLockedRegistrants.notifyRegistrants(); } // Sanity check: we expect mPin1State to match mIccLockEnabled. // When mPin1State is DISABLED mIccLockEanbled should be false. // When mPin1State is ENABLED mIccLockEnabled should be true. // // Here we validate these assumptions to assist in identifying which ril/radio's // have not correctly implemented GET_SIM_STATUS switch (mPin1State) { case PINSTATE_DISABLED: if (mIccLockEnabled) { loge("QUERY_FACILITY_LOCK:enabled GET_SIM_STATUS.Pin1:disabled." + " Fixme"); } break; case PINSTATE_ENABLED_NOT_VERIFIED: case PINSTATE_ENABLED_VERIFIED: case PINSTATE_ENABLED_BLOCKED: case PINSTATE_ENABLED_PERM_BLOCKED: if (!mIccLockEnabled) { loge("QUERY_FACILITY_LOCK:disabled GET_SIM_STATUS.Pin1:enabled." + " Fixme"); } case PINSTATE_UNKNOWN: default: if (DBG) log("Ignoring: pin1state=" + mPin1State); break; } } else { loge("Bogus facility lock response"); } }}
假设UICC卡的pin功能开启了,则mAppState == AppState.APPSTATE_PIN(或类似),且并没有被解锁,则注册监听此消息的类被通知。这里有两个地方注册监听了此消息。一个是IccCardProxy。另一个是SimRecord,代码就不贴了。SimRecord的处理就是在Pin锁定的状态下仍然读取少量的UICC卡EF信息。
可以看到,它实际上的处理就是将需要解锁的信息写入数据库,以便APP查询。中间过程就不详细叙述了,大致过程如下图所示:
当手机开机或OnSystemReady触发了KeyguardService.OnSystemReady,接着KeyguardViewMediator.onSystemReady();接着doKeyguardLocked。查询UICC卡的状态,在锁pin的时候显示解Pin界面。
对于无pin锁或已经解pin了,mAppState == AppState.APPSTATE_READY。其处理过程基本类似。IccCardProxy将状态信息写入数据库。而RuimRecords或SimRecords则是读取uicc卡的信息。
private void fetchRuimRecords() {
mRecordsRequested = true;
if (DBG) log("fetchRuimRecords " + mRecordsToLoad); mCi.getIMSIForApp(mParentApp.getAid(), obtainMessage(EVENT_GET_IMSI_DONE)); mRecordsToLoad++; mFh.loadEFTransparent(EF_ICCID, obtainMessage(EVENT_GET_ICCID_DONE)); mRecordsToLoad++; mFh.loadEFTransparent(EF_PL, obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfPlLoaded())); mRecordsToLoad++; mFh.loadEFTransparent(EF_CSIM_LI, obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimLiLoaded())); mRecordsToLoad++; mFh.loadEFTransparent(EF_CSIM_SPN, obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimSpnLoaded())); mRecordsToLoad++; mFh.loadEFLinearFixed(EF_CSIM_MDN, 1, obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimMdnLoaded())); mRecordsToLoad++; mFh.loadEFTransparent(EF_CSIM_IMSIM, obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimImsimLoaded())); mRecordsToLoad++; mFh.loadEFLinearFixedAll(EF_CSIM_CDMAHOME, obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimCdmaHomeLoaded())); mRecordsToLoad++; // Entire PRL could be huge. We are only interested in // the first 4 bytes of the record. mFh.loadEFTransparent(EF_CSIM_EPRL, 4, obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimEprlLoaded())); mRecordsToLoad++; mFh.loadEFTransparent(EF_CSIM_MIPUPP, obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimMipUppLoaded())); mRecordsToLoad++; if (DBG) log("fetchRuimRecords " + mRecordsToLoad + " requested: " + mRecordsRequested); // Further records that can be inserted are Operator/OEM dependent}
总结类图结构和以上UICC管理流程如下图所示:
首先是RILJ检测到UICC卡状态(或Radio状态的变化),向上层UiccController广播一个消息,UiccController接收到此消息后,调用RILJ的有关函数获得状态的具体信息,接着根据此状态信息,更新UiccCard,接着更新UiccCardApplicaton。UiccCardApplication接下来的处理分了两条线:1,向IccCardProxy广播卡信息,IccCardProxy将卡的状态信息写入Telephony.db以供APP查询;2,通过其内部类IccFileHandler读取Uicc卡文件,并存入IccRecord,读取完毕后再通过与1类似的方式将EVENT_RECORDS_LOADED状态写入Telephony.db。
- Telephony-Uicc双卡管理
- android telephony 之 UICC 卡数据读写及 UICC 框架结构
- android telephony 之 UICC 卡数据读写及 UICC 框架结构
- android telephony uicc
- android telephony 工作流程(一)--UICC概述及SIM卡文件系统
- android telephony 工作流程(二)--UICC上层实现框架
- android telephony 工作流程(三)--UICC CARD & APP 更新
- UICC
- UICC
- UICC
- Android6.0的phone应用源码分析(9)——UICC卡管理
- Android6.0的phone应用源码分析(9)——UICC卡管理
- telephony
- UICC,USIM卡与SIM的区别
- UICC,USIM卡与SIM的区别
- UICC,USIM卡与SIM的区别
- UICC,USIM卡与SIM的区别
- UICC,USIM卡与SIM的区别
- Android 的jar混淆踩坑之旅
- IT的青春饭
- scala map/list/array/的常用内置遍历操作总结
- 【java】强引用、弱引用、软引用、虚引用
- laravel中将session由文件保存改为数据库保存
- Telephony-Uicc双卡管理
- LeetCode刷题(C++)——Convert Sorted List to Binary Search Tree(Normal)
- 1052. 卖个萌 (20)
- 初识HTML/CSS之CSS篇
- 地毯填补问题(分治)(AOJ 859)
- Thymeleaf教程(一)简介
- angr:基于python的二进制分析框架 安装与使用
- POJ
- 生活