UICC--Android O

Uicc card

关于这部分的流程,UiccController.java中的注释写的很清楚,先把这部分注释拿出来,如下:

 * 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----  | |          | | | | ---SIMFileHandle
           RuimRecords----  |          | | | ----RuimFileHandler
          IsimUiccRecords---           | | -----UsimFileHandler
                                       | -------CsimFileHandler
                                       -----IsimFileHandler

上面的注释是简单明了,按照代码流程看几遍,记住每个类的主要作用, 这部分也就算是掌握了。

UiccController

UiccController是在PhoneApp启动的过程中创建的,算是这部分知识的一个起点;UiccController构造函数里面便注册了RIL监听, 后续根据这些监听反馈便逐层创建了各个对象。
比较重要的两个点:
1.调用Ril的getIccCardStatus方法向modem查询card的状态。
2.用onGetIccCardStatusDone方法处理modem的反馈结果,UiccCard对象就是在这个方法里面创建的。

UiccCard

UiccCard—每个对象对应一张卡。
1.创建UiccCardApplication—每张卡最多有8个应用。
2.创建Catservice。
3.通过这个对象提供的API可以获取Card state,application等信息。
UiccCard的update方法用来更新对象的内部信息,方法如下:

 public void update(Context c, CommandsInterface ci, IccCardStatus ics) {
        synchronized (mLock) {
            CardState oldState = mCardState;
            mCardState = ics.mCardState;//更新card state
            mUniversalPinState = ics.mUniversalPinState;//更新pin state
            mGsmUmtsSubscriptionAppIndex = ics.mGsmUmtsSubscriptionAppIndex;//更新application的index,下面两句也是这个作用.
            mCdmaSubscriptionAppIndex = ics.mCdmaSubscriptionAppIndex;
            mImsSubscriptionAppIndex = ics.mImsSubscriptionAppIndex;
            mContext = c;
            mCi = ci;

            //update applications
            if (DBG) log(ics.mApplications.length + " applications");
            for ( int i = 0; i < mUiccApplications.length; i++) {
            //下面这段代码用来创建/删除UiccCardApplication对象。
                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);
                }
            }

            createAndUpdateCatService();//catservice相关的操作。

            // Reload the carrier privilege rules if necessary.
            log("Before privilege rules: " + mCarrierPrivilegeRules + " : " + mCardState);
            if (mCarrierPrivilegeRules == null && mCardState == CardState.CARDSTATE_PRESENT) {
                mCarrierPrivilegeRules = new UiccCarrierPrivilegeRules(this,
                        mHandler.obtainMessage(EVENT_CARRIER_PRIVILIGES_LOADED));
            } else if (mCarrierPrivilegeRules != null && mCardState != CardState.CARDSTATE_PRESENT) {
                mCarrierPrivilegeRules = null;
            }

            sanitizeApplicationIndexes();//通过判断Application是否有效来确定application index有效性.

            RadioState radioState = mCi.getRadioState();
            if (DBG) log("update: radioState=" + radioState + " mLastRadioState="
                    + mLastRadioState);
            // No notifications while radio is off or we just powering up
            if (radioState == RadioState.RADIO_ON && mLastRadioState == RadioState.RADIO_ON) {
            //下面这段代码用于提示用户卡插入或者拔出,重启手机。
                if (oldState != CardState.CARDSTATE_ABSENT &&
                        mCardState == CardState.CARDSTATE_ABSENT) {
                    if (DBG) log("update: notify card removed");
                    mAbsentRegistrants.notifyRegistrants();
                    mHandler.sendMessage(mHandler.obtainMessage(EVENT_CARD_REMOVED, null));
                } else if (oldState == CardState.CARDSTATE_ABSENT &&
                        mCardState != CardState.CARDSTATE_ABSENT) {
                    if (DBG) log("update: notify card added");
                    mHandler.sendMessage(mHandler.obtainMessage(EVENT_CARD_ADDED, null));
                }
            }
            mLastRadioState = radioState;
        }
    }

上面update方法根据卡的状态变化发出EVENT_CARD_REMOVED/EVENT_CARD_ADDED 消息,handler处理这两个消息时会调用onIccSwap(boolean isAdded) 方法。

    private void onIccSwap(boolean isAdded) {
        boolean isHotSwapSupported = mContext.getResources().getBoolean(//判断是否支持热插拔。
                R.bool.config_hotswapCapable);
        if (isHotSwapSupported) {//如果支持热插拔就不需要重启手机。
            log("onIccSwap: isHotSwapSupported is true, don't prompt for rebooting");
            return;
        }
        log("onIccSwap: isHotSwapSupported is false, prompt for rebooting");

        promptForRestart(isAdded);//用于不支持热插拔时,提示用户重启手机,并调用PowerManager
    }

UiccCardApplication

1.内部存储了Pin1,Pin2, FDN等信息; 所以可以通过这个对象提供的API来查询这些信息。
2.内部有IccFileHandler,SIMRecords对象; 可以通过这个对象提供的API来获取这两个对象。

SIMRecords

IccRecords有三个子类,分别对应了一种卡,主要看看SIMRecords。
1.fetchSimRecords是重要的一个方法, 这个方法获取了卡里的很多数据,像IMSI, Spn,PLMN,PNN, CPHS等;SIM refresh,ready时会调用该方法。
2.SIMRecords对象在RIL里注册了SIM refresh监听,在UiccCardApplication里面注册了app ready/lock事件,还注册了ACTION_CARRIER_CONFIG_CHANGED的广播(用于spn的override)。

IccCardProxy

另外IccCardProxy也要说下,根据这个名字也能猜到这个类的大致作用; 这个类实现了IccCard接口,根据注释,
这个接口是为了外部应用(主要是PhoneApp)可以方便的操作icc card相关的功能,这当然也是IccCardProxy的作用了。
这个类的对象是在phone对象初始化的过程中创建的,我们通过phone.getIccCard()获取的也是这个类的对象。
由于这个类的作用是方便外部应用操作icc card相关的功能, 所以这个类的方法很多, 而具体方法里的实现自然要用到UiccController,Uicard, UiccCardApplication, IccRecords和IccFileHandler。IccCardProxy 会通过ACTION_SIM_STATE_CHANGED 将SIM state发出去。当IccCardProxy收到IccCardConstants.INTENT_VALUE_ICC_LOCKED的状态,SIMRecords的records loaded以及UiccCard 的EVENT_CARRIER_PRIVILIGES_LOADED的通知后,还会发送ACTION_INTERNAL_SIM_STATE_CHANGED广播。

ICC Card状态(IccCardStatus.CardState)

CARDSTATE_ABSENT,
CARDSTATE_PRESENT,
CARDSTATE_ERROR,
CARDSTATE_RESTRICTED;

SIM状态(IccCardConstants.State)

UNKNOWN,        /** ordinal(0) == {@See TelephonyManager#SIM_STATE_UNKNOWN} */
ABSENT,         /** ordinal(1) == {@See TelephonyManager#SIM_STATE_ABSENT} */
PIN_REQUIRED,   /** ordinal(2) == {@See TelephonyManager#SIM_STATE_PIN_REQUIRED} */
PUK_REQUIRED,   /** ordinal(3) == {@See TelephonyManager#SIM_STATE_PUK_REQUIRED} */
NETWORK_LOCKED, /** ordinal(4) == {@See TelephonyManager#SIM_STATE_NETWORK_LOCKED} */
READY,          /** ordinal(5) == {@See TelephonyManager#SIM_STATE_READY} */
NOT_READY,      /** ordinal(6) == {@See TelephonyManager#SIM_STATE_NOT_READY} */
PERM_DISABLED,  /** ordinal(7) == {@See TelephonyManager#SIM_STATE_PERM_DISABLED} */
CARD_IO_ERROR,  /** ordinal(8) == {@See TelephonyManager#SIM_STATE_CARD_IO_ERROR} */
CARD_RESTRICTED;/** ordinal(9) == {@See TelephonyManager#SIM_STATE_CARD_RESTRICTED} */

icc card的状态比较好理解,通过UiccCard的getCardState方法可以获取。
我们比较常用的是SIM状态,可以通过TelephonyManger提供的getSimState(int slotIndex)/getSimState() 这两个API获取。getSimState(int slotIndex)/getSimState()内部进一步调用了SubscriptionController里面的方法getSimStateForSlotIndex(int index),而getSimStateForSlotIndex(int index)进一步调用了IccCardProxy的getState()方法,所以如果可以获取GsmCdmaPhone对象,可以通过getIccCard()方法获取IccCardProxy对象,进而调用getState()方法获取SIM状态。getState()方法只是返回了一个成员变量mExternalState,而这个变量值的更新是通过IccCardProxy在UiccController,UiccCard以及UiccApplication里面注册的监听驱动的(registerUiccCardEvents方法), 这个成员变量的赋值操作是通过setExternalState方法,下面几个情况会调用这个方法:
1.构造函数State.NOT_READY
2.updateQuietMode

  • EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED
  • EVENT_RADIO_ON
  • setVoiceRadioTech

3.handleMessage

  • EVENT_RADIO_OFF_OR_UNAVAILABLE
  • EVENT_ICC_ABSENT
  • EVENT_APP_READY
  • EVENT_NETWORK_LOCKED

4.updateExternalState

  • updateIccAvailability<–EVENT_ICC_CHANGED
  • onSubscriptionActivated<–EVENT_SUBSCRIPTION_ACTIVATED
  • onSubscriptionDeactivated<–EVENT_SUBSCRIPTION_DEACTIVATED

5.processLockedState

  • handleMessage:EVENT_ICC_LOCKED

SIM state的定义是综合了IccCardStatus.CardState,IccCardStatus.PinState以及IccCardApplicationStatus.AppState。
下面的表格根据IccCardProxy里面的代码逻辑,总结了SIM state各个状态所依赖的条件。

SIM state Depends on
State.UNKNOWN UNKNOWN只是一个短暂的状态, 比如在一些状态转换的时候会处于该状态。除了APPSTATE_UNKNOWN时为UNKOWN外,UNKNOWN也被用于其他特殊情况。
State.ABSENT CARDSTATE_ABSENT
State.NOT_READY Radio 不可用, 获取不到UiccCard或者UiccApplication对象的情况下设置为此状态。
State.READY APPSTATE_READY CARDSTATE_PRESENT
State.PIN_REQUIRED APPSTATE_PIN
State.PUK_REQUIRED APPSTATE_PUK
State.NETWORK_LOCKED APPSTATE_SUBSCRIPTION_PERSO, SIMLock开启时
State.PERM_DISABLED PinState.PINSTATE_ENABLED_PERM_BLOCKED
State.CARD_IO_ERROR CARDSTATE_ERROR
State.CARD_RESTRICTED CARDSTATE_RESTRICTED

开机SIM状态更新

命令GET_SIM_STATUS, 首先返回CARDSTATE_ABSENT, 然后是CARDSTATE_PRESENT, 这些RIL消息里面会带有PIN lock等信息。

onRecordLoaded

对于SIMRecords中的onRecordLoaded方法, 从定义和调用角度考虑,分成两部分:

  1. 在IccRecords中定义的onRecordLoaded方法,子类SIMRecords复写了该方法。
    IccRecords的handleMessage(EVENT_GET_ICC_RECORD_DONE)中调用该方法,
    SIMRecords重写了IccRecords的handleMessage方法(在default中调用IccRecords的handleMessage方法),
    对onRecordLoaded的调用也做了调整,将调用放在了handleMessage的末尾,通过变量isRecordLoadResponse做调用控制,所有需要调用该方法的EVENT可以将isRecordLoadResponse赋值为true。该方法会调用onAllRecordsLoaded(所有records都加载完成时),onAllRecordsLoaded除了保存一些数据之外还会通知监听了records loaded的观察者,比如IccCardProxy。
  2. 在IccRecords中的接口IccRecordLoaded中定义的onRecordLoaded方法,SIMRecords中的私有类EfPlLoaded和EfUsimLiLoaded都实现了IccRecordLoaded接口,
    也实现了onRecordLoaded方法。 接口IccRecordLoaded是用来作为call
    back的,根据code,是作为EVENT_GET_ICC_RECORD_DONE的call
    back用来获取EF_LI和EF_PL这两个文件的值(SIMRecords.loadEfLiAndEfPl)。

这里写图片描述

你可能感兴趣的:(UICC--Android O)