Telephony-Uicc双卡管理

这个部分原稿来自于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。

猜你喜欢

转载自blog.csdn.net/xuning2516/article/details/70952618