这个部分原稿来自于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。