Android4.X中SIM卡信息初始化過程詳解
本文實例講述了Android4.X中SIM卡信息初始化過程詳解。分享給大家供大家參考,具體如下:
Phone 對象初始化的過程中,會加載SIM卡的部分?jǐn)?shù)據(jù)信息,這些信息會保存在IccRecords 和 AdnRecordCache 中。SIM卡的數(shù)據(jù)信息的初始化過程主要分為如下幾個步驟
1.RIL 和 UiccController 建立監(jiān)聽關(guān)系 ,SIM卡狀態(tài)發(fā)生變化時,UiccController 第一個去處理。
Phone 應(yīng)用初始化 Phone 對象時會建立一個 RIL 和UiccController 的監(jiān)聽關(guān)系:UiccController 監(jiān)聽 RIL,相關(guān)代碼如下
sCommandsInterface = new RIL(context, networkMode, cdmaSubscription); UiccController.make(context, sCommandsInterface); UiccController 構(gòu)造的過程 private UiccController(Context c, CommandsInterface ci) { if (DBG) log("Creating UiccController"); mContext = c; mCi = ci; mCi.registerForIccStatusChanged(this, EVENT_ICC_STATUS_CHANGED, null); // TODO remove this once modem correctly notifies the unsols mCi.registerForOn(this, EVENT_ICC_STATUS_CHANGED, null); }
從代碼中可以看出,UiccController 對象被注冊為RIL對象的監(jiān)聽者,當(dāng) RIL 檢測到 uicc card 狀態(tài)發(fā)生變化或者 radio on UiccController 都會處理對應(yīng)的數(shù)據(jù)變化。UiccController 是 SIM卡狀態(tài)發(fā)生變化后的第一個處理者。
UiccController 處理 EVENT_ICC_STATUS_CHANGED
public void handleMessage (Message msg) { synchronized (mLock) { switch (msg.what) { case EVENT_ICC_STATUS_CHANGED: if (DBG) log("Received EVENT_ICC_STATUS_CHANGED, calling getIccCardStatus"); mCi.getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE)); break; case EVENT_GET_ICC_STATUS_DONE: if (DBG) log("Received EVENT_GET_ICC_STATUS_DONE"); AsyncResult ar = (AsyncResult)msg.obj; onGetIccCardStatusDone(ar); break; default: Rlog.e(LOG_TAG, " Unknown Event " + msg.what); } } }
從代碼中可以看出,RIL 上報 SIM卡狀態(tài)發(fā)生變化后,做了兩件事,一是獲取SIM卡的具體狀態(tài),二是處理這個狀態(tài)。
UiccController 處理具體的SIM卡狀態(tài)
private synchronized void onGetIccCardStatusDone(AsyncResult ar) { 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; } IccCardStatus status = (IccCardStatus)ar.result; if (mUiccCard == null) { //Create new card mUiccCard = new UiccCard(mContext, mCi, status); } else { //Update already existing card mUiccCard.update(mContext, mCi , status); } if (DBG) log("Notifying IccChangedRegistrants"); mIccChangedRegistrants.notifyRegistrants(); }
從代碼中可以看出,做了兩件事,
一是 創(chuàng)建或者 更新 UiccCard
二是 通知監(jiān)聽 UiccController 的監(jiān)聽者。
2.創(chuàng)建或者更新 UiccCard,UiccCard 創(chuàng)建或者更新與SIM卡類型對應(yīng)的UiccCardApplication.
一個UiccCard 對象代表著一張SIM卡,UiccCard 根據(jù)獲取的SIM卡信息創(chuàng)建 UiccCardApplication,UiccCardApplication去讀取具體的SIM卡里的信息。
更新UiccCard
public void update(Context c, CommandsInterface ci, IccCardStatus ics) { synchronized (mLock) { if (mDestroyed) { loge("Updated after destroyed! Fix me!"); return; } CardState oldState = mCardState; mCardState = ics.mCardState; mUniversalPinState = ics.mUniversalPinState; mGsmUmtsSubscriptionAppIndex = ics.mGsmUmtsSubscriptionAppIndex; mCdmaSubscriptionAppIndex = ics.mCdmaSubscriptionAppIndex; mImsSubscriptionAppIndex = ics.mImsSubscriptionAppIndex; mContext = c; mCi = ci; //update applications if (DBG) log(ics.mApplications.length + " applications"); 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); } } if (mUiccApplications.length > 0 && mUiccApplications[0] != null) { // Initialize or Reinitialize CatService mCatService = CatService.getInstance(mCi, mContext, this); } else { if (mCatService != null) { mCatService.dispose(); } mCatService = 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; } }
IccCardStatus,記錄了RIL 讀取的SIM卡的信息,UiccCard 根據(jù) IccCardStatus 中記錄的應(yīng)用程序信息,創(chuàng)建 UiccCardApplication.
UiccCard 還創(chuàng)建了 CatService,用于讀取 STK 的信息。
創(chuàng)建或者更新 UiccCardApplication
UiccCardApplication,會記錄對應(yīng)的卡的狀態(tài),類型,以及卡的記錄信息。
//創(chuàng)建 UiccCardApplication UiccCardApplication(UiccCard uiccCard, IccCardApplicationStatus as, Context c, CommandsInterface ci) { if (DBG) log("Creating UiccApp: " + as); mUiccCard = uiccCard; mAppState = as.app_state; mAppType = as.app_type; mPersoSubState = as.perso_substate; mAid = as.aid; mAppLabel = as.app_label; mPin1Replaced = (as.pin1_replaced != 0); mPin1State = as.pin1; mPin2State = as.pin2; mContext = c; mCi = ci; mIccFh = createIccFileHandler(as.app_type); mIccRecords = createIccRecords(as.app_type, mContext, mCi); ///讀取 SIM卡上的 EF 文件信息 if (mAppState == AppState.APPSTATE_READY) { queryFdn(); // FDN 信息 queryPin1State(); // Pin State } } //更新UiccCardApplication 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; 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(); } notifyPinLockedRegistrantsIfNeeded(null); notifyReadyRegistrantsIfNeeded(null); } } }
在更新和創(chuàng)建UiccCardApplication的過程中,有如下幾個重要的變量
IccRecords
記錄 SIM卡上的EF 文件信息,實現(xiàn)類有SIMRecords,RuimRecords,IsimUiccRecords,對應(yīng)于不同的類型的SIM卡。
IccFileHandler
根據(jù)SIM卡的類型,去讀取SIM卡上的信息,實現(xiàn)類有SIMFileHandler,RuimFileHandler,UsimFileHandler,CsimFileHandler,IsimFileHandler,對應(yīng)于不同的SIM卡。
創(chuàng)建 IccRecords 對象
正如前面所描述的,IccRecords 記錄SIM卡的EF文件信息,具體的讀取SIM卡EF文件信息的過程是由 IccFileHandler 來實現(xiàn)的,以 SIMRecords 為例。
public SIMRecords(UiccCardApplication app, Context c, CommandsInterface ci) { super(app, c, ci); // 1.電話本的緩存 mAdnCache = new AdnRecordCache(mFh); mVmConfig = new VoiceMailConstants(); mSpnOverride = new SpnOverride(); mRecordsRequested = false; // No load request is made till SIM ready // recordsToLoad is set to 0 because no requests are made yet mRecordsToLoad = 0; mCi.setOnSmsOnSim(this, EVENT_SMS_ON_SIM, null); mCi.registerForIccRefresh(this, EVENT_SIM_REFRESH, null); // Start off by setting empty state resetRecords(); //2. 讀取 SIM卡的所有重要的記錄信息 mParentApp.registerForReady(this, EVENT_APP_READY, null); if (DBG) log("SIMRecords X ctor this=" + this); }
這個過程包含兩個重要的步驟
創(chuàng)建AdnRecordCache,用于保存電話本數(shù)據(jù),根據(jù)EF的ID,可以分別讀取SIM卡和USIM卡的電話本數(shù)據(jù)。AdnRecordCache 中持有一個UsimPhoneBookManager,它就是用來讀取USIM卡電話本數(shù)據(jù)的。GSM的SIM卡和WCDMA的USIM卡都是對應(yīng)的 SimRecords.
讀取SIM卡的所有重要記錄信息,在fetchSimRecords 方法中實現(xiàn)。
protected void fetchSimRecords() { mRecordsRequested = true; if (DBG) log("fetchSimRecords " + mRecordsToLoad); mCi.getIMSIForApp(mParentApp.getAid(), obtainMessage(EVENT_GET_IMSI_DONE)); mRecordsToLoad++; mFh.loadEFTransparent(EF_ICCID, obtainMessage(EVENT_GET_ICCID_DONE)); mRecordsToLoad++; // FIXME should examine EF[MSISDN]'s capability configuration // to determine which is the voice/data/fax line new AdnRecordLoader(mFh).loadFromEF(EF_MSISDN, EF_EXT1, 1, obtainMessage(EVENT_GET_MSISDN_DONE)); mRecordsToLoad++; // Record number is subscriber profile mFh.loadEFLinearFixed(EF_MBI, 1, obtainMessage(EVENT_GET_MBI_DONE)); mRecordsToLoad++; mFh.loadEFTransparent(EF_AD, obtainMessage(EVENT_GET_AD_DONE)); mRecordsToLoad++; // Record number is subscriber profile mFh.loadEFLinearFixed(EF_MWIS, 1, obtainMessage(EVENT_GET_MWIS_DONE)); mRecordsToLoad++; // Also load CPHS-style voice mail indicator, which stores // the same info as EF[MWIS]. If both exist, both are updated // but the EF[MWIS] data is preferred // Please note this must be loaded after EF[MWIS] mFh.loadEFTransparent( EF_VOICE_MAIL_INDICATOR_CPHS, obtainMessage(EVENT_GET_VOICE_MAIL_INDICATOR_CPHS_DONE)); mRecordsToLoad++; // Same goes for Call Forward Status indicator: fetch both // EF[CFIS] and CPHS-EF, with EF[CFIS] preferred. mFh.loadEFLinearFixed(EF_CFIS, 1, obtainMessage(EVENT_GET_CFIS_DONE)); mRecordsToLoad++; mFh.loadEFTransparent(EF_CFF_CPHS, obtainMessage(EVENT_GET_CFF_DONE)); mRecordsToLoad++; getSpnFsm(true, null); mFh.loadEFTransparent(EF_SPDI, obtainMessage(EVENT_GET_SPDI_DONE)); mRecordsToLoad++; mFh.loadEFLinearFixed(EF_PNN, 1, obtainMessage(EVENT_GET_PNN_DONE)); mRecordsToLoad++; mFh.loadEFTransparent(EF_SST, obtainMessage(EVENT_GET_SST_DONE)); mRecordsToLoad++; mFh.loadEFTransparent(EF_INFO_CPHS, obtainMessage(EVENT_GET_INFO_CPHS_DONE)); mRecordsToLoad++; mFh.loadEFTransparent(EF_CSP_CPHS,obtainMessage(EVENT_GET_CSP_CPHS_DONE)); mRecordsToLoad++; mFh.loadEFTransparent(EF_GID1, obtainMessage(EVENT_GET_GID1_DONE)); mRecordsToLoad++; // XXX should seek instead of examining them all if (false) { // XXX mFh.loadEFLinearFixedAll(EF_SMS, obtainMessage(EVENT_GET_ALL_SMS_DONE)); mRecordsToLoad++; } if (CRASH_RIL) { String sms = "0107912160130310f20404d0110041007030208054832b0120" + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + "ffffffffffffffffffffffffffffff"; byte[] ba = IccUtils.hexStringToBytes(sms); mFh.updateEFLinearFixed(EF_SMS, 1, ba, null, obtainMessage(EVENT_MARK_SMS_READ_DONE, 1)); } if (DBG) log("fetchSimRecords " + mRecordsToLoad + " requested: " + mRecordsRequested); }
總的來說,創(chuàng)建 SimRecords 的過程就是讀取并且保存SIM卡重要信息的過程。其中,電話本的信息保存在 mAdnCache 中,其他信息保存在 SimRecords 中,但是在Phone對象完成初始化后,mAdnCache 里是空的,也就是說,在IccRecords 初始化的過程中,AdnRecordCache 并沒有主動去請求SIM卡聯(lián)系人的數(shù)據(jù)。
所有的IccRecords 是通過 IccFileHandler 向Modem 發(fā)命令讀取數(shù)據(jù)的。他們之間的交互圖如下
3.通知UiccController 的監(jiān)聽者,與UiccCardApplication的相關(guān)信息可以更新了。根據(jù)分析源代碼,我們可以看到,PhoneBase ,ServiceStateTracker,IccCardProxy,DcTrackerBase,這些類是 UiccController 的監(jiān)聽者。他們都會處理UiccController 的變化。我們可以這么理解,這些類是SIM卡狀態(tài)發(fā)生變化后,第二批處理SIM卡狀態(tài)變化的實體。第一個處理SIM卡狀態(tài)變化的是 UiccController.
希望本文所述對大家Android程序設(shè)計有所幫助。
- Android 利用反射+try catch實現(xiàn)sdk按需引入依賴庫的方法
- 解決Android Studio sdk emulator directory is missing問題
- Android Studio下載、安裝和配置+SDK+tools下載(無敵超級詳細(xì)版本)
- Android Studio報錯unable to access android sdk add-on list解決方案
- Android Studio設(shè)置或修改Android SDK路徑方法
- Android中實現(xiàn)自動生成布局View的初始化代碼方法
- Android使用ContentProvider初始化SDK庫方案小結(jié)
相關(guān)文章
Android應(yīng)用 坐標(biāo)系詳細(xì)介紹
這篇文章主要介紹了 Android 坐標(biāo)系的相關(guān)資料,本文對Android 坐標(biāo)系的知識做了詳細(xì)解讀,需要的朋友可以參考下2016-11-11Android自定義ViewGroup實現(xiàn)豎向引導(dǎo)界面
這篇文章主要為大家詳細(xì)介紹了Andoird自定義ViewGroup實現(xiàn)豎向引導(dǎo)界面,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-03-03Android 使用Picasso加載網(wǎng)絡(luò)圖片等比例縮放的實現(xiàn)方法
在做android圖片加載的時候,由于手機屏幕受限,很多大圖加載過來的時候,我們要求等比例縮放,接下來小編給大家?guī)砹薃ndroid 使用Picasso加載網(wǎng)絡(luò)圖片等比例縮放的實現(xiàn)方法,感興趣的朋友一起看看吧2018-08-08ComposeDesktop開發(fā)桌面端多功能APK工具
這篇文章主要為大家介紹了ComposeDesktop開發(fā)桌面端多功能APK工具實現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-07-07Android同步異步任務(wù)與多線程及Handler消息處理機制基礎(chǔ)詳細(xì)講解
這篇文章主要介紹了Android同步異步任務(wù)與多線程及Handler消息處理機制基礎(chǔ),handler其實就是主線程在起了一個子線程,子線程運行并生成Message,Looper獲取message并傳遞給Handler,Handler逐個獲取子線程中的Message,感興趣的小伙伴快跟隨小編一起學(xué)習(xí)一下2022-11-11