本文代碼以高通平臺Android 12為分析對象岩调,可能會與Google原生AOSP有些許差異。
本文主要介紹UICC在Framework的初始化過程和SIM卡數(shù)據(jù)加載過程奶稠,可以幫助讀者分析一些關于SIM卡狀態(tài)與數(shù)據(jù)相關的問題。
在framework下UiccController.java開頭包含下面這段注釋捡遍,可以看出來UiccController就是管理Android SIM卡的控制器:
/**
* This class is responsible for keeping all knowledge about
* Universal Integrated Circuit Card (UICC), also know as SIM's,
* in the system. It is also used as API to get appropriate
* applications to pass them to phone and service trackers.
*
* UiccController is created with the call to make() function.
* UiccController is a singleton and make() must only be called once
* and throws an exception if called multiple times.
*
* Once created UiccController registers with RIL for "on" and "unsol_sim_status_changed"
* notifications. When such notification arrives UiccController will call
* getIccCardStatus (GET_SIM_STATUS). Based on the response of GET_SIM_STATUS
* request appropriate tree of uicc objects will be created.
*
* Following is class diagram for uicc classes:
*
* UiccController
* #
* |
* UiccSlot[]
* #
* |
* UiccCard
* #
* |
* UiccProfile
* # #
* | ------------------
* UiccCardApplication CatService
* # #
* | |
* IccRecords IccFileHandler
* ^ ^ ^ ^ ^ ^ ^ ^
* SIMRecords---- | | | | | | ---SIMFileHandler
* RuimRecords----- | | | | ----RuimFileHandler
* IsimUiccRecords--- | | -----UsimFileHandler
* | ------CsimFileHandler
* ----IsimFileHandler
*
* Legend: # stands for Composition
* ^ stands for Generalization
*
* See also {@link com.android.internal.telephony.IccCard}
*/
1.UiccController初始化
從下面的代碼可以看出锌订,UiccController屬于單例模式,只能創(chuàng)建一個實例画株,如果多次創(chuàng)建會拋出RuntimeException("UiccController.make() should only be called once")異常辆飘。
/**
* API to make UiccController singleton if not already created.
*/
public static UiccController make(Context c) {
synchronized (mLock) {
if (mInstance != null) {
throw new RuntimeException("UiccController.make() should only be called once");
}
mInstance = new UiccController(c);
return mInstance;
}
}
UiccController的初始化是在Phone進程初始化的時候進行的,有關Phone進程初始化流程想了解的可以去查看相關代碼與文檔谓传,本文不詳細講解蜈项。
PhoneFactory調(diào)用makeDefaultPhone()時,會調(diào)用UiccController.make()創(chuàng)建UiccController實例:
@UnsupportedAppUsage
public static void makeDefaultPhone(Context context) {
...
// Instantiate UiccController so that all other classes can just
// call getInstance()
sUiccController = UiccController.make(context);
...
}
再來看看UiccController初始化的時候干了什么:
private UiccController(Context c) {
if (DBG) log("Creating UiccController");
mContext = c;
mCis = PhoneFactory.getCommandsInterfaces(); //獲取到RIL接口實例(RIL是Phone進程初始化時創(chuàng)建的) RIL implementation of the CommandsInterface.
int numPhysicalSlots = c.getResources().getInteger(
com.android.internal.R.integer.config_num_physical_slots);
numPhysicalSlots = TelephonyProperties.sim_slots_count().orElse(numPhysicalSlots);
if (DBG) {
logWithLocalLog("config_num_physical_slots = " + numPhysicalSlots);
}
// Minimum number of physical slot count should be equals to or greater than phone count,
// if it is less than phone count use phone count as physical slot count.
if (numPhysicalSlots < mCis.length) {
numPhysicalSlots = mCis.length; //如果定義的物理卡槽數(shù)量小于Phone進程實際創(chuàng)建的phone的數(shù)量续挟,那么就使用phone進程實際創(chuàng)建的phone的數(shù)量
}
mUiccSlots = new UiccSlot[numPhysicalSlots]; //根據(jù)物理卡槽的數(shù)量創(chuàng)建UiccSlot數(shù)組
mPhoneIdToSlotId = new int[mCis.length]; //SlotId數(shù)組紧卒,index就是phoneId
Arrays.fill(mPhoneIdToSlotId, INVALID_SLOT_ID);
if (VDBG) logPhoneIdToSlotIdMapping();
mRadioConfig = RadioConfig.getInstance(); //獲取到RadioConfig實例(RadioConfig是Phone進程初始化時創(chuàng)建的)
mRadioConfig.registerForSimSlotStatusChanged(this, EVENT_SLOT_STATUS_CHANGED, null); //注冊EVENT_SLOT_STATUS_CHANGED事件監(jiān)聽
for (int i = 0; i < mCis.length; i++) {//開始遍歷每個RIL對象進行SIM相關的事件注冊
mCis[i].registerForIccStatusChanged(this, EVENT_ICC_STATUS_CHANGED, i);
if (!StorageManager.inCryptKeeperBounce()) {
mCis[i].registerForAvailable(this, EVENT_RADIO_AVAILABLE, i);
} else {
mCis[i].registerForOn(this, EVENT_RADIO_ON, i);
}
mCis[i].registerForNotAvailable(this, EVENT_RADIO_UNAVAILABLE, i);
mCis[i].registerForIccRefresh(this, EVENT_SIM_REFRESH, i);
}
mLauncher = new UiccStateChangedLauncher(c, this); //初始化UiccStateChangedLauncher
mCardStrings = loadCardStrings(); //加載保存在SharedPreferences中的ICCID(如果是eSIM,保存的就是EID)
mDefaultEuiccCardId = UNINITIALIZED_CARD_ID; //初始化默認EuiccCardId
mEuiccSlots = mContext.getResources()
.getIntArray(com.android.internal.R.array.non_removable_euicc_slots); //獲取配置的EuiccSlot數(shù)組
mHasBuiltInEuicc = hasBuiltInEuicc(); //如果mEuiccSlots數(shù)組不為空且長度大于0诗祸,該值為true
PhoneConfigurationManager.registerForMultiSimConfigChange(
this, EVENT_MULTI_SIM_CONFIG_CHANGED, null); //注冊EVENT_MULTI_SIM_CONFIG_CHANGED事件監(jiān)聽
mPinStorage = new PinStorage(mContext);//初始化PinStorage
}
UiccController本身是繼承了Handler的跑芳,從UiccController的構造函數(shù)可以看出來,在初始化時主要做的事件就是注冊各種SIM相關的事件監(jiān)聽和Radio狀態(tài)監(jiān)聽贬媒,確定UiccSlot的數(shù)量聋亡。
2.SIM卡的初始化時序圖
接下來看看SIM卡初始化的時序圖:
setp 1: rild進程主動上報UNSOL_RESPONSE_RADIO_STATE_CHANGED
setp 2: RIL判斷上報的射頻狀態(tài),根據(jù)上報的狀態(tài)進行不同處理际乘,下面的方法在BaseCommands中坡倔,由RadioIndication上報后調(diào)用該方法:
//***** Protected Methods
/**
* Store new RadioState and send notification based on the changes
*
* This function is called only by RIL.java when receiving unsolicited
* RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED
*
* RadioState has 3 values : RADIO_OFF, RADIO_UNAVAILABLE, RADIO_ON.
*
* @param newState new RadioState decoded from RIL_UNSOL_RADIO_STATE_CHANGED
* @param forceNotifyRegistrants boolean indicating if registrants should be notified even if
* there is no change in state
*/
protected void setRadioState(int newState, boolean forceNotifyRegistrants) {
int oldState;
synchronized (mStateMonitor) {
oldState = mState;
mState = newState;
if (oldState == mState && !forceNotifyRegistrants) {
// no state transition
return;
}
mRadioStateChangedRegistrants.notifyRegistrants();
if (mState != TelephonyManager.RADIO_POWER_UNAVAILABLE
&& oldState == TelephonyManager.RADIO_POWER_UNAVAILABLE) {
mAvailRegistrants.notifyRegistrants();
}
if (mState == TelephonyManager.RADIO_POWER_UNAVAILABLE
&& oldState != TelephonyManager.RADIO_POWER_UNAVAILABLE) {
mNotAvailRegistrants.notifyRegistrants();
}
if (mState == TelephonyManager.RADIO_POWER_ON
&& oldState != TelephonyManager.RADIO_POWER_ON) {
mOnRegistrants.notifyRegistrants();
}
if ((mState == TelephonyManager.RADIO_POWER_OFF
|| mState == TelephonyManager.RADIO_POWER_UNAVAILABLE)
&& (oldState == TelephonyManager.RADIO_POWER_ON)) {
mOffOrNotAvailRegistrants.notifyRegistrants();
}
}
}
setp 3-4: 通過mAvailRegistrants.notifyRegistrants()通知UiccController->handlerMessage->EVENT_RADIO_AVAILABLE:
case EVENT_RADIO_AVAILABLE:
case EVENT_RADIO_ON:
if (DBG) {
log("Received EVENT_RADIO_AVAILABLE/EVENT_RADIO_ON, calling "
+ "getIccCardStatus");
}
mCis[phoneId].getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE,
phoneId));
// slot status should be the same on all RILs; request it only for phoneId 0
if (phoneId == 0) {
if (DBG) {
log("Received EVENT_RADIO_AVAILABLE/EVENT_RADIO_ON for phoneId 0, "
+ "calling getIccSlotsStatus");
}
mRadioConfig.getSimSlotsStatus(obtainMessage(EVENT_GET_SLOT_STATUS_DONE,
phoneId));
}
break;
setp 5-10: 射頻狀態(tài)變?yōu)?RADIO_AVAILABLE或者RADIO_ON后,開始主動獲取SIM卡狀態(tài)相關的信息脖含,調(diào)用getIccCardStatus向rild查詢罪塔,rild轉(zhuǎn)發(fā)到modem后,返回查詢到的信息养葵,再轉(zhuǎn)發(fā)返回給UiccController:
case EVENT_GET_ICC_STATUS_DONE:
if (DBG) log("Received EVENT_GET_ICC_STATUS_DONE");
onGetIccCardStatusDone(ar, phoneId);
break;
setp 11: onGetIccCardStatusDone中通過mUiccSlots[slotId].update()開始更新SIM卡信息征堪,在全部更新完成后執(zhí)行mIccChangedRegistrants.notifyRegistrants通知所有關注SIM狀態(tài)的觀察者(如CatService、CarrierResolver关拒、ServiceStateTracker佃蚜、GsmSMSDispatcher、Phone等等):
private synchronized void onGetIccCardStatusDone(AsyncResult ar, Integer index) {
...
IccCardStatus status = (IccCardStatus)ar.result;
...
if (mUiccSlots[slotId] == null) {
if (VDBG) {
log("Creating mUiccSlots[" + slotId + "]; mUiccSlots.length = "
+ mUiccSlots.length);
}
mUiccSlots[slotId] = new UiccSlot(mContext, true);
}
mUiccSlots[slotId].update(mCis[index], status, index, slotId);
UiccCard card = mUiccSlots[slotId].getUiccCard();
if (card == null) {
if (DBG) log("mUiccSlots[" + slotId + "] has no card. Notifying IccChangedRegistrants");
mIccChangedRegistrants.notifyRegistrants(new AsyncResult(null, index, null));
return;
}
...
if (DBG) log("Notifying IccChangedRegistrants");
mIccChangedRegistrants.notifyRegistrants(new AsyncResult(null, index, null));
}
setp 12-13: UiccSlot更新radioState着绊,初始化UiccCard或者是EUiccCard:
public void update(CommandsInterface ci, IccCardStatus ics, int phoneId, int slotIndex) {
if (DBG) log("cardStatus update: " + ics.toString());
synchronized (mLock) {
CardState oldState = mCardState;
mCardState = ics.mCardState;
mIccId = ics.iccid;
mPhoneId = phoneId;
parseAtr(ics.atr);
mCi = ci;
mIsRemovable = isSlotRemovable(slotIndex);
int radioState = mCi.getRadioState();
if (DBG) {
log("update: radioState=" + radioState + " mLastRadioState=" + mLastRadioState);
}
if (absentStateUpdateNeeded(oldState)) {
updateCardStateAbsent();
// Because mUiccCard may be updated in both IccCardStatus and IccSlotStatus, we need to
// create a new UiccCard instance in two scenarios:
// 1\. mCardState is changing from ABSENT to non ABSENT.
// 2\. The latest mCardState is not ABSENT, but there is no UiccCard instance.
} else if ((oldState == null || oldState == CardState.CARDSTATE_ABSENT
|| mUiccCard == null) && mCardState != CardState.CARDSTATE_ABSENT) {
// No notification while we are just powering up
if (radioState != TelephonyManager.RADIO_POWER_UNAVAILABLE
&& mLastRadioState != TelephonyManager.RADIO_POWER_UNAVAILABLE) {
if (DBG) log("update: notify card added");
sendMessage(obtainMessage(EVENT_CARD_ADDED, null));
}
// card is present in the slot now; create new mUiccCard
if (mUiccCard != null) {
loge("update: mUiccCard != null when card was present; disposing it now");
mUiccCard.dispose();
}
if (!mIsEuicc) {
mUiccCard = new UiccCard(mContext, mCi, ics, mPhoneId, mLock);
} else {
// The EID should be reported with the card status, but in case it's not we want
// to catch that here
if (TextUtils.isEmpty(ics.eid)) {
loge("update: eid is missing. ics.eid=" + ics.eid);
}
mUiccCard = new EuiccCard(mContext, mCi, ics, phoneId, mLock);
}
} else {
if (mUiccCard != null) {
mUiccCard.update(mContext, mCi, ics);
}
}
mLastRadioState = radioState;
}
}
setp 14-15: UiccCard更新狀態(tài)和內(nèi)容谐算,同時開始初始化UiccProfile進行更新:
public void update(Context c, CommandsInterface ci, IccCardStatus ics) {
synchronized (mLock) {
mCardState = ics.mCardState;
mContext = c;
mCi = ci;
mIccid = ics.iccid;
updateCardId();
if (mCardState != CardState.CARDSTATE_ABSENT) {
if (mUiccProfile == null) {
mUiccProfile = TelephonyComponentFactory.getInstance()
.inject(UiccProfile.class.getName()).makeUiccProfile(
mContext, mCi, ics, mPhoneId, this, mLock);
} else {
mUiccProfile.update(mContext, mCi, ics);
}
} else {
throw new RuntimeException("Card state is absent when updating!");
}
}
}
setp 16-36:初始化UiccProfile,設置PhoneType归露,調(diào)用update()初始化UiccCardApplication洲脂,UiccCardApplication初始化主要做的就是查詢當前SIM卡Fdn和Pin鎖狀態(tài),然后初始化mIccLockEnabled值狀態(tài)剧包。
UiccProfile的update()里面還做了很多事情恐锦,比如創(chuàng)建CatService往果,CatService通知StkApp進行SIM卡相關的狀態(tài)更新等等,這里就不詳細講解了一铅。
最后調(diào)用updateIccAvailability(true) > setExternalState(IccCardConstants.State.READY) > UiccController.updateInternalIccState()最終調(diào)用到了SubscriptionInfoUpdater中陕贮。
public UiccProfile(Context c, CommandsInterface ci, IccCardStatus ics, int phoneId,
UiccCard uiccCard, Object lock) {
if (DBG) log("Creating profile");
mLock = lock;
mUiccCard = uiccCard;
mPhoneId = phoneId;
if (mUiccCard instanceof EuiccCard) {
// for RadioConfig<1.2 eid is not known when the EuiccCard is constructed
((EuiccCard) mUiccCard).registerForEidReady(mHandler, EVENT_EID_READY, null);
}
mPinStorage = UiccController.getInstance().getPinStorage();
update(c, ci, ics);
ci.registerForOffOrNotAvailable(mHandler, EVENT_RADIO_OFF_OR_UNAVAILABLE, null);
Phone phone = PhoneFactory.getPhone(phoneId);
if (phone != null) {
setCurrentAppType(phone.getPhoneType() == PhoneConstants.PHONE_TYPE_GSM);
}
resetProperties();
updateIccAvailability(false);
IntentFilter intentfilter = new IntentFilter();
intentfilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
c.registerReceiver(mCarrierConfigChangedReceiver, intentfilter);
}
/**
* Update the UiccProfile.
*/
public void update(Context c, CommandsInterface ci, IccCardStatus ics) {
synchronized (mLock) {
mUniversalPinState = ics.mUniversalPinState;
mGsmUmtsSubscriptionAppIndex = ics.mGsmUmtsSubscriptionAppIndex;
mCdmaSubscriptionAppIndex = ics.mCdmaSubscriptionAppIndex;
mImsSubscriptionAppIndex = ics.mImsSubscriptionAppIndex;
mApplicationCount = ics.mApplications.length;
mContext = c;
mCi = ci;
mTelephonyManager = (TelephonyManager) mContext.getSystemService(
Context.TELEPHONY_SERVICE);
//update applications
if (DBG) log(ics.mApplications.length + " applications");
mLastReportedNumOfUiccApplications = ics.mApplications.length;
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);
}
}
createAndUpdateCatServiceLocked();
// Reload the carrier privilege rules if necessary.
log("Before privilege rules: " + mCarrierPrivilegeRules + " : " + ics.mCardState);
if (mCarrierPrivilegeRules == null && ics.mCardState == CardState.CARDSTATE_PRESENT) {
mCarrierPrivilegeRules = new UiccCarrierPrivilegeRules(this,
mHandler.obtainMessage(EVENT_CARRIER_PRIVILEGES_LOADED));
} else if (mCarrierPrivilegeRules != null
&& ics.mCardState != CardState.CARDSTATE_PRESENT) {
mCarrierPrivilegeRules = null;
mContext.getContentResolver().unregisterContentObserver(
mProvisionCompleteContentObserver);
}
sanitizeApplicationIndexesLocked();
if (mRadioTech != ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN) {
setCurrentAppType(ServiceState.isGsm(mRadioTech));
}
updateIccAvailability(true);
}
}
private void setExternalState(IccCardConstants.State newState, boolean override) {
synchronized (mLock) {
if (!SubscriptionManager.isValidSlotIndex(mPhoneId)) {
loge("setExternalState: mPhoneId=" + mPhoneId + " is invalid; Return!!");
return;
}
if (!override && newState == mExternalState) {
log("setExternalState: !override and newstate unchanged from " + newState);
return;
}
mExternalState = newState;
if (mExternalState == IccCardConstants.State.LOADED) {
// Update the MCC/MNC.
if (mIccRecords != null) {
Phone currentPhone = PhoneFactory.getPhone(mPhoneId);
String operator = currentPhone.getOperatorNumeric();
log("setExternalState: operator=" + operator + " mPhoneId=" + mPhoneId);
if (!TextUtils.isEmpty(operator)) {
mTelephonyManager.setSimOperatorNumericForPhone(mPhoneId, operator);
String countryCode = operator.substring(0, 3);
if (countryCode != null) {
mTelephonyManager.setSimCountryIsoForPhone(mPhoneId,
MccTable.countryCodeForMcc(countryCode));
} else {
loge("setExternalState: state LOADED; Country code is null");
}
} else {
loge("setExternalState: state LOADED; Operator name is null");
}
}
}
log("setExternalState: set mPhoneId=" + mPhoneId + " mExternalState=" + mExternalState);
UiccController.updateInternalIccState(mContext, mExternalState,
getIccStateReason(mExternalState), mPhoneId);
}
}
setp 37-40: SubscriptionInfoUpdater調(diào)用sendMessage(EVENT_SIM_READY),然后執(zhí)行handleSimReady()方法開始更新SubscriptionInfo并通過SubscriptionController.notifySubscriptionInfoChanged()通知出去馅闽,并且對外發(fā)送SIM卡相關的廣播飘蚯。
protected void handleSimReady(int phoneId) {
List<Integer> cardIds = new ArrayList<>();
logd("handleSimReady: phoneId: " + phoneId);
if (sIccId[phoneId] != null && sIccId[phoneId].equals(ICCID_STRING_FOR_NO_SIM)) {
logd(" SIM" + (phoneId + 1) + " hot plug in");
sIccId[phoneId] = null;
}
// ICCID is not available in IccRecords by the time SIM Ready event received
// hence get ICCID from UiccSlot.
UiccSlot uiccSlot = UiccController.getInstance().getUiccSlotForPhone(phoneId);
String iccId = (uiccSlot != null) ? IccUtils.stripTrailingFs(uiccSlot.getIccId()) : null;
if (!TextUtils.isEmpty(iccId)) {
sIccId[phoneId] = iccId;
updateSubscriptionInfoByIccId(phoneId, true /* updateEmbeddedSubs */);
}
cardIds.add(getCardIdFromPhoneId(phoneId));
updateEmbeddedSubscriptions(cardIds, (hasChanges) -> {
if (hasChanges) {
mSubscriptionController.notifySubscriptionInfoChanged();
}
});
broadcastSimStateChanged(phoneId, IccCardConstants.INTENT_VALUE_ICC_READY, null);
broadcastSimCardStateChanged(phoneId, TelephonyManager.SIM_STATE_PRESENT);
broadcastSimApplicationStateChanged(phoneId, TelephonyManager.SIM_STATE_NOT_READY);
}
至此SIM卡初始化相關的流程差不多就結束了,其實還有一些流程和狀態(tài)變化沒有詳細說出來福也,比如IccFileHandler執(zhí)行讀取SIM卡內(nèi)容填充IccRecords、更新各種狀態(tài)相關并通知出去等等攀圈,關注這個的可以重點查看CarrierResolver暴凑、ServiceStateTracker、GsmSMSDispatcher赘来、Phone這些類的流程现喳。