#注:本文基于Android O
在PhoneFactory中创建Phone对象时,UiccController也会被创建。
PhoneFactory.java
public static void makeDefaultPhone(Context context) {
......
sUiccController = UiccController.make(context, sCommandsInterfaces);
......
}
而UiccController初始化时就会注册监听一些事件,如ICC状态变化、RadioOn/RadioAvailable,
当这些事件被触发后,就会触发UiccController处理EVENT_ICC_STATUS_CHANGED事件。
UiccController.java
private 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.java
public 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.java
public 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.java
public 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!