Uicc card

来源:互联网 发布:英国海淘鞋子 知乎 编辑:程序博客网 时间:2024/06/10 12:01

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监听,在UiccCardAoolication里面注册了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()方法,所以如果可以获取PhoneApp对象,可以通过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 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)。

    这里写图片描述