@UiccCardApplication.java 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 { return null; } }
我们仍然挑选典型的SIMRecords来分析。
public String getIMSI() {} public String getMsisdnNumber() {} public String getGid1() {} public UsimServiceTable getUsimServiceTable() {} public void setMsisdnNumber(String alphaTag, String number, Message onComplete) {} public String getMsisdnAlphaTag() {} public String getVoiceMailNumber() {} public void setVoiceMailNumber(String alphaTag, String voiceNumber, Message onComplete) {} public String getVoiceMailAlphaTag(){} public void setVoiceMessageWaiting(int line, int countWaiting) {} public boolean getVoiceCallForwardingFlag() {} public String getOperatorNumeric() {} public int getDisplayRule(String plmn) {} public boolean isCspPlmnEnabled() {}同时来看一下其集成的父类IccRecords提供的public方法:
public AdnRecordCache getAdnCache() {} public String getIccId() {} public void setImsi(String imsi) {} public String getServiceProviderName() {} public boolean getVoiceMessageWaiting() {} public int getVoiceMessageCount() {} public boolean getRecordsLoaded() {} public void setVoiceCallForwardingFlag(int line, boolean enable, String number) {} public boolean isProvisioned () {} public IsimRecords getIsimRecords() {} public void registerForRecordsLoaded(Handler h, int what, Object obj) {} public void registerForImsiReady(Handler h, int what, Object obj) {} public void registerForRecordsEvents(Handler h, int what, Object obj) {} public void registerForNewSms(Handler h, int what, Object obj) {} public void registerForNetworkSelectionModeAutomatic( Handler h, int what, Object obj) {}从这些方法看出,IccRecords的主要功能分为两部分:
2、注册常用信息的监听器,包括SIMRecords、IMSI、RecordEvents、NewSms、NetworkSelection等事件;
public class SIMRecords extends IccRecords {} public abstract class IccRecords extends Handler implements IccConstants {}然后看他的构造函数:
public SIMRecords(UiccCardApplication app, Context c, CommandsInterface ci) { //在父类中对mCi、mFh等变量初始化 super(app, c, ci); //创建Adn缓存,用于操作SIM卡联系人 mAdnCache = new AdnRecordCache(mFh); //创建VoiceMail缓存 mVmConfig = new VoiceMailConstants(); mSpnOverride = new SpnOverride(); mRecordsRequested = false; // No load request is made till SIM ready mRecordsToLoad = 0; mCi.setOnSmsOnSim(this, EVENT_SMS_ON_SIM, null); mCi.registerForIccRefresh(this, EVENT_SIM_REFRESH, null); //初始化成员变量 resetRecords(); //监听UiccCardApplication的Ready状态 mParentApp.registerForReady(this, EVENT_APP_READY, null); }以上的创建过程完成了两个重要任务:
2、监听UiccCardApplication的Ready状态;
protected void fetchSimRecords() { mRecordsRequested = true; //获取SIM卡的IMSI mCi.getIMSIForApp(mParentApp.getAid(), obtainMessage(EVENT_GET_IMSI_DONE)); mRecordsToLoad++; //得到SIM卡的ICCID mFh.loadEFTransparent(EF_ICCID, obtainMessage(EVENT_GET_ICCID_DONE)); mRecordsToLoad++; new AdnRecordLoader(mFh).loadFromEF(EF_MSISDN, EF_EXT1, 1, obtainMessage(EVENT_GET_MSISDN_DONE)); mRecordsToLoad++; //更新VoiceMail 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++; mFh.loadEFTransparent( EF_VOICE_MAIL_INDICATOR_CPHS, obtainMessage(EVENT_GET_VOICE_MAIL_INDICATOR_CPHS_DONE)); mRecordsToLoad++; 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++; }
从上面可以看出,SIMRecords的更新过程就是用IccFileHandler将常用的SIM卡信息,读取并保存,其中就包括IMSI和ICCID等信息。
对于第二种途径可以进行更丰富的客制化操作。下面我们分别来看这两种途径。
SIM卡中有两个地方可以存储VoiceMail信息(EF_MBDN、EF_MAILBOX_CPHS),我们可以通过读取EF_MBI分区来获取VoiceMail的存储位置。
前面分析过,SIMRecords会在接收到UiccCardApplication的Ready通知后更新SIMRecords信息,其中就包括向Modem请求SIM中的VoiceMail的存储位置信息:@SIMRecords.java protected void fetchSimRecords() { //更新VoiceMail mFh.loadEFLinearFixed(EF_MBI, 1, obtainMessage(EVENT_GET_MBI_DONE)); mRecordsToLoad++; }当接收到EF_MBI中的信息后,由handleMessage()负责解析:
public void handleMessage(Message msg) { switch (msg.what) { case EVENT_GET_MBI_DONE: boolean isValidMbdn; isRecordLoadResponse = true; ar = (AsyncResult)msg.obj; data = (byte[]) ar.result; isValidMbdn = false; if (ar.exception == null) { mMailboxIndex = data[0] & 0xff; if (mMailboxIndex != 0 && mMailboxIndex != 0xff) { //得到VoiceMail的存储位置 isValidMbdn = true; } } mRecordsToLoad += 1; if (isValidMbdn) { // Note: MBDN was not included in NUM_OF_SIM_RECORDS_LOADED //读取EF_MBDN中的VoiceMail new AdnRecordLoader(mFh).loadFromEF(EF_MBDN, EF_EXT6, mMailboxIndex, obtainMessage(EVENT_GET_MBDN_DONE)); } else { //读取EF_MAILBOX_CPHS中的VoiceMail new AdnRecordLoader(mFh).loadFromEF(EF_MAILBOX_CPHS, EF_EXT1, 1, obtainMessage(EVENT_GET_CPHS_MAILBOX_DONE)); } break; } }我们看到,经过对返回值的解析,得到了VoiceMail的存储位置,接着就向Modem申请该位置中的VoiceMail信息,当接收到Modem的反馈后,会再次进入handleMessage()中解析:
public void handleMessage(Message msg) { switch (msg.what) { case EVENT_GET_CPHS_MAILBOX_DONE: case EVENT_GET_MBDN_DONE: mVoiceMailNum = null; mVoiceMailTag = null; isRecordLoadResponse = true; ar = (AsyncResult)msg.obj; adn = (AdnRecord)ar.result; if (adn.isEmpty() && msg.what == EVENT_GET_MBDN_DONE) { mRecordsToLoad += 1; //如果当前的EF_MBDN分区读取失败,再次尝试使用EF_MAILBOX_CPHS分区读取VoiceMail信息 new AdnRecordLoader(mFh).loadFromEF( EF_MAILBOX_CPHS, EF_EXT1, 1, obtainMessage(EVENT_GET_CPHS_MAILBOX_DONE)); break; } //得到VoiceMail的Number和Tag mVoiceMailNum = adn.getNumber(); mVoiceMailTag = adn.getAlphaTag(); break; } }
由此,我们就从SIM卡中读取到了VoiceMail信息。
在SIMRecords的构造函数中初始化了一个特殊的对象:
public SIMRecords(UiccCardApplication app, Context c, CommandsInterface ci) { super(app, c, ci); mVmConfig = new VoiceMailConstants(); }这里出现的VoiceMailConstants就是专门为VoiceMail的配置文件而创建的,他的主要作用就是, 读取并解析系统配置文件,并根据当前的MCC/MNC获取相应的VoiceMail。
@VoiceMailConstants.java VoiceMailConstants () { CarrierVmMap = new HashMap<String, String[]>(); loadVoiceMail(); } private void loadVoiceMail() { FileReader vmReader; //获取配置文件,路径:"etc/voicemail-conf.xml" final File vmFile = new File(Environment.getRootDirectory(), PARTNER_VOICEMAIL_PATH); try { vmReader = new FileReader(vmFile); } catch (FileNotFoundException e) { } try { XmlPullParser parser = Xml.newPullParser(); parser.setInput(vmReader); XmlUtils.beginDocument(parser, "voicemail"); while (true) { //解析XML文件 XmlUtils.nextElement(parser); String name = parser.getName(); if (!"voicemail".equals(name)) { break; } String[] data = new String[SIZE]; //获取预置的VoiceMail信息 String numeric = parser.getAttributeValue(null, "numeric"); data[NAME] = parser.getAttributeValue(null, "carrier"); data[NUMBER] = parser.getAttributeValue(null, "vmnumber"); data[TAG] = parser.getAttributeValue(null, "vmtag"); //保存在CarrierVmMap的缓存中 CarrierVmMap.put(numeric, data); } } catch (XmlPullParserException e) { } catch (IOException e) { } finally { } }在VoiceMailConstants的创建过程中,解析系统"etc/voicemail-conf.xml"文件,并把里面的每一项(包含numeric、carrier、vmnumber、vmtag信息)都保存在CarrierVmMap缓存中,以便查询。
protected void fetchSimRecords() { mCi.getIMSIForApp(mParentApp.getAid(), obtainMessage(EVENT_GET_IMSI_DONE)); //记录下发送请求的个数 mRecordsToLoad++; }然后在handleMessage()的finally中,会将当前已经处理过的Event数目减掉:
public void handleMessage(Message msg) { try { switch (msg.what) { } }catch (RuntimeException exc) { }finally { //每个曾加+1的Event接收到回应后都会是isRecordLoadResponse==true if (isRecordLoadResponse) { onRecordLoaded(); } } } protected void onRecordLoaded() { //当该Event处理完毕后,要将mRecordsToLoad数目-1 mRecordsToLoad -= 1; if (mRecordsToLoad == 0 && mRecordsRequested == true) { //表明所有Event处理完毕 onAllRecordsLoaded(); } else if (mRecordsToLoad < 0) { mRecordsToLoad = 0; } }也就是说,当请求的所有Event都处理完毕后,就会进入onAllRecordsLoaded()中继续处理:
protected void onAllRecordsLoaded() { //得到当前的MCC+MNC String operator = getOperatorNumeric(); //根据的当前的MCC+MNC去配置文件中查找相应的VoiceMail setVoiceMailByCountry(operator); }继续看:
private void setVoiceMailByCountry (String spn) { if (mVmConfig.containsCarrier(spn)) { mIsVoiceMailFixed = true; //读取XML中的VoiceMail信息 mVoiceMailNum = mVmConfig.getVoiceMailNumber(spn); mVoiceMailTag = mVmConfig.getVoiceMailTag(spn); } }
到这里我们发现,最终是通过当前SIM卡的MCC+MNC去配置文件中匹配相应的VoiceMail信息。
public void setVoiceMailNumber(String alphaTag, String voiceNumber, Message onComplete) { if (mIsVoiceMailFixed) { //从配置文件中读取的VoiceMail无法修改 AsyncResult.forMessage((onComplete)).exception = new IccVmFixedException("Voicemail number is fixed by operator"); onComplete.sendToTarget(); return; } mNewVoiceMailNum = voiceNumber; mNewVoiceMailTag = alphaTag; AdnRecord adn = new AdnRecord(mNewVoiceMailTag, mNewVoiceMailNum); if (mMailboxIndex != 0 && mMailboxIndex != 0xff) { //向EF_MBDN分区更新Voicemail new AdnRecordLoader(mFh).updateEF(adn, EF_MBDN, EF_EXT6, mMailboxIndex, null, obtainMessage(EVENT_SET_MBDN_DONE, onComplete)); } else if (isCphsMailboxEnabled()) { //向EF_MAILBOX_CPHS分区更新Voicemail new AdnRecordLoader(mFh).updateEF(adn, EF_MAILBOX_CPHS, EF_EXT1, 1, null, obtainMessage(EVENT_SET_CPHS_MAILBOX_DONE, onComplete)); } else { //异常处理 AsyncResult.forMessage((onComplete)).exception = new IccVmNotSupportedException("Update SIM voice mailbox error"); onComplete.sendToTarget(); } }更新完成之后就会在handleMessage()中更新mVoiceMailNum、mVoiceMailTag的值:
public void handleMessage(Message msg) { switch (msg.what) { case EVENT_SET_MBDN_DONE: //EF_MBDN分区的更新结果 isRecordLoadResponse = false; ar = (AsyncResult)msg.obj; if (ar.exception == null) { //重新设置mVoiceMailNum、mVoiceMailTag的值 mVoiceMailNum = mNewVoiceMailNum; mVoiceMailTag = mNewVoiceMailTag; } if (isCphsMailboxEnabled()) { adn = new AdnRecord(mVoiceMailTag, mVoiceMailNum); Message onCphsCompleted = (Message) ar.userObj; if (ar.exception == null && ar.userObj != null) { AsyncResult.forMessage(((Message) ar.userObj)).exception = null; //给申请者发送更改成功的回调信息 ((Message) ar.userObj).sendToTarget(); onCphsCompleted = null; } //更新AdnRecord new AdnRecordLoader(mFh). updateEF(adn, EF_MAILBOX_CPHS, EF_EXT1, 1, null, obtainMessage(EVENT_SET_CPHS_MAILBOX_DONE, onCphsCompleted)); } else { if (ar.userObj != null) { AsyncResult.forMessage(((Message) ar.userObj)).exception = ar.exception; //给申请者发送更改失败的回调信息 ((Message) ar.userObj).sendToTarget(); } } break; case EVENT_SET_CPHS_MAILBOX_DONE: //EF_MAILBOX_CPHS分区的更新结果 isRecordLoadResponse = false; ar = (AsyncResult)msg.obj; if(ar.exception == null) { //重新设置mVoiceMailNum、mVoiceMailTag的值 mVoiceMailNum = mNewVoiceMailNum; mVoiceMailTag = mNewVoiceMailTag; } else { if (DBG) log("Set CPHS MailBox with exception: " + ar.exception); } if (ar.userObj != null) { //给申请者发送更改的结果 AsyncResult.forMessage(((Message) ar.userObj)).exception = ar.exception; ((Message) ar.userObj).sendToTarget(); } break; } }由此,我们就完成了更改Voicemail信息的任务。