Android在經過幾次更新後,在與卡相關的管理出現的重大的改變。謹以些文,給自己做下學習該塊的筆記。既然作爲開源的,我們第一步當然是從代碼入手,分析該處的關係。
Uicc的架構圖如下:
從圖中可以看出,UiccController是用來控制所有與卡相關的操作,通過UiccController,我們可以訪問IccRecords(SIM卡相關), CatService(STK相關),IccFileHandle(讀取SIM卡文件)。
與前面幾個版相比,變化最大的就是IccCard.java這個文件,在4.2以前的版本中,該IccCard是一個類,而在4.2中,它卻是一個接口,代碼如下:
public interface IccCard
所以就不會存在以前版本中的SimCard, UsimCard, 現在統一用IccCardProxy來替代他們所有功能。
下面我將會展現卡相關的怎麼樣初始化的,UiccController是被PhoneFacotry中的makeDefaultPhone去初始化的,代碼如下:
sCommandsInterface = new RIL(context, networkMode, cdmaSubscription);
// Instantiate UiccController so that all other classes can just call getInstance()
UiccController.make(context, sCommandsInterface);
int phoneType = TelephonyManager.getPhoneType(networkMode);
if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
Log.i(LOG_TAG, "Creating GSMPhone");
sProxyPhone = new PhoneProxy(new GSMPhone(context,
sCommandsInterface, sPhoneNotifier));
可以看到,第一步先實例化RIL,我們會得到一個RIL的實例,然後,把這個實例調用UiccControl的make函數。這個函數會實例UiccController中一個成員變量,其它使用時,直接使用getInstance方向即可, 從這個地方可以看,它是個單例的模式,只用創建一次。代碼如下:
public static UiccController make(Context c, CommandsInterface ci) {
synchronized (mLock) {
if (mInstance != null) {
throw new RuntimeException("UiccController.make() should only be called once");
}
mInstance = new UiccController(c, ci);
return mInstance;
}
}
public static UiccController getInstance() {
synchronized (mLock) {
if (mInstance == null) {
throw new RuntimeException(
"UiccController.getInstance can't be called before make()");
}
return mInstance;
}
}
private UiccController(Context c, CommandsInterface ci) {
if (DBG) log("Creating UiccController");
mContext = c;
mCi = ci;
mCi.registerForIccStatusChanged(this, EVENT_ICC_STATUS_CHANGED, null);
// TODO remove this once modem correctly notifies the unsols
mCi.registerForOn(this, EVENT_ICC_STATUS_CHANGED, null);
}
2. 在UiccController初始化完成後,從它的構造函數中,我們可以看到該類還註冊了一個監控事件,分別爲EVENT_ICC_STATUS_CHANGED,該事件是用來監控SIM卡的狀態有變化的,由上層主動給FRAMEWORK上報消息。當接收到這個消息後,UiccController會通過RIL給MODEM發送消息,查詢下SIM卡的狀態。
public void handleMessage (Message msg) {
synchronized (mLock) {
switch (msg.what) {
case EVENT_ICC_STATUS_CHANGED:
if (DBG) log("Received EVENT_ICC_STATUS_CHANGED, calling getIccCardStatus");
mCi.getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE));
break;
case EVENT_GET_ICC_STATUS_DONE:
if (DBG) log("Received EVENT_GET_ICC_STATUS_DONE");
AsyncResult ar = (AsyncResult)msg.obj;
onGetIccCardStatusDone(ar);
break;
default:
Log.e(LOG_TAG, " Unknown Event " + msg.what);
}
}
}
private synchronized void onGetIccCardStatusDone(AsyncResult ar) {
if (ar.exception != null) {
Log.e(LOG_TAG,"Error getting ICC status. "
+ "RIL_REQUEST_GET_ICC_STATUS should "
+ "never return an error", ar.exception);
return;
}
IccCardStatus status = (IccCardStatus)ar.result;
if (mUiccCard == null) {
//Create new card
mUiccCard = new UiccCard(mContext, mCi, status);
} else {
//Update already existing card
mUiccCard.update(mContext, mCi , status);
}
if (DBG) log("Notifying IccChangedRegistrants");
mIccChangedRegistrants.notifyRegistrants();
}
當發送查詢請求,待到查詢結果上來後,會初始化UiccController的成員變量mUiccCard,如果是mUiccCard是空,即還沒有初始化過,就重新NEW一個UiccCard的實例。如果是實例化過的,就重新更新下UiccCard的信息。到這個時候時,就算有SIM卡了,需要向其它註冊了 監控SIM卡狀態的註冊者通知。以便其它應用能做知道SIM卡已經好了。
3. 我們接下往下走,剛說到在初始化UiccCard的時候,會重新NEW一個實例,這個NEW的過程到底幹了什麼,請看下面的代碼:
public UiccCard(Context c, CommandsInterface ci, IccCardStatus ics) {
if (DBG) log("Creating");
mCardState = ics.mCardState;
update(c, ci, ics);
}
public void update(Context c, CommandsInterface ci, IccCardStatus ics) {
synchronized (mLock) {
if (mDestroyed) {
loge("Updated after destroyed! Fix me!");
return;
}
CardState oldState = mCardState;
mCardState = ics.mCardState;
mUniversalPinState = ics.mUniversalPinState;
mGsmUmtsSubscriptionAppIndex = ics.mGsmUmtsSubscriptionAppIndex;
mCdmaSubscriptionAppIndex = ics.mCdmaSubscriptionAppIndex;
mImsSubscriptionAppIndex = ics.mImsSubscriptionAppIndex;
mContext = c;
mCi = ci;
//update applications
if (DBG) log(ics.mApplications.length + " applications");
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);
}
}
if (mUiccApplications.length > 0 && mUiccApplications[0] != null) {
// Initialize or Reinitialize CatService
mCatService = CatService.getInstance(mCi,
mContext,
this);
} else {
if (mCatService != null) {
mCatService.dispose();
}
mCatService = 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;
}
}
從上面的代碼看,最終都是調用update方法來更新狀態。這個update的方法做了以下工作:
第一:更新SIM卡的信息,第二,判斷下當前狀態轉換是怎麼樣, 即更新mUiccApplications的數組信息。第三,更新STK的SERVICE,這個看過我的前面STK講解的童鞋,看到這個應該會很熟悉,對這個正是給STK提供服務的SERVICE,從這裏可以看到,STK初始化跟以前不一樣,這樣的話,STK服務啓動的較慢,這個時候,和STK上層的一個STK SERVICE RUNNING這個是配合,只有CAT SERVICE向下面報告了CAT SERVICE好了,下面纔可以向上報STK的相關命令。第四,通過狀態判斷,向外面發送CARD REMOVE還是CARDADD消息。
4. 細心點的童鞋有可能會注意到,我在開始的提到的IccCard這個東東怎麼沒有看到呢,其它,他是在Phone創建後,用phoneproxy來替換phone(包括GSMPHONE,CDMAPHONE等等),這樣做的好處是爲了屏蔽這些PHONE之間的差異點。對外來說,都是一樣的接口。好,既然這樣的話,我要去看看phoneproxy怎麼初始化的。代碼如下:
// Instantiate UiccController so that all other classes can just call getInstance()
UiccController.make(context, sCommandsInterface);
int phoneType = TelephonyManager.getPhoneType(networkMode);
if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
Log.i(LOG_TAG, "Creating GSMPhone");
sProxyPhone = new PhoneProxy(new GSMPhone(context,
sCommandsInterface, sPhoneNotifier));
} else if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
switch (TelephonyManager.getLteOnCdmaModeStatic()) {
case PhoneConstants.LTE_ON_CDMA_TRUE:
Log.i(LOG_TAG, "Creating CDMALTEPhone");
sProxyPhone = new PhoneProxy(new CDMALTEPhone(context,
sCommandsInterface, sPhoneNotifier));
break;
case PhoneConstants.LTE_ON_CDMA_FALSE:
default:
Log.i(LOG_TAG, "Creating CDMAPhone");
sProxyPhone = new PhoneProxy(new CDMAPhone(context,
sCommandsInterface, sPhoneNotifier));
break;
}
}
在上面這段代碼,請大家注意NEW PHONEPROXY的時候,會根據PHONE的類型創建不同的PHONE,但然後再用PHONEPROXY把所有PHONE之間的區別不一樣的給屏蔽掉,以便對外面的接口來說,都是一致的。PHONEPROXY的初始化,如下:
public PhoneProxy(PhoneBase phone) {
mActivePhone = phone;
mResetModemOnRadioTechnologyChange = SystemProperties.getBoolean(
TelephonyProperties.PROPERTY_RESET_ON_RADIO_TECH_CHANGE, false);
mIccSmsInterfaceManagerProxy = new IccSmsInterfaceManagerProxy(
phone.getIccSmsInterfaceManager());
mIccPhoneBookInterfaceManagerProxy = new IccPhoneBookInterfaceManagerProxy(
phone.getIccPhoneBookInterfaceManager());
mPhoneSubInfoProxy = new PhoneSubInfoProxy(phone.getPhoneSubInfo());
mCommandsInterface = ((PhoneBase)mActivePhone).mCM;
mCommandsInterface.registerForRilConnected(this, EVENT_RIL_CONNECTED, null);
mCommandsInterface.registerForOn(this, EVENT_RADIO_ON, null);
mCommandsInterface.registerForVoiceRadioTechChanged(
this, EVENT_VOICE_RADIO_TECH_CHANGED, null);
mIccCardProxy = new IccCardProxy(phone.getContext(), mCommandsInterface);
if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_GSM) {
// For the purpose of IccCardProxy we only care about the technology family
mIccCardProxy.setVoiceRadioTech(ServiceState.RIL_RADIO_TECHNOLOGY_UMTS);
} else if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) {
mIccCardProxy.setVoiceRadioTech(ServiceState.RIL_RADIO_TECHNOLOGY_1xRTT);
}
}
至於細節東東,只有去看代碼了,在創建GSMPHONE的時候做了好多的事。需要自己去體會。由於一直從事GSMPHONE ,好像還沒有遇到CDMA的實例。