UICC开机初始化

#注:本文基于Android O

在PhoneFactory中创建Phone对象时,UiccController也会被创建。
PhoneFactory.java

public static void makeDefaultPhone(Context context) {
            ......
            sUiccController = UiccController.make(context, sCommandsInterfaces);
            ......
}

而UiccController初始化时就会注册监听一些事件,如ICC状态变化、RadioOn/RadioAvailable,
当这些事件被触发后,就会触发UiccController处理EVENT_ICC_STATUS_CHANGED事件。
UiccController.java

private UiccController(Context c, CommandsInterface []ci) {
    ......
    for (int i = 0; i < mCis.length; i++) {
        Integer index = new Integer(i);
        mCis[i].registerForIccStatusChanged(this, EVENT_ICC_STATUS_CHANGED, index);
        ......
        if (!StorageManager.inCryptKeeperBounce()) {
            mCis[i].registerForAvailable(this, EVENT_ICC_STATUS_CHANGED, index);
        } else {
            mCis[i].registerForOn(this, EVENT_ICC_STATUS_CHANGED, index);
        }
        ......
    }
    ......
}

然后再来看收到ICC状态变化后的处理流程,实际上就是去读取ICC的状态。

public void handleMessage (Message msg) {
        ......
        switch (msg.what) {
            case EVENT_ICC_STATUS_CHANGED:
                ......
                mCis[index].getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE, index));
                break;
            case EVENT_GET_ICC_STATUS_DONE:
                ......
                onGetIccCardStatusDone(ar, index);
                break;
        ......
}

读取到的ICC状态后,接着就创建/更新UiccCard对象,同时也会通知相关的监听者。

private synchronized void onGetIccCardStatusDone(AsyncResult ar, Integer index) {
    ......
    if (mUiccCards[index] == null) {
        //Create new card
        mUiccCards[index] = new UiccCard(mContext, mCis[index], status, index);
    } else {
        //Update already existing card
        mUiccCards[index].update(mContext, mCis[index] , status);
    }
    ......
    mIccChangedRegistrants.notifyRegistrants(new AsyncResult(null, index, null));
}

我们来看看UiccCard的初始化流程,其实就是比update()多了个存储CardState和PhoneId的过程。
UiccCard.java

public UiccCard(Context c, CommandsInterface ci, IccCardStatus ics, int phoneId) {
    mCardState = ics.mCardState;
    mPhoneId = phoneId;
    update(c, ci, ics);
}

主要的操作还是在update()方法中,里面会创建/删除/更新UiccCardApplication对象。若读取到了新的app,则创建;若之前的app已不在当前读取的值中,则删除;否则就更新。

createAndUpdateCatServiceLocked()为STK相关内容,暂不讨论。

最后还有一个关于SIM卡热插拔的处理。

public void update(Context c, CommandsInterface ci, IccCardStatus ics) {
        ......
        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);
            }
        }

        createAndUpdateCatService();
        ......
        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));
            }
        }
}

先来看看热插拔吧,若手机支持热插拔,就啥都不会做;若不支持热插拔,则会弹出提示要求用户重启手机。

public void handleMessage(Message msg){
    switch (msg.what) {
        case EVENT_CARD_REMOVED:
            onIccSwap(false);
            break;
        case EVENT_CARD_ADDED:
            onIccSwap(true);
            break;
    ......
}

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

再接着之前的流程分析,我们以创建UiccCardApplication的流程为例来分析,而更新UiccCardApplication的流程和创建差不多,就不再赘述了。
UiccCardApplication的构造方法中,会创建IccFileHandler和IccRecords。
UiccCardApplication.java

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();
    }
    ......
}

而在创建IccFileHandler和IccRecords对象时,会根据UiccCardApplication的类型来创建不同的子对象。
例如,对于APPTYPE_SIM,就会创建SIMFileHandler和SIMRecords对象。

(中国移动的卡有2个app,但log只打印了一个APPTYPE_USIM,
中国联通的卡有1个APPTYPE_USIM,
中国电信的卡有1个APPTYPE_USIM和1个APPTYPE_CSIM)

private IccRecords createIccRecords(AppType type, Context c, CommandsInterface ci) {
    if (type == AppType.APPTYPE_USIM || type == AppType.APPTYPE_SIM) {
        return new SIMRecords(this, c, ci);
    } else if (type == AppType.APPTYPE_RUIM || type == AppType.APPTYPE_CSIM){
        return new RuimRecords(this, c, ci);
    } else if (type == AppType.APPTYPE_ISIM) {
        return new IsimUiccRecords(this, c, ci);
    } else {
        // Unknown app type (maybe detection is still in progress)
        return null;
    }
}

private IccFileHandler createIccFileHandler(AppType type) {
    switch (type) {
        case APPTYPE_SIM:
            return new SIMFileHandler(this, mAid, mCi);
        case APPTYPE_RUIM:
            return new RuimFileHandler(this, mAid, mCi);
        case APPTYPE_USIM:
            return new UsimFileHandler(this, mAid, mCi);
        case APPTYPE_CSIM:
            return new CsimFileHandler(this, mAid, mCi);
        case APPTYPE_ISIM:
            return new IsimFileHandler(this, mAid, mCi);
        default:
            return null;
    }
}

先来看看SIMFileHandler,也仅仅是调用了父类的构造方法,而其父类的构造方法也仅仅是将传入的参数保存了起来。

    public SIMFileHandler(UiccCardApplication app, String aid, CommandsInterface ci) {
        super(app, aid, ci);
    }
    protected IccFileHandler(UiccCardApplication app, String aid, CommandsInterface ci) {
        mParentApp = app;
        mAid = aid;
        mCi = ci;
    }

然后再来看SIMRecords,其构造方法中会注册监听SIM卡中app ready的事件。
而一般情况下,第一次上报卡状态时,基本上就已经是ready状态了,这时,在注册监听app ready状态后就会马上触发SIMRecords处理EVENT_APP_READY事件。即使当前上报的状态不是ready,等到后面上传ready状态时也会触发EVENT_APP_READY事件。
SIMRecords.java

public SIMRecords(UiccCardApplication app, Context c, CommandsInterface ci) {
    ......
    mParentApp.registerForReady(this, EVENT_APP_READY, null);
    ......
}


SIMRecords的Handler处理EVENT_APP_READY事件。

public void handleMessage(Message msg) {
        ......
        switch (msg.what) {
            case EVENT_APP_READY:
                onReady();
                break;
        ......
}

public void onReady() {
    fetchSimRecords();
}

这时就会去读取各种所需的ICC数据,包括IMSI,ICCID,MSISDN, MBI, AD, MWIS, SPDI等。

protected void fetchSimRecords() {
    ......
    mCi.getIMSIForApp(mParentApp.getAid(), obtainMessage(EVENT_GET_IMSI_DONE));
    mRecordsToLoad++;

    mFh.loadEFTransparent(EF_ICCID, obtainMessage(EVENT_GET_ICCID_DONE));
    mRecordsToLoad++;

    new AdnRecordLoader(mFh).loadFromEF(EF_MSISDN, getExtFromEf(EF_MSISDN), 1,
                obtainMessage(EVENT_GET_MSISDN_DONE));
    mRecordsToLoad++;
    ......
}

在众多ICC数据中,IMSI被单独列了出来,当读取到IMSI后,卡状态会从READY变为IMSI。其变化流程为:

-> 处理EVENT_GET_IMSI_DONE

-> 通知相关监听者

-> 触发IccCardProxy发送TelephonyIntents.ACTION_SIM_STATE_CHANGED的广播,ICC状态值为IccCardConstants.INTENT_VALUE_ICC_IMSI

public void handleMessage(Message msg) {
    ......
    try {
        switch (msg.what) {
            case EVENT_GET_IMSI_DONE:
                ......
                mImsiReadyRegistrants.notifyRegistrants();
                break;
        ......
        } finally {
            // Count up record load responses even if they are fails
            if (isRecordLoadResponse) {
                onRecordLoaded();
            }
        }
    ......
}

每当读取完一条信息后,就会调用onRecordLoaded()方法,判断是否所有需要的数据都读取完毕,如果都读取完了,就继续调用onAllRecordsLoaded()方法。需要注意的是,即使在读取ICC数据是出错,只要有返回消息,我们仍然认为这条数据已经读取了。

protected void onRecordLoaded() {
    ......
    mRecordsToLoad -= 1;
    ......
    if (mRecordsToLoad == 0 && mRecordsRequested == true) {
        onAllRecordsLoaded();
    } else if (mRecordsToLoad < 0) {
        loge("recordsToLoad <0, programmer error suspected");
        mRecordsToLoad = 0;
    }
}

ICC数据读取完毕后,就通知监听者,这时卡状态就变为LOADED了。

protected void onAllRecordsLoaded() {
    ......
    mRecordsLoadedRegistrants.notifyRegistrants(
        new AsyncResult(null, null, null));
}


总结一下,创建Phone时同时创建UiccController,并注册监听卡状态变化,modem检测到卡后上报卡状态发生变化,AP就去读取卡状态,之后再创建UiccCard,在UiccCard中,会根据读取到的卡信息,创建UiccCardApplication,并根据不同app类型创建IccFileHandler和IccRecords,app类型为APPTYPE_SIM时会创建SIMFileHandler和SIMRecords,而SIMRecords的构造方法中会注册监听app ready的事件,等到modem上报app ready时,就会去读取ICC的各种数据,当读取到IMSI后,卡状态变为IMSI,全部数据读取完后,卡状态变为LOADED。Over!
 

转载于:https://my.oschina.net/igiantpanda/blog/1608371

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值