高通平臺Android 12-Telephony之SIM卡初始化流程學習總結

本文代碼以高通平臺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卡初始化的時序圖:

image.png

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轉發(fā)到modem后,返回查詢到的信息,再轉發(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這些類的流程。

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容