Android6.0的phone应用源码分析(9)——UICC卡管理

这部分严格来说并不属于phone应用,但是考虑到手机卡与phone应用的密切关系,也就放在同一个系列了。

1.1   UICC卡的各种状态

这些内容一般都被定义在IccCardStatus类中。

首先是卡的状态:

public enumCardState {
        CARDSTATE_ABSENT,  //表示掉卡
        CARDSTATE_PRESENT, //表示卡正常
        CARDSTATE_ERROR; //表示卡出现了错误

}

 

接着是描述Pin的状态

 publicenum PinState {
        PINSTATE_UNKNOWN,// PIN码状态不确定
        PINSTATE_ENABLED_NOT_VERIFIED, //PIN码锁定,用户输入的PIN码错误,无法进入手机
        PINSTATE_ENABLED_VERIFIED,// PINM码锁定,用户输入正确的PIN码,进入手机
        PINSTATE_DISABLED,// 没有进行PIN码锁定
        PINSTATE_ENABLED_BLOCKED,//PIN码解锁失败,提示输入PUK码
        PINSTATE_ENABLED_PERM_BLOCKED;// PUK码解锁失败后,永久锁定

}

 

 IccCardApplicationStatus,描述UiccCardApplication的信息,每一个IccCardApplicationStatus,对应于一个UiccCardApplication。IccCardApplicationStatus实质上是多个卡应用状态的集合。包括AppType、AppState、PersoSubState以及其他状态。如下

public class IccCardApplicationStatus {
    //描述UiccCardApplication的类型,和卡的类型对应
    public enum AppType{
        APPTYPE_UNKNOWN,
        APPTYPE_SIM,  //GSM 卡
        APPTYPE_USIM, // WCDMA 卡
        APPTYPE_RUIM, // CDMA 卡
        APPTYPE_CSIM,
        APPTYPE_ISIM
    };
    //描述UiccCardApplication的状态,和卡的状态对应
    public enum AppState{
        APPSTATE_UNKNOWN, //卡不存在
        APPSTATE_DETECTED,// 卡已经检测到
        APPSTATE_PIN,// 卡已经被PIN码锁定
        APPSTATE_PUK,// 卡已经被PUK码锁定
        APPSTATE_SUBSCRIPTION_PERSO, //卡已经被网络锁定
        APPSTATE_READY;//卡已经准备好了
  };
    //描述卡被网络锁定的信息,对国内用户来说意义不大
    public enum PersoSubState{
        PERSOSUBSTATE_UNKNOWN,
        PERSOSUBSTATE_IN_PROGRESS,
        PERSOSUBSTATE_READY,
        PERSOSUBSTATE_SIM_NETWORK,
        PERSOSUBSTATE_SIM_NETWORK_SUBSET,
        PERSOSUBSTATE_SIM_CORPORATE,
        PERSOSUBSTATE_SIM_SERVICE_PROVIDER,
        PERSOSUBSTATE_SIM_SIM,
        PERSOSUBSTATE_SIM_NETWORK_PUK,
       PERSOSUBSTATE_SIM_NETWORK_SUBSET_PUK,
        PERSOSUBSTATE_SIM_CORPORATE_PUK,
       PERSOSUBSTATE_SIM_SERVICE_PROVIDER_PUK,
        PERSOSUBSTATE_SIM_SIM_PUK,
        PERSOSUBSTATE_RUIM_NETWORK1,
        PERSOSUBSTATE_RUIM_NETWORK2,
        PERSOSUBSTATE_RUIM_HRPD,
        PERSOSUBSTATE_RUIM_CORPORATE,
        PERSOSUBSTATE_RUIM_SERVICE_PROVIDER,
        PERSOSUBSTATE_RUIM_RUIM,
        PERSOSUBSTATE_RUIM_NETWORK1_PUK,
        PERSOSUBSTATE_RUIM_NETWORK2_PUK,
        PERSOSUBSTATE_RUIM_HRPD_PUK,
        PERSOSUBSTATE_RUIM_CORPORATE_PUK,
       PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_PUK,
        PERSOSUBSTATE_RUIM_RUIM_PUK;
    };

    public AppType       app_type;
    public AppState      app_state;
    // applicable only if app_state ==RIL_APPSTATE_SUBSCRIPTION_PERSO
    public PersoSubState  perso_substate;
    // null terminated string, e.g.,from 0xA0, 0x00 -> 0x41, 0x30, 0x30, 0x30 */
    publicString         aid;
    // null terminated string
    publicString         app_label;
    // applicable to USIM and CSIM
    publicint           pin1_replaced;
    publicPinState           pin1;//卡的pin1码
    publicPinState           pin2;//卡的pin2 码
    ....
    }

1.2   UiccController

UiccController负责了所有有关Uicc卡的操作。与其相关的类有以下几个:

 UiccController:整个UICC相关信息的控制接口;监控SIM状态变化;

  UiccCard:UICC卡代码中对应的抽象;

  IccCardStatus:维护UICC卡的状态:CardState & PinState;

  UiccCardApplication:UICC具体的一个应用;负责Pin Puk密码设置解锁,数据的读取,存储;

  CatService:负责SIM Toolkit相关;

  IccConstants:SIM File Address;存储不同数据在Sim卡上的字段地址;SIMRecords等基类;

  SIMRecords /RuimRecords:记录SIM卡上的数据;

IccFileHandler:读取SIM数据以及接收读取的结果;

它们之间的关系大致如下图所示:


UiccController在APP构造phones的过程中被创建,从其构造函数来看,它注册监听了RILJ的多个状态变化。对UICC卡状态的变化、Radio的状态进行了监听。

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);

             //注册RADIO状态变化监听 

            if (DECRYPT_STATE.equals(SystemProperties.get("vold.decrypt"))) {

                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);

        }

    }

从上面UiccController的私有构造函数可以看出,UiccController是一个单例,它管理了所有的Uicc卡。这里首先假设底层UICC卡状态发生变化。则根据前几章的经验,RILJ将广播消息给此消息的所有注册者,这里当然是UiccController啦。UiccController在其handleMessage()中被处理。

public void handleMessage (Message msg) {

        synchronized (mLock) {

            Integer index = getCiIndex(msg);

 

            if (index < 0 || index >= mCis.length) {

                Rlog.e(LOG_TAG, "Invalid index : " + index + " received with event " + msg.what);

                return;

            }

 

            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);

            }

        }

    }

可以看到,UiccController在接受到RILJ手机卡状态变化的广播后,首先是调用RILJ的方法查询了UICC卡状态,然后再回到UiccController.handlerMessage提取出UICC卡的状态。这种处理方式与之前CallTracker对来电的处理基本上是一致的,这里就不再详述了。直接进入对卡状态的处理:

private synchronized void onGetIccCardStatusDone(AsyncResult ar, Integer index) {

        if (ar.exception != null) {

            Rlog.e(LOG_TAG,"Error getting ICC status. "

                    + "RIL_REQUEST_GET_ICC_STATUS should "

                    + "never return an error", ar.exception);

            return;

        }

        if (!isValidCardIndex(index)) {

            Rlog.e(LOG_TAG,"onGetIccCardStatusDone: invalid index : " + index);

            return;

        }

 

        IccCardStatus status = (IccCardStatus)ar.result;

 

        if (mUiccCards[index] == null) {

            //如果没有卡记录,则是新卡,需要new一个UiccCard

            mUiccCards[index] = new UiccCard(mContext, mCis[index], status, index);

        } else {

            //如果已经存在,跟新已有的UiccCard就行了

            mUiccCards[index].update(mContext, mCis[index] , status);

        }

 

        if (DBG) log("Notifying IccChangedRegistrants");

        mIccChangedRegistrants.notifyRegistrants(new AsyncResult(null, index, null));

 

    }

这里重点关注UiccCard状态的update。

public void update(Context c, CommandsInterface ci, IccCardStatus ics) {

        synchronized (mLock) {

            CardState oldState = mCardState;

            mCardState = ics.mCardState;

            mUniversalPinState = ics.mUniversalPinState;

            mGsmUmtsSubscriptionAppIndex = ics.mGsmUmtsSubscriptionAppIndex;

            mCdmaSubscriptionAppIndex = ics.mCdmaSubscriptionAppIndex;

            mImsSubscriptionAppIndex = ics.mImsSubscriptionAppIndex;

            mContext = c;

            mCi = ci;

 

            //触发UiccApplication的状态更新

            if (DBG) log(ics.mApplications.length + " applications");

            for ( int i = 0; i < mUiccApplications.length; i++) {

                if (mUiccApplications[i] == null) {

                    //新应用

                    if (i < ics.mApplications.length) {

                        mUiccApplications[i] = new UiccCardApplication(this,

                                ics.mApplications[i], mContext, mCi);

                    }

                } else if (i >= ics.mApplications.length) {

                    //移除应用

                    mUiccApplications[i].dispose();

                    mUiccApplications[i] = null;

                } else {

                    //更新已有的应用状态

                    mUiccApplications[i].update(ics.mApplications[i], mContext, mCi);

                }

            }

 

            createAndUpdateCatService();

 

            // 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();

 

            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;

        }

    }

可以看到UiccCard根据RILJ上报的卡状态消息,更新了自己,并导致了UiccCardApplication.update()。

void update (IccCardApplicationStatus as, Context c, CommandsInterface ci) {

        synchronized (mLock) {

            if (mDestroyed) {

                loge("Application updated after destroyed! Fix me!");

                return;

            }

 

            if (DBG) log(mAppType + " update. New " + as);

            mContext = c;

            mCi = ci;

            AppType oldAppType = mAppType;

            AppState oldAppState = mAppState;

            PersoSubState oldPersoSubState = mPersoSubState;

            mAppType = as.app_type;

            mAuthContext = getAuthContext(mAppType);

            mAppState = as.app_state;

            mPersoSubState = as.perso_substate;

            mAid = as.aid;

            mAppLabel = as.app_label;

            mPin1Replaced = (as.pin1_replaced != 0);

            mPin1State = as.pin1;

            mPin2State = as.pin2;

 

            if (mAppType != oldAppType) {

                if (mIccFh != null) { mIccFh.dispose();}

                if (mIccRecords != null) { mIccRecords.dispose();}

                mIccFh = createIccFileHandler(as.app_type);

                mIccRecords = createIccRecords(as.app_type, c, ci);

            }

 

            if (mPersoSubState != oldPersoSubState &&

                    mPersoSubState == PersoSubState.PERSOSUBSTATE_SIM_NETWORK) {

                notifyNetworkLockedRegistrantsIfNeeded(null);

            }

 

            if (mAppState != oldAppState) {

                if (DBG) log(oldAppType + " changed state: " + oldAppState + " -> " + mAppState);

                // If the app state turns to APPSTATE_READY, then query FDN status,

                //as it might have failed in earlier attempt.

                if (mAppState == AppState.APPSTATE_READY) {

                    queryFdn();  //查询fdn

                    queryPin1State();//查询Pin1状态:开或关

                }

                notifyPinLockedRegistrantsIfNeeded(null);

                notifyReadyRegistrantsIfNeeded(null);

            }

        }

    }

UiccApplication首先是更新了自己的状态。特别需要注意update的最后一个大if。当已经确认状态发生变化时,又对mAppState做了检测,在mAppState==AppState.APPSTATE_READY的情况下又查询了fdn和pin。难道AppState.APPSTATE_READY并不代表pin已经通过了么?查看notifyPinLockedRegistrantsIfNeeded(),notifyReadyRegistrantsIfNeeded()。也许将这段代码等价有下面的代码更容易理解(这里一定要仔细阅读前面的状态说明)。

if (mAppState == AppState.APPSTATE_READY) {

                    queryFdn();  //查询fdn

                    queryPin1State();//查询Pin1状态:开或关

notifyPinLockedRegistrantsIfNeeded(null);

                }else{

                    notifyReadyRegistrantsIfNeeded(null);

}

假设UICC卡的pin功能开启了,则mAppState == AppState.APPSTATE_PIN(或类似),且并没有被解锁,则注册监听此消息的类被通知。这里有两个地方注册监听了此消息。一个是IccCardProxy。另一个是SimRecord,代码就不贴了。SimRecord的处理就是在Pin锁定的状态下仍然读取少量的UICC卡EF信息。IccCardProxy的处理如下:

private void processLockedState() {

        synchronized (mLock) {

            if (mUiccApplication == null) {

                //Don't need to do anything if non-existent application is locked

                return;

            }

            PinState pin1State = mUiccApplication.getPin1State();

            if (pin1State == PinState.PINSTATE_ENABLED_PERM_BLOCKED) {

                setExternalState(State.PERM_DISABLED);

                return;

            }

 

            AppState appState = mUiccApplication.getState();

            switch (appState) {

                case APPSTATE_PIN:

                    mPinLockedRegistrants.notifyRegistrants();

                    setExternalState(State.PIN_REQUIRED);

                    break;

                case APPSTATE_PUK:

                    setExternalState(State.PUK_REQUIRED);

                    break;

                case APPSTATE_DETECTED:

                case APPSTATE_READY:

                case APPSTATE_SUBSCRIPTION_PERSO:

                case APPSTATE_UNKNOWN:

                    // Neither required

                    break;

            }

        }

    }

可以看到,它实际上的处理就是将需要解锁的信息写入数据库,以便APP查询。中间过程就不详细叙述了,大致过程如下图所示:


当手机开机或OnSystemReady触发了KeyguardService.OnSystemReady,接着KeyguardViewMediator.onSystemReady();接着doKeyguardLocked。查询UICC卡的状态,在锁pin的时候显示解Pin界面。

对于无pin锁或已经解pin了,mAppState == AppState.APPSTATE_READY。其处理过程基本类似。IccCardProxy将状态信息写入数据库。而RuimRecords或SimRecords则是读取uicc卡的信息。

private void fetchRuimRecords() {

        mRecordsRequested = true;

 

        if (DBG) log("fetchRuimRecords " + mRecordsToLoad);

 

        mCi.getIMSIForApp(mParentApp.getAid(), obtainMessage(EVENT_GET_IMSI_DONE));

        mRecordsToLoad++;

 

        mFh.loadEFTransparent(EF_ICCID,

                obtainMessage(EVENT_GET_ICCID_DONE));

        mRecordsToLoad++;

 

        mFh.loadEFTransparent(EF_PL,

                obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfPlLoaded()));

        mRecordsToLoad++;

 

        mFh.loadEFTransparent(EF_CSIM_LI,

                obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimLiLoaded()));

        mRecordsToLoad++;

 

        mFh.loadEFTransparent(EF_CSIM_SPN,

                obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimSpnLoaded()));

        mRecordsToLoad++;

 

        mFh.loadEFLinearFixed(EF_CSIM_MDN, 1,

                obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimMdnLoaded()));

        mRecordsToLoad++;

 

        mFh.loadEFTransparent(EF_CSIM_IMSIM,

                obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimImsimLoaded()));

        mRecordsToLoad++;

 

        mFh.loadEFLinearFixedAll(EF_CSIM_CDMAHOME,

                obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimCdmaHomeLoaded()));

        mRecordsToLoad++;

 

        // Entire PRL could be huge. We are only interested in

        // the first 4 bytes of the record.

        mFh.loadEFTransparent(EF_CSIM_EPRL, 4,

                obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimEprlLoaded()));

        mRecordsToLoad++;

 

        mFh.loadEFTransparent(EF_CSIM_MIPUPP,

                obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimMipUppLoaded()));

        mRecordsToLoad++;

 

        if (DBG) log("fetchRuimRecords " + mRecordsToLoad + " requested: " + mRecordsRequested);

        // Further records that can be inserted are Operator/OEM dependent

    }

总结类图结构和以上UICC管理流程如下图所示:


首先是RILJ检测到UICC卡状态(或Radio状态的变化),向上层UiccController广播一个消息,UiccController接收到此消息后,调用RILJ的有关函数获得状态的具体信息,接着根据此状态信息,更新UiccCard,接着更新UiccCardApplicaton。UiccCardApplication接下来的处理分了两条线:1,向IccCardProxy广播卡信息,IccCardProxy将卡的状态信息写入Telephony.db以供APP查询;2,通过其内部类IccFileHandler读取Uicc卡文件,并存入IccRecord,读取完毕后再通过与1类似的方式将EVENT_RECORDS_LOADED状态写入Telephony.db。



  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值