首页 > 代码库 > Android4.4 Telephony流程分析——SIM卡开机时的数据加载
Android4.4 Telephony流程分析——SIM卡开机时的数据加载
本文代码以MTK平台Android 4.4为分析对象,与Google原生AOSP有些许差异,请读者知悉。
本文主要介绍sim卡数据的读取过程,当射频状态处于准备状态时,此时UiccCardApplication应处于AppState.APPSTATE_READY状态,我们沿着这个信号跟踪下去。阅读本文时可先阅读Android4.4 Telephony流程分析——SIM卡开机时的初始化一文,了解Radio和sim卡状态更新过程。
先来看一下数据加载的序列图:
step1~step3,走的是更新过程,创建过程参考Android4.4 Telephony流程分析——SIM卡开机时的初始化一文step21之后的步骤。
step4,通过Modem查询sim卡的FDN(固定拨号)数据。
step5,通过Modem查询sim卡的pin1状态。
step6~step7,将pin1状态通知出去,IccCardProxy会注册mPinLockedRegistrants。
step8~step9,将sim卡ready状态发出去。
private void notifyReadyRegistrantsIfNeeded(Registrant r) { if (mDestroyed) { return; } if (mAppState == AppState.APPSTATE_READY) { if (mPin1State == PinState.PINSTATE_ENABLED_NOT_VERIFIED || mPin1State == PinState.PINSTATE_ENABLED_BLOCKED || mPin1State == PinState.PINSTATE_ENABLED_PERM_BLOCKED) { loge("Sanity check failed! APPSTATE is ready while PIN1 is not verified!!!"); // Don't notify if application is in insane state return; } if (r == null) { if (DBG) log("Notifying registrants: READY"); mReadyRegistrants.notifyRegistrants(); } else { if (DBG) log("Notifying 1 registrant: READY"); r.notifyRegistrant(new AsyncResult(null, null, null)); } } }
如果此时pin1是被激活的,也就是sim卡开启了pin1锁,sim卡ready状态就不会发出去。
监听mReadyRegistrants状态变化的对象很多,主要有:SIMRecords(同类的还有RuimRecords、IsimUiccRecords),IccCardProxy(会将SIM卡状态广播出去),GsmServiceStateTracker(会根据SIM状态去注册网络),这里主要说一下SIMRecords,读取SIM的数据。
step12,fetchSimRecords()方法:
//MTK-END [mtk80601][111215][ALPS00093395] protected void fetchSimRecords() { mRecordsRequested = true; if (DBG) log("fetchSimRecords " + mRecordsToLoad); mCi.getIMSIForApp(mParentApp.getAid(), obtainMessage(EVENT_GET_IMSI_DONE));//读IMSI mRecordsToLoad++; //iccFh.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(phone).loadFromEF(EF_MSISDN, EF_EXT1, 1, //obtainMessage(EVENT_GET_MSISDN_DONE)); //recordsToLoad++; // 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)); //recordsToLoad++; 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++; /* Detail description: This feature provides a interface to get menu title string from EF_SUME */ if (mTelephonyExt != null) { if (mTelephonyExt.isSetLanguageBySIM()) { mFh.loadEFTransparent(EF_SUME, obtainMessage(EVENT_QUERY_MENU_TITLE_DONE)); mRecordsToLoad++; } } else { loge("fetchSimRecords(): mTelephonyExt is null!!!"); } fetchCPHSOns(); // 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); /* * Here, we assume that PHB is ready and try to read the entries. * If it is not, we will receive the event EVENT_PHB_READY later. * Then, we will ready the PHB entries again. */ fetchPhbRecords();//读adn联系人 fetchRatBalancing(); }
读SIM卡是要通过Modem的,上层读Modem就得通过RIL,我们以读ADN联系人fetchPhbRecords()为例,来看看读取过程step13~step22,主要是通过IccFileHandler实现,先请求adn的长度(step16~step19),然后再请求具体的adn联系人数据step20~step22,这个过程就是与Modem交互的过程,读者可以先了解一下SIM卡的文件结构,才能更好的理解为什么这样读,我也不甚熟悉。
step23,获取SIM卡内置的紧急号码,这个是由运营商定制的。
step24~step26,当需要load的数据都load完成,才会执行,再在onAllRecordsLoaded中发布mRecordsLoadedRegistrants通知。
protected void onRecordLoaded() { // One record loaded successfully or failed, In either case // we need to update the recordsToLoad count mRecordsToLoad -= 1; if (DBG) log("onRecordLoaded " + mRecordsToLoad + " requested: " + mRecordsRequested); if (mRecordsToLoad == 0 && mRecordsRequested == true) { onAllRecordsLoaded(); } else if (mRecordsToLoad < 0) { loge("recordsToLoad <0, programmer error suspected"); mRecordsToLoad = 0; } }
step27之后的步骤,都是对mRecordsLoadedRegistrants侦听的响应,IccCardProxy侦听到后,会发广播给外界:
private void broadcastIccStateChangedIntent(String value, String reason) { synchronized (mLock) { if (mQuietMode) { log("QuietMode: NOT Broadcasting intent ACTION_SIM_STATE_CHANGED " + value + " reason " + reason); return; } Intent intent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED); //intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); intent.putExtra(PhoneConstants.PHONE_NAME_KEY, "Phone"); intent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE, value); intent.putExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON, reason); intent.putExtra(PhoneConstants.GEMINI_SIM_ID_KEY, mSimId); if (DBG) log("Broadcasting intent ACTION_SIM_STATE_CHANGED " + value + " reason " + reason + " sim id " + mSimId); ActivityManagerNative.broadcastStickyIntent(intent, READ_PHONE_STATE, UserHandle.USER_ALL); } } public void broadcastIccStateChangedExtendIntent(String value, String reason) { synchronized (mLock) { if (mQuietMode) { log("QuietMode: NOT Broadcasting extend intent ACTION_SIM_STATE_CHANGED " + value + " reason " + reason); return; } Intent intent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED_EXTEND); //intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); intent.putExtra(PhoneConstants.PHONE_NAME_KEY, "Phone"); intent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE, value); intent.putExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON, reason); intent.putExtra(PhoneConstants.GEMINI_SIM_ID_KEY, mSimId); if (DBG) log("Broadcasting intent ACTION_SIM_STATE_CHANGED_EXTEND " + value + " reason " + reason + " sim id " + mSimId); ActivityManagerNative.broadcastStickyIntent(intent, READ_PHONE_STATE, UserHandle.USER_ALL); } }
GsmServiceStateTracker会去刷新运营商名称,有需要的话,还会重新选择注册网络。
右键复制图片地址,在浏览器中打开即可查看大图。
未完待续,有不对的地方,请指正。
Android4.4 Telephony流程分析——SIM卡开机时的数据加载