高通平台Android 12-Telephony之SIM卡初始化流程学习总结

本文代码以高通平台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卡初始化的时序图:

image.png

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这些类的流程。

你可能感兴趣的:(高通平台Android 12-Telephony之SIM卡初始化流程学习总结)