• Android SIM卡识别加载流程


    总述

    本文基于Android N(Android 7)

    首先要知道SIM卡一般是挂载在CP侧(MODEM侧)的,由MODEM给予真正的上电、识别、通信等,然后通过AP侧与CP侧之间的信息交流(Android系统中的逻辑主要集中在RIL层以上部分,RIL层以下为与MODEM通信的RIL库),完成Android侧SIM卡的加载过程。
    SIM卡的加载识别流程,作为Telephony系统的一个重要部分,其加载识别流程比较复杂,且需要理解SIM卡的一些相关概念,否则代码难以理解。分析代码时需要结合设备的SIM卡加载日志进行分析。Android系统中SIM卡的加载过程是以消息驱动的,即SIM卡识别加载流程基本就是MODEM与Android侧进行信息交互、消息通知、响应的过程。

    代码路径

    frameworks/opt/telephony/src/java/com/android/internal/telephony/uicc
    
    • 1

    UICC框架

    实际上我们日常所谓的SIM卡,其名称是UICC(Universal Integrated Circuit Card),卡上包含了多种应用,用户标识模块(SIM)、通用用户标识模块(USIM),CSIM(电信卡接入电信CDMA EVDO的模块)、ISIM(VOLTE使用)及其它应用(电子钱包等)。即我们现在所使用的卡,里面一般可以包含多个模块应用,从而可以适配各种网络。比如移动卡包含USIM(接入LTE和GSM用),电信卡包含USIM和CSIM等(CSIM接入CDMA和EVDO用,所以电信卡经常是双模卡)。

    因此我们的SIM卡识别框架就要从UICC说起。我们可以看UiccController的开头说明。

    /**
     * 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
     *                            #
     *                            |
     *                        UiccCard
     *                          #   #
     *                          |   ------------------
     *                    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}
     * and {@link com.android.internal.telephony.uicc.IccCardProxy}
     */     
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • UiccController
      SIM卡识别加载的核心类,所有的元素都从这里开始逐步创建,保持着与RIL(或者说同MODEM)的通信,将各种信息状态传达出去,然后各个具体的模块会处理处理对应事务。
    • UiccCard
      作为中间角色,主要是创建、更新UiccCardApplication及CatService
    • UiccCardApplication
      MODEM识别SIM卡后会返回卡中包含哪些模块,比如如果包含了USIM和CSIM,则会创建其usim和csim两个UiccCardApplication,每个UiccCardApplication又会创建自己的IccFileHandler、IccRecords对象,
    • IccRecords
      提供SIM卡常用信息查询,如IMSI、ICCID、Contacts等,UiccController根据SIM卡包含的不同模块,通过创建模块对应的UiccCardApplication,继而创建不同IccRecords对象
    • IccFileHandler
      负责SIM卡文件系统的读写

    IccRecords和IccFileHandler根据当前所属的UiccCardApplication是什么类型的,会被创建出对应的各个子类(SIMRecords、RuimRecords、UsimFileHandler等)

    SIM卡识别加载流程

    上述框架如果是初次接触的人看完,一定是云里雾里的,因为有太多的专业概念。简单来说,GSM CDMA LTE等不同的网络制式,其伴随着不同的SIM卡类型诞生,SIM卡中存储着对应网络制式需要的一些数据(比如鉴权信息,以及卡的通用属性ICCID等)。现在,UICC卡中可以同时写入这些不同的卡信息,作为独立的模块。所以在卡的识别过程,MODEM返回卡中包含了哪些模块,这样Android系统这边就可以创建出对应的模块(UiccCardApplication)。

    下面我们通过具体的代码流程,来进一步学习了解Android系统中是怎么识别加载SIM卡的。先上一张网上看到的画的很好的流程图
    Android系统SIM卡识别加载流程
    下述分析根据上述流程图进行简要说明,完整步骤可参考上图阅读分析源码。

    1. UiccController创建(流程图step 1、step 3、step4)
      在com.android.phone进程启动,创建phone对象之前,会先创建UiccController:
      sUiccController = UiccController.make(context, sCommandsInterfaces);
      public static void makeDefaultPhone(Context context) {
      				...
                    telephonyComponentFactory.initSubscriptionController(
                            context, sCommandsInterfaces);
                    // Instantiate UiccController so that all other classes can just
                    // call getInstance()
                    //创建UiccController
                    sUiccController = UiccController.make(context, sCommandsInterfaces);
    
                    for (int i = 0; i < numPhones; i++) {
                        Phone phone = null;
                        int phoneType = TelephonyManager.getPhoneType(networkModes[i]);
                        if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
                            phone = telephonyComponentFactory.makePhone(context,
                                    sCommandsInterfaces[i], sPhoneNotifier, i,
                                    PhoneConstants.PHONE_TYPE_GSM,
                                    telephonyComponentFactory);
                        } else if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
                            phone = telephonyComponentFactory.makePhone(context,
                                    sCommandsInterfaces[i], sPhoneNotifier, i,
                                    PhoneConstants.PHONE_TYPE_CDMA_LTE,
                                    telephonyComponentFactory);
                        }
                        Rlog.i(LOG_TAG, "Creating Phone with type = " + phoneType + " sub = " + i);
    
                        sPhones[i] = phone;
                    }
    			
    			...   
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    UiccController 的创建,会向RIL注册很多监听信息(mCis[i].registerForxxxx),比如EVENT_ICC_
    STATUS_CHANGED、EVENT_RADIO_UNAVAILABLE等,尤其是EVENT_ICC_STATUS_CHANGED这个消息,在SIM卡的识别加载中尤其关键。一旦有任何的SIM卡状态变更,RIL就会接收到MODEM发送上来的信息,然后RIL又发送给监听者(比如这里的UiccController)。

        private UiccController(Context c, CommandsInterface []ci) {
            if (DBG) log("Creating UiccController");
            mContext = c;
            mCis = ci;
            for (int i = 0; i < mCis.length; i++) {
                Integer index = new Integer(i);
                mCis[i].registerForIccStatusChanged(this, EVENT_ICC_STATUS_CHANGED, index);
                // TODO remove this once modem correctly notifies the unsols
                // If the device has been decrypted or FBE is supported, read SIM when radio state is
                // available.
                // Else wait for radio to be on. This is needed for the scenario when SIM is locked --
                // to avoid overlap of CryptKeeper and SIM unlock screen.
                if (DECRYPT_STATE.equals(SystemProperties.get("vold.decrypt")) ||
                        StorageManager.isFileEncryptedNativeOrEmulated()) {
                    mCis[i].registerForAvailable(this, EVENT_ICC_STATUS_CHANGED, index);
                } else {
                    mCis[i].registerForOn(this, EVENT_ICC_STATUS_CHANGED, index);
                }
                mCis[i].registerForNotAvailable(this, EVENT_RADIO_UNAVAILABLE, index);
                mCis[i].registerForIccRefresh(this, EVENT_SIM_REFRESH, index);
            }
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    2.获取SIM卡状态(流程图step 5、step 6)
    MODEM检测到SIM卡上电后,发送消息给RIL,RIL再发送EVENT_ICC_STATUS_CHANGED给监听者,比如上文的UiccController 。UiccController 是一个handler类,所以,对于RIL发送回来的消息,他在handleMessage 进行处理。
    UiccController 通过 mCis[index].getIccCardStatus主动查询SIM卡的状态。mCi即RIL,当RIL获取到SIM卡状态信息后,则继续发送EVENT_GET_ICC_STATUS_DONE消息给UiccController ,UiccController 接着就继续处理获取到的信息(通过onGetIccCardStatusDone)。

        public void handleMessage (Message msg) {
    			...
                AsyncResult ar = (AsyncResult)msg.obj;
                switch (msg.what) {
                    case EVENT_ICC_STATUS_CHANGED:
                        if (DBG) log("Received EVENT_ICC_STATUS_CHANGED, calling getIccCardStatus");
                        mCis[index].getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE, index));
                        break;
                    case EVENT_GET_ICC_STATUS_DONE:
                        if (DBG) log("Received EVENT_GET_ICC_STATUS_DONE");
                        onGetIccCardStatusDone(ar, index);
                        break;
                    case EVENT_RADIO_UNAVAILABLE:
                        if (DBG) log("EVENT_RADIO_UNAVAILABLE, dispose card");
                        if (mUiccCards[index] != null) {
                            mUiccCards[index].dispose();
                        }
                        mUiccCards[index] = null;
                        mIccChangedRegistrants.notifyRegistrants(new AsyncResult(null, index, null));
                        break;
                    case EVENT_SIM_REFRESH:
                        if (DBG) log("Received EVENT_SIM_REFRESH");
                        onSimRefresh(ar, index);
                        break;
                    default:
                        Rlog.e(LOG_TAG, " Unknown Event " + msg.what);
                        break;
                }
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    1. 获取到SIM卡状态后创建更新UiccCardApplication(流程图step 6)
      UiccController在onGetIccCardStatusDone接口中,创建UiccCard(首次创建,后续则是进行更新),UiccCard
      则根据SIM卡返回的信息,判断支持几个模块,然后根据支持的模块数,创建/更新对应的应用UiccCardApplication
      。随后也创建了CatService。
    2. 创建IccRecords和IccFileHandler(后续我们以SIMRecord为例)(流程图step 6.1.1.1.1.1)
      UiccCardApplication创建/更新的过程,会创建IccRecords和IccFileHandler(根据UiccCardApplication的类型)。根据UiccCardApplication的AppType的类型(APPTYPE_USIM、APPTYPE_CSIM、APPTYPE_ISIM等),会创建对应的IccRecords(SIMRecords、RuimRecords、IsimUiccRecords等)和IccFileHandler(SIMFileHandler、CsimFileHandler、IsimFileHandler等)
        public UiccCardApplication(UiccCard uiccCard,
                            IccCardApplicationStatus as,
                            Context c,
                            CommandsInterface ci) {
            ...
            mIccFh = createIccFileHandler(as.app_type);
            mIccRecords = createIccRecords(as.app_type, mContext, mCi);
            if (mAppState == AppState.APPSTATE_READY) {
                queryFdn();
                queryPin1State();
            }
            mCi.registerForNotAvailable(mHandler, EVENT_RADIO_UNAVAILABLE, null);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    这里我们假设创建的是USIM对应的SIMRecords,可以看到SIMRecords又向mParentApp(UiccCardApplication)注册了EVENT_APP_READY事件的监听。所以如果UiccCardApplication获取到EVENT_APP_READY状态后,SIMRecords就收收到监听通知。这样SIM卡状态就绪了,就可以进行下一步的SIM卡信息获取流程了。

        public SIMRecords(UiccCardApplication app, Context c, CommandsInterface ci) {
            ...
            mParentApp.registerForReady(this, EVENT_APP_READY, null);
            mParentApp.registerForLocked(this, EVENT_APP_LOCKED, null);
            if (DBG) log("SIMRecords X ctor this=" + this);
    		...
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    1. SIM信息获取(流程图step 11、16、17)
      我们在上面第3点分析中提到,UiccController获取到SIM卡状态后,就会通过UiccCard去更新UiccCardApplication。当SIM卡状态是APPSTATE_READY,则UiccCardApplication发送EVENT_APP_READY给SIMRecords,SIMRecords也是一个Handler类,所以他在handleMessage中处理EVENT_APP_READY消息,即这时候,Android侧这边已经知道,SIM卡已经状态就绪,那么可以开始去读取具体的各种信息了。所以调用onReady->fetchSimRecords,fetchSimRecords中便是去主动获取SIM卡的各类信息,包括常见的IMSI、ICCID,以及很多乱七八糟不知道干嘛用的信息。
        public void handleMessage(Message msg) {
           ...
            try { switch (msg.what) {
                case EVENT_APP_READY:
                    onReady();
                    break;
    
                case EVENT_APP_LOCKED:
                    onLocked();
                    break;
            ...
     		}catch (RuntimeException exc) {
                // I don't want these exceptions to be fatal
                logw("Exception parsing SIM record", exc);
            } finally {
                // Count up record load responses even if they are fails
                if (isRecordLoadResponse) {
                    onRecordLoaded();
                }
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    仍然如上代码流程所示,在最终所有的信息都获取到后,依次执行如下步骤:onRecordLoaded->onAllRecordsLoaded->mRecordsLoadedRegistrants.notifyRegistrants,这里会通知到IccCardProxy。IccCardProxy在创建的时候监听了SIMRecords的信息加载。

    至此,实际上SIM卡的识别和加载就已经基本完成了,但是这里还有一个很关键的类没有分析到:IccCardProxy。IccCardProxy看名字是一个代理,他作为中间人,持有我们前面分析过的各个主要类,能够拿到几乎所有的SIM卡状态、信息等,所以IccCardProxy是作为对外(UICC子系统以外)的一个接口类,他会将拿到的状态、信息进行广播,把状态、信息广播出去,或者其他子系统、应用、接口等可以通过IccCardProxy进行主动的信息获取。下面我们对IccCardProxy进行详细的分析。

    1. IccCardProxy创建
      IccCardProxy在创建phone的时候创建,如下所示
    	//以GsmCdmaPhone.java中的phone初始化为例
        private void initOnce(CommandsInterface ci) {
           ...
            
            mIccCardProxy = mTelephonyComponentFactory.makeIccCardProxy(mContext, mCi, mPhoneId);
    
            mCi.registerForAvailable(this, EVENT_RADIO_AVAILABLE, null);
            mCi.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
            mCi.registerForOn(this, EVENT_RADIO_ON, null);
            mCi.setOnSuppServiceNotification(this, EVENT_SSN, null);
           ...
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    具体创建IccCardProxy过程如下,最关键的一点,是向UiccController注册了EVENT_ICC_CHANGED监听消息,则UiccController会有多个时机向IccCardProxy发送EVENT_ICC_CHANGED的通知消息:registerForIccChanged调用时、onGetIccCardStatusDone中(获取到SIM卡状态后)等。

        public IccCardProxy(Context context, CommandsInterface ci, int phoneId) {
            if (DBG) log("ctor: ci=" + ci + " phoneId=" + phoneId);
           ...
            mUiccController = UiccController.getInstance();
            mUiccController.registerForIccChanged(this, EVENT_ICC_CHANGED, null);
            ci.registerForOn(this,EVENT_RADIO_ON, null);
            ci.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_UNAVAILABLE, null);
    
            resetProperties();
            setExternalState(State.NOT_READY, false);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    1. IccCardProxy中EVENT_ICC_CHANGED消息的处理(流程图step 9)
      IccCardProxy接收到EVENT_ICC_CHANGED消息后,就会去获取getUiccCard、getApplication、getIccRecords,把UICC框架中的几个核心成员获取到,然后调用registerUiccCardEvents注册了一系列的监听消息:EVENT_APP_READY、EVENT_RECORDS_LOADED等。
        private void updateIccAvailability() {
            synchronized (mLock) {
                UiccCard newCard = mUiccController.getUiccCard(mPhoneId);
                CardState state = CardState.CARDSTATE_ABSENT;
                UiccCardApplication newApp = null;
                IccRecords newRecords = null;
                if (newCard != null) {
                    state = newCard.getCardState();
                    newApp = newCard.getApplication(mCurrentAppType);
                    if (newApp != null) {
                        newRecords = newApp.getIccRecords();
                    }
                }
    
                if (mIccRecords != newRecords || mUiccApplication != newApp || mUiccCard != newCard) {
                    if (DBG) log("Icc changed. Reregestering.");
                    unregisterUiccCardEvents();
                    mUiccCard = newCard;
                    mUiccApplication = newApp;
                    mIccRecords = newRecords;
                    registerUiccCardEvents();
                }
                updateExternalState();
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
        private void registerUiccCardEvents() {
            if (mUiccCard != null) {
                mUiccCard.registerForAbsent(this, EVENT_ICC_ABSENT, null);
            }
            if (mUiccApplication != null) {
                mUiccApplication.registerForReady(this, EVENT_APP_READY, null);
                mUiccApplication.registerForLocked(this, EVENT_ICC_LOCKED, null);
                mUiccApplication.registerForNetworkLocked(this, EVENT_NETWORK_LOCKED, null);
            }
            if (mIccRecords != null) {
                mIccRecords.registerForImsiReady(this, EVENT_IMSI_READY, null);
                mIccRecords.registerForRecordsLoaded(this, EVENT_RECORDS_LOADED, null);
                mIccRecords.registerForRecordsEvents(this, EVENT_ICC_RECORD_EVENTS, null);
            }
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    1. SIM卡就绪、信息加载完毕后IccCardProxy的处理步骤
      SIM卡状态就绪后,UiccApplication会给IccCardProxy发送EVENT_APP_READY的消息,IccCardProxy则调用setExternalState将APP_READY的状态(或者其他状态,比如ABSENT、NOT_READY等)设置到系统属性“gsm.sim.state”。随后将状态通过广播发送出去。
        private void setExternalState(State newState, boolean override) {
            	...
                mExternalState = newState;
                loge("setExternalState: set mPhoneId=" + mPhoneId + " mExternalState=" + mExternalState);
                mTelephonyManager.setSimStateForPhone(mPhoneId, getState().toString());
    
                // For locked states, we should be sending internal broadcast.
                if (IccCardConstants.INTENT_VALUE_ICC_LOCKED.equals(getIccStateIntentString(mExternalState))) {
                    broadcastInternalIccStateChangedIntent(getIccStateIntentString(mExternalState),
                            getIccStateReason(mExternalState));
                } else {
                    broadcastIccStateChangedIntent(getIccStateIntentString(mExternalState),
                            getIccStateReason(mExternalState));
                }
                ...
            }
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    SIM卡信息获取完毕后(SIMRecords发出EVENT_RECORDS_LOADED消息通知IccCardProxy),然后IccCardProxy更新了“gsm.sim.operator.numeric”属性的值,即更新mccmnc,随后调用onRecordsLoaded发送了个参数值为INTENT_VALUE_ICC_LOADED的广播出去。

    
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
               ...
                case EVENT_ICC_CHANGED:
                    if (mInitialized) {
                        updateIccAvailability();
                    }
                    break;
                ...
                case EVENT_APP_READY:
                    setExternalState(State.READY);
                    break;
                case EVENT_RECORDS_LOADED:
                    // Update the MCC/MNC.
                    if (mIccRecords != null) {
                        Phone currentPhone = PhoneFactory.getPhone(mPhoneId);
                        String operator = currentPhone.getOperatorNumeric();
                        log("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(Integer.parseInt(countryCode)));
                            } else {
                                loge("EVENT_RECORDS_LOADED Country code is null");
                            }
                        } else {
                            loge("EVENT_RECORDS_LOADED Operator name is null");
                        }
                    }
                    if (mUiccCard != null && !mUiccCard.areCarrierPriviligeRulesLoaded()) {
                        mUiccCard.registerForCarrierPrivilegeRulesLoaded(
                            this, EVENT_CARRIER_PRIVILIGES_LOADED, null);
                    } else {
                        onRecordsLoaded();
                    }
                    break;
               ...
            }
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    至此,SIM卡加载、信息获取流程结束。SIM卡的状态众多、信息类型繁杂,本文抛砖引玉,只从主要流程进行分析,具体更多的细节仍然需要结合实际的日志和代码进行分析梳理,才能够对SIM卡的加载有更深刻的理解。

    日志分析举例

    很多人对SIM卡中所包含的应用不是很了解,这边通过移动卡和电信卡的日志进行说明。

    1. 移动卡 UiccCardApplication的创建与更新
      如下日志所示,接收到EVENT_GET_ICC_STATUS_DONE 事件后,则UiccCard被创建,紧接着根据返回的值创建UiccCardApplication,创建的参数、类型等,则是一个 IccCardApplicationStatus 类型的状态值({APPTYPE_USIM,
      APPSTATE_SUBSCRIPTION_PERSO,PERSOSUBSTATE_READY,pin1=PINSTATE_DISABLED,pin2=PINSTATE_ENABLED_NOT_VERIFIED})
      即mAppType=APPTYPE_USIM(这个值表示当前应用是USIM模块,决定了后续创建的IccRecords、IccFileHandler是什么类型的),mAppState=APPSTATE_SUBSCRIPTION_PERSO(决定了该模块应用的状态,后续会变为APPSTATE_READY,READY状态才是可用的)
    04-26 10:04:04.696 D/UiccController( 3580): Received EVENT_GET_ICC_STATUS_DONE
    04-26 10:04:04.696 D/UiccCard( 3580): 1 applications
    04-26 10:04:04.696 D/UiccCardApplication( 3580): Creating UiccApp: {APPTYPE_USIM,APPSTATE_SUBSCRIPTION_PERSO,PERSOSUBSTATE_READY,pin1=PINSTATE_DISABLED,pin2=PINSTATE_ENABLED_NOT_VERIFIED}
    
    
    • 1
    • 2
    • 3
    • 4

    MODEM继续更新SIM卡状态后,则如下所示,UiccController通过UiccCard更新UiccCardApplication,这时SIM卡该模块应用的状态已经变成了APPSTATE_READY。此时SIM卡状态就绪了(只有一个应用,其他应用也就不再继续更新了)

    04-26 10:04:06.214 D/UiccController( 3580): Received EVENT_GET_ICC_STATUS_DONE
    04-26 10:04:06.214 D/UiccCard( 3580): 1 applications
    04-26 10:04:06.214 D/UiccCardApplication( 3580): APPTYPE_USIM update. New {APPTYPE_USIM,APPSTATE_READY,pin1=PINSTATE_DISABLED,pin2=PINSTATE_ENABLED_NOT_VERIFIED}
    04-26 10:04:06.214 D/UiccCardApplication( 3580): APPTYPE_USIM changed state: APPSTATE_SUBSCRIPTION_PERSO -> APPSTATE_READY
    04-26 10:04:06.214 D/RILJ    ( 3580): [3758]> QUERY_FACILITY_LOCK [FD 7 a0000000871002ff86ffff89ffffffff] [SUB1]
    04-26 10:04:06.215 D/RILJ    ( 3580): [3759]> QUERY_FACILITY_LOCK [SC 7 a0000000871002ff86ffff89ffffffff] [SUB1]
    04-26 10:04:06.216 D/UiccCardApplication( 3580): Notifying registrants: READY
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    1. 电信卡 UiccCardApplication的创建与更新
      这边不再列举完整的创建、更新日志,仅简单看下普通电信卡带有几个应用,如下所示:
    
    2022-05-30 15:54:29.596 com.android.phone D/UiccController( 3111): Received EVENT_GET_ICC_STATUS_DONE
    
    2022-05-30 15:54:29.596 com.android.phone D/UiccCard( 3111): 2 applications
    
    2022-05-30 15:54:29.596 com.android.phone D/UiccCardApplication( 3111): APPTYPE_CSIM update. New {APPTYPE_CSIM,APPSTATE_READY,pin1=PINSTATE_DISABLED,pin2=PINSTATE_ENABLED_NOT_VERIFIED}
    
    2022-05-30 15:54:29.596 com.android.phone D/UiccCardApplication( 3111): APPTYPE_USIM update. New {APPTYPE_USIM,APPSTATE_READY,pin1=PINSTATE_DISABLED,pin2=PINSTATE_ENABLED_NOT_VERIFIED}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    我们可以看到,普通电信卡有两个应用,即APPTYPE_CSIM 和APPTYPE_USIM ,其中APPTYPE_USIM 主要是用于4G入网使用,而APPTYPE_CSIM 主要是用于2G/3G入网使用(电信的2G/3G与4G属于两种不同的技术制式,卡中需要通过两种应用来保存不同的参数,以便电信卡可以顺利连接入2G/3G或者4G)
    当然,现在电信也推出了单模卡(只有USIM应用),这种卡是无法连接电信2G 3G的,只能连接4G,这里就不展开描述了。

  • 相关阅读:
    【论文写作】RSA算法的实现总体设计参考
    两个线程交替执行的几种方式
    linux设置宽带
    计算机毕业设计选什么题目好?springboot 职业技术学院图书管理系统
    计及新能源出力不确定性的电气设备综合能源系统协同优化(Matlab代码实现)
    【2023集创赛】平头哥杯一等奖作品:基于无剑100开源SoC平台构建双核TEE安全系统
    设计模式--单例模式
    坚持写作是一个很难的事情
    【Transformers】第 2 章:主题的实践介绍
    今天公司来了个拿 30K 出来的测试,算是见识到了基础的天花板
  • 原文地址:https://blog.csdn.net/carolven/article/details/125307693