#注:本文基于Android O
在PhoneFactory中创建Phone对象时,UiccController也会被创建。 PhoneFactory.javapublic static void makeDefaultPhone(Context context) { ...... sUiccController = UiccController.make(context, sCommandsInterfaces); ......}
而UiccController初始化时就会注册监听一些事件,如ICC状态变化、RadioOn/RadioAvailable,
当这些事件被触发后,就会触发UiccController处理EVENT_ICC_STATUS_CHANGED事件。 UiccController.javaprivate UiccController(Context c, CommandsInterface []ci) { ...... for (int i = 0; i < mCis.length; i++) { Integer index = new Integer(i); mCis[i].registerForIccStatusChanged(this, EVENT_ICC_STATUS_CHANGED, index); ...... if (!StorageManager.inCryptKeeperBounce()) { mCis[i].registerForAvailable(this, EVENT_ICC_STATUS_CHANGED, index); } else { mCis[i].registerForOn(this, EVENT_ICC_STATUS_CHANGED, index); } ...... } ......}
然后再来看收到ICC状态变化后的处理流程,实际上就是去读取ICC的状态。
public void handleMessage (Message msg) { ...... switch (msg.what) { case EVENT_ICC_STATUS_CHANGED: ...... mCis[index].getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE, index)); break; case EVENT_GET_ICC_STATUS_DONE: ...... onGetIccCardStatusDone(ar, index); break; ......}
读取到的ICC状态后,接着就创建/更新UiccCard对象,同时也会通知相关的监听者。
private synchronized void onGetIccCardStatusDone(AsyncResult ar, Integer index) { ...... if (mUiccCards[index] == null) { //Create new card mUiccCards[index] = new UiccCard(mContext, mCis[index], status, index); } else { //Update already existing card mUiccCards[index].update(mContext, mCis[index] , status); } ...... mIccChangedRegistrants.notifyRegistrants(new AsyncResult(null, index, null));}
我们来看看UiccCard的初始化流程,其实就是比update()多了个存储CardState和PhoneId的过程。
UiccCard.javapublic UiccCard(Context c, CommandsInterface ci, IccCardStatus ics, int phoneId) { mCardState = ics.mCardState; mPhoneId = phoneId; update(c, ci, ics);}
主要的操作还是在update()方法中,里面会创建/删除/更新UiccCardApplication对象。若读取到了新的app,则创建;若之前的app已不在当前读取的值中,则删除;否则就更新。
createAndUpdateCatServiceLocked()为STK相关内容,暂不讨论。
最后还有一个关于SIM卡热插拔的处理。
public void update(Context c, CommandsInterface ci, IccCardStatus ics) { ...... 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); } } createAndUpdateCatService(); ...... 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)); } }}
先来看看热插拔吧,若手机支持热插拔,就啥都不会做;若不支持热插拔,则会弹出提示要求用户重启手机。
public void handleMessage(Message msg){ switch (msg.what) { case EVENT_CARD_REMOVED: onIccSwap(false); break; case EVENT_CARD_ADDED: onIccSwap(true); break; ......}private void onIccSwap(boolean isAdded) { boolean isHotSwapSupported = mContext.getResources().getBoolean( R.bool.config_hotswapCapable); if (isHotSwapSupported) { log("onIccSwap: isHotSwapSupported is true, don't prompt for rebooting"); return; } log("onIccSwap: isHotSwapSupported is false, prompt for rebooting"); promptForRestart(isAdded);}
再接着之前的流程分析,我们以创建UiccCardApplication的流程为例来分析,而更新UiccCardApplication的流程和创建差不多,就不再赘述了。
UiccCardApplication的构造方法中,会创建IccFileHandler和IccRecords。 UiccCardApplication.javapublic UiccCardApplication(UiccCard uiccCard, IccCardApplicationStatus as, Context c, CommandsInterface ci) { ...... mIccFh = createIccFileHandler(as.app_type); mIccRecords = createIccRecords(as.app_type, mContext, mCi); if (mAppState == AppState.APPSTATE_READY) { queryFdn(); queryPin1State(); } ......}
而在创建IccFileHandler和IccRecords对象时,会根据UiccCardApplication的类型来创建不同的子对象。
例如,对于APPTYPE_SIM,就会创建SIMFileHandler和SIMRecords对象。(中国移动的卡有2个app,但log只打印了一个APPTYPE_USIM,
中国联通的卡有1个APPTYPE_USIM, 中国电信的卡有1个APPTYPE_USIM和1个APPTYPE_CSIM)private IccRecords createIccRecords(AppType type, Context c, CommandsInterface ci) { if (type == AppType.APPTYPE_USIM || type == AppType.APPTYPE_SIM) { return new SIMRecords(this, c, ci); } else if (type == AppType.APPTYPE_RUIM || type == AppType.APPTYPE_CSIM){ return new RuimRecords(this, c, ci); } else if (type == AppType.APPTYPE_ISIM) { return new IsimUiccRecords(this, c, ci); } else { // Unknown app type (maybe detection is still in progress) return null; }}private IccFileHandler createIccFileHandler(AppType type) { switch (type) { case APPTYPE_SIM: return new SIMFileHandler(this, mAid, mCi); case APPTYPE_RUIM: return new RuimFileHandler(this, mAid, mCi); case APPTYPE_USIM: return new UsimFileHandler(this, mAid, mCi); case APPTYPE_CSIM: return new CsimFileHandler(this, mAid, mCi); case APPTYPE_ISIM: return new IsimFileHandler(this, mAid, mCi); default: return null; }}
先来看看SIMFileHandler,也仅仅是调用了父类的构造方法,而其父类的构造方法也仅仅是将传入的参数保存了起来。
public SIMFileHandler(UiccCardApplication app, String aid, CommandsInterface ci) { super(app, aid, ci); }
protected IccFileHandler(UiccCardApplication app, String aid, CommandsInterface ci) { mParentApp = app; mAid = aid; mCi = ci; }
然后再来看SIMRecords,其构造方法中会注册监听SIM卡中app ready的事件。
而一般情况下,第一次上报卡状态时,基本上就已经是ready状态了,这时,在注册监听app ready状态后就会马上触发SIMRecords处理EVENT_APP_READY事件。即使当前上报的状态不是ready,等到后面上传ready状态时也会触发EVENT_APP_READY事件。 SIMRecords.javapublic SIMRecords(UiccCardApplication app, Context c, CommandsInterface ci) { ...... mParentApp.registerForReady(this, EVENT_APP_READY, null); ......}SIMRecords的Handler处理EVENT_APP_READY事件。
public void handleMessage(Message msg) { ...... switch (msg.what) { case EVENT_APP_READY: onReady(); break; ......}public void onReady() { fetchSimRecords();}
这时就会去读取各种所需的ICC数据,包括IMSI,ICCID,MSISDN, MBI, AD, MWIS, SPDI等。
protected void fetchSimRecords() { ...... mCi.getIMSIForApp(mParentApp.getAid(), obtainMessage(EVENT_GET_IMSI_DONE)); mRecordsToLoad++; mFh.loadEFTransparent(EF_ICCID, obtainMessage(EVENT_GET_ICCID_DONE)); mRecordsToLoad++; new AdnRecordLoader(mFh).loadFromEF(EF_MSISDN, getExtFromEf(EF_MSISDN), 1, obtainMessage(EVENT_GET_MSISDN_DONE)); mRecordsToLoad++; ......}
在众多ICC数据中,IMSI被单独列了出来,当读取到IMSI后,卡状态会从READY变为IMSI。其变化流程为:
-> 处理EVENT_GET_IMSI_DONE
-> 通知相关监听者
-> 触发IccCardProxy发送TelephonyIntents.ACTION_SIM_STATE_CHANGED的广播,ICC状态值为IccCardConstants.INTENT_VALUE_ICC_IMSI
public void handleMessage(Message msg) { ...... try { switch (msg.what) { case EVENT_GET_IMSI_DONE: ...... mImsiReadyRegistrants.notifyRegistrants(); break; ...... } finally { // Count up record load responses even if they are fails if (isRecordLoadResponse) { onRecordLoaded(); } } ......}
每当读取完一条信息后,就会调用onRecordLoaded()方法,判断是否所有需要的数据都读取完毕,如果都读取完了,就继续调用onAllRecordsLoaded()方法。需要注意的是,即使在读取ICC数据是出错,只要有返回消息,我们仍然认为这条数据已经读取了。
protected void onRecordLoaded() { ...... mRecordsToLoad -= 1; ...... if (mRecordsToLoad == 0 && mRecordsRequested == true) { onAllRecordsLoaded(); } else if (mRecordsToLoad < 0) { loge("recordsToLoad <0, programmer error suspected"); mRecordsToLoad = 0; }}
ICC数据读取完毕后,就通知监听者,这时卡状态就变为LOADED了。
protected void onAllRecordsLoaded() { ...... mRecordsLoadedRegistrants.notifyRegistrants( new AsyncResult(null, null, null));}总结一下,创建Phone时同时创建UiccController,并注册监听卡状态变化,modem检测到卡后上报卡状态发生变化,AP就去读取卡状态,之后再创建UiccCard,在UiccCard中,会根据读取到的卡信息,创建UiccCardApplication,并根据不同app类型创建IccFileHandler和IccRecords,app类型为APPTYPE_SIM时会创建SIMFileHandler和SIMRecords,而SIMRecords的构造方法中会注册监听app ready的事件,等到modem上报app ready时,就会去读取ICC的各种数据,当读取到IMSI后,卡状态变为IMSI,全部数据读取完后,卡状态变为LOADED。Over!