Telephony-Uicc双卡管理

来源:互联网 发布:淘宝发布宝贝商家编码 编辑:程序博客网 时间:2024/06/05 02:44

这个部分原稿来自于Android6.0的phone应用源码分析(9)——UICC卡管理
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()。也许将这段代码等价有下面的代码更容易理解(这里一定要仔细阅读前面的状态说明)。
queryPin1State只是为了查询pin码功能是否开启,处理mIccLockEnabled 和mPin1State两个状态不一致的情况
/* REMOVE when mIccLockEnabled is not needed/
private void onQueryFacilityLock(AsyncResult ar) {
synchronized (mLock) {
if(ar.exception != null) {
if (DBG) log(“Error in querying facility lock:” + ar.exception);
return;
}

        int[] ints = (int[])ar.result;        if(ints.length != 0) {            if (DBG) log("Query facility lock : "  + ints[0]);            mIccLockEnabled = (ints[0] != 0);            if (mIccLockEnabled) {                mPinLockedRegistrants.notifyRegistrants();            }            // Sanity check: we expect mPin1State to match mIccLockEnabled.            // When mPin1State is DISABLED mIccLockEanbled should be false.            // When mPin1State is ENABLED mIccLockEnabled should be true.            //            // Here we validate these assumptions to assist in identifying which ril/radio's            // have not correctly implemented GET_SIM_STATUS            switch (mPin1State) {                case PINSTATE_DISABLED:                    if (mIccLockEnabled) {                        loge("QUERY_FACILITY_LOCK:enabled GET_SIM_STATUS.Pin1:disabled."                                + " Fixme");                    }                    break;                case PINSTATE_ENABLED_NOT_VERIFIED:                case PINSTATE_ENABLED_VERIFIED:                case PINSTATE_ENABLED_BLOCKED:                case PINSTATE_ENABLED_PERM_BLOCKED:                    if (!mIccLockEnabled) {                        loge("QUERY_FACILITY_LOCK:disabled GET_SIM_STATUS.Pin1:enabled."                                + " Fixme");                    }                case PINSTATE_UNKNOWN:                default:                    if (DBG) log("Ignoring: pin1state=" + mPin1State);                    break;            }        } else {            loge("Bogus facility lock response");        }    }}

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

可以看到,它实际上的处理就是将需要解锁的信息写入数据库,以便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。

0 0
原创粉丝点击