这部分严格来说并不属于phone应用,但是考虑到手机卡与phone应用的密切关系,也就放在同一个系列了。
1.1 UICC卡的各种状态
这些内容一般都被定义在IccCardStatus类中。
首先是卡的状态:
public enumCardState {
CARDSTATE_ABSENT, //表示掉卡
CARDSTATE_PRESENT, //表示卡正常
CARDSTATE_ERROR; //表示卡出现了错误
}
接着是描述Pin的状态
publicenum PinState {
PINSTATE_UNKNOWN,// PIN码状态不确定
PINSTATE_ENABLED_NOT_VERIFIED, //PIN码锁定,用户输入的PIN码错误,无法进入手机
PINSTATE_ENABLED_VERIFIED,// PINM码锁定,用户输入正确的PIN码,进入手机
PINSTATE_DISABLED,// 没有进行PIN码锁定
PINSTATE_ENABLED_BLOCKED,//PIN码解锁失败,提示输入PUK码
PINSTATE_ENABLED_PERM_BLOCKED;// PUK码解锁失败后,永久锁定
}
IccCardApplicationStatus,描述UiccCardApplication的信息,每一个IccCardApplicationStatus,对应于一个UiccCardApplication。IccCardApplicationStatus实质上是多个卡应用状态的集合。包括AppType、AppState、PersoSubState以及其他状态。如下
public class IccCardApplicationStatus {
//描述UiccCardApplication的类型,和卡的类型对应
public enum AppType{
APPTYPE_UNKNOWN,
APPTYPE_SIM, //GSM 卡
APPTYPE_USIM, // WCDMA 卡
APPTYPE_RUIM, // CDMA 卡
APPTYPE_CSIM,
APPTYPE_ISIM
};
//描述UiccCardApplication的状态,和卡的状态对应
public enum AppState{
APPSTATE_UNKNOWN, //卡不存在
APPSTATE_DETECTED,// 卡已经检测到
APPSTATE_PIN,// 卡已经被PIN码锁定
APPSTATE_PUK,// 卡已经被PUK码锁定
APPSTATE_SUBSCRIPTION_PERSO, //卡已经被网络锁定
APPSTATE_READY;//卡已经准备好了
};
//描述卡被网络锁定的信息,对国内用户来说意义不大
public enum PersoSubState{
PERSOSUBSTATE_UNKNOWN,
PERSOSUBSTATE_IN_PROGRESS,
PERSOSUBSTATE_READY,
PERSOSUBSTATE_SIM_NETWORK,
PERSOSUBSTATE_SIM_NETWORK_SUBSET,
PERSOSUBSTATE_SIM_CORPORATE,
PERSOSUBSTATE_SIM_SERVICE_PROVIDER,
PERSOSUBSTATE_SIM_SIM,
PERSOSUBSTATE_SIM_NETWORK_PUK,
PERSOSUBSTATE_SIM_NETWORK_SUBSET_PUK,
PERSOSUBSTATE_SIM_CORPORATE_PUK,
PERSOSUBSTATE_SIM_SERVICE_PROVIDER_PUK,
PERSOSUBSTATE_SIM_SIM_PUK,
PERSOSUBSTATE_RUIM_NETWORK1,
PERSOSUBSTATE_RUIM_NETWORK2,
PERSOSUBSTATE_RUIM_HRPD,
PERSOSUBSTATE_RUIM_CORPORATE,
PERSOSUBSTATE_RUIM_SERVICE_PROVIDER,
PERSOSUBSTATE_RUIM_RUIM,
PERSOSUBSTATE_RUIM_NETWORK1_PUK,
PERSOSUBSTATE_RUIM_NETWORK2_PUK,
PERSOSUBSTATE_RUIM_HRPD_PUK,
PERSOSUBSTATE_RUIM_CORPORATE_PUK,
PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_PUK,
PERSOSUBSTATE_RUIM_RUIM_PUK;
};
public AppType app_type;
public AppState app_state;
// applicable only if app_state ==RIL_APPSTATE_SUBSCRIPTION_PERSO
public PersoSubState perso_substate;
// null terminated string, e.g.,from 0xA0, 0x00 -> 0x41, 0x30, 0x30, 0x30 */
publicString aid;
// null terminated string
publicString app_label;
// applicable to USIM and CSIM
publicint pin1_replaced;
publicPinState pin1;//卡的pin1码
publicPinState pin2;//卡的pin2 码
....
}
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()。也许将这段代码等价有下面的代码更容易理解(这里一定要仔细阅读前面的状态说明)。
if (mAppState == AppState.APPSTATE_READY) { queryFdn(); //查询fdn queryPin1State();//查询Pin1状态:开或关 notifyPinLockedRegistrantsIfNeeded(null); }else{ notifyReadyRegistrantsIfNeeded(null); } |
假设UICC卡的pin功能开启了,则mAppState == AppState.APPSTATE_PIN(或类似),且并没有被解锁,则注册监听此消息的类被通知。这里有两个地方注册监听了此消息。一个是IccCardProxy。另一个是SimRecord,代码就不贴了。SimRecord的处理就是在Pin锁定的状态下仍然读取少量的UICC卡EF信息。IccCardProxy的处理如下:
private void processLockedState() { synchronized (mLock) { if (mUiccApplication == null) { //Don't need to do anything if non-existent application is locked return; } PinState pin1State = mUiccApplication.getPin1State(); if (pin1State == PinState.PINSTATE_ENABLED_PERM_BLOCKED) { setExternalState(State.PERM_DISABLED); return; }
AppState appState = mUiccApplication.getState(); switch (appState) { case APPSTATE_PIN: mPinLockedRegistrants.notifyRegistrants(); setExternalState(State.PIN_REQUIRED); break; case APPSTATE_PUK: setExternalState(State.PUK_REQUIRED); break; case APPSTATE_DETECTED: case APPSTATE_READY: case APPSTATE_SUBSCRIPTION_PERSO: case APPSTATE_UNKNOWN: // Neither required break; } } } |
可以看到,它实际上的处理就是将需要解锁的信息写入数据库,以便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。