本文代码以高通平台Android 12为分析对象,可能会与Google原生AOSP有些许差异。
本文主要介绍UICC在Framework的初始化过程和SIM卡数据加载过程,可以帮助读者分析一些关于SIM卡状态与数据相关的问题。
在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属于单例模式,只能创建一个实例,如果多次创建会抛出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调用makeDefaultPhone()时,会调用UiccController.make()创建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进程初始化时创建的) 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; //如果定义的物理卡槽数量小于Phone进程实际创建的phone的数量,那么就使用phone进程实际创建的phone的数量
}
mUiccSlots = new UiccSlot[numPhysicalSlots]; //根据物理卡槽的数量创建UiccSlot数组
mPhoneIdToSlotId = new int[mCis.length]; //SlotId数组,index就是phoneId
Arrays.fill(mPhoneIdToSlotId, INVALID_SLOT_ID);
if (VDBG) logPhoneIdToSlotIdMapping();
mRadioConfig = RadioConfig.getInstance(); //获取到RadioConfig实例(RadioConfig是Phone进程初始化时创建的)
mRadioConfig.registerForSimSlotStatusChanged(this, EVENT_SLOT_STATUS_CHANGED, null); //注册EVENT_SLOT_STATUS_CHANGED事件监听
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数组
mHasBuiltInEuicc = hasBuiltInEuicc(); //如果mEuiccSlots数组不为空且长度大于0,该值为true
PhoneConfigurationManager.registerForMultiSimConfigChange(
this, EVENT_MULTI_SIM_CONFIG_CHANGED, null); //注册EVENT_MULTI_SIM_CONFIG_CHANGED事件监听
mPinStorage = new PinStorage(mContext);//初始化PinStorage
}
UiccController本身是继承了Handler的,从UiccController的构造函数可以看出来,在初始化时主要做的事件就是注册各种SIM相关的事件监听和Radio状态监听,确定UiccSlot的数量。
2.SIM卡的初始化时序图
接下来看看SIM卡初始化的时序图:
setp 1: rild进程主动上报UNSOL_RESPONSE_RADIO_STATE_CHANGED
setp 2: RIL判断上报的射频状态,根据上报的状态进行不同处理,下面的方法在BaseCommands中,由RadioIndication上报后调用该方法:
//***** 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: 射频状态变为 RADIO_AVAILABLE或者RADIO_ON后,开始主动获取SIM卡状态相关的信息,调用getIccCardStatus向rild查询,rild转发到modem后,返回查询到的信息,再转发返回给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卡信息,在全部更新完成后执行mIccChangedRegistrants.notifyRegistrants通知所有关注SIM状态的观察者(如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更新状态和内容,同时开始初始化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,调用update()初始化UiccCardApplication,UiccCardApplication初始化主要做的就是查询当前SIM卡Fdn和Pin锁状态,然后初始化mIccLockEnabled值状态。
UiccProfile的update()里面还做了很多事情,比如创建CatService,CatService通知StkApp进行SIM卡相关的状态更新等等,这里就不详细讲解了。
最后调用updateIccAvailability(true) > setExternalState(IccCardConstants.State.READY) > UiccController.updateInternalIccState()最终调用到了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调用sendMessage(EVENT_SIM_READY),然后执行handleSimReady()方法开始更新SubscriptionInfo并通过SubscriptionController.notifySubscriptionInfoChanged()通知出去,并且对外发送SIM卡相关的广播。
protected void handleSimReady(int phoneId) {
List 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卡初始化相关的流程差不多就结束了,其实还有一些流程和状态变化没有详细说出来,比如IccFileHandler执行读取SIM卡内容填充IccRecords、更新各种状态相关并通知出去等等,关注这个的可以重点查看CarrierResolver、ServiceStateTracker、GsmSMSDispatcher、Phone这些类的流程。