博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
UICC开机初始化
阅读量:6913 次
发布时间:2019-06-27

本文共 9287 字,大约阅读时间需要 30 分钟。

hot3.png

#注:本文基于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

你可能感兴趣的文章
删除Exchange2010数据库报错“此邮箱数据库与一个或多个活动 MailboxExport 队列关联”...
查看>>
我的友情链接
查看>>
Linux性能实时监测工具 Netdata
查看>>
Awstats服务
查看>>
linux源地址转换(一)
查看>>
ZooKeeper客户端Curator使用一 创建连接
查看>>
图文说明虚拟机的几种网络模式
查看>>
将 instance 连接到 first_local_net - 每天5分钟玩转 OpenStack(82)
查看>>
Ubuntu屏幕截图快捷键知多少
查看>>
60佳优秀的 Photoshop 网页制作教程【下篇】
查看>>
JQuery Select多选插件实现
查看>>
1-Ictclas50分词系统ForJava
查看>>
Tomcat部署servlet小应用无法找到相应的servlet类的问题可能原因
查看>>
Coding and Paper Letter(七)
查看>>
51CTO篮球俱乐部精彩集锦(5月9日)
查看>>
java实现正则表达式到NFA的转换
查看>>
python-54: 验证码登陆尝试
查看>>
Fedora 11 安装指南-06
查看>>
C#中的异步和多线程
查看>>
归并非递归排序
查看>>