Uicc之IccRecords

与IccFileHandler类似,UiccCardApplication也会根据当前SIM卡的类型创建不同的IccRecords对象,这个对象与IccFileHandler的区别在于,IccFileHandler是以SIM文件系统为操作对象,而IccRecords是以SIM存储内容为操作对象(IccFileHandler偏重底层实现,IccRecords偏重上层应用)
        下面是IccRecords不同的子类对象:
  1. @UiccCardApplication.java  
  2. private IccRecords createIccRecords(AppType type, Context c, CommandsInterface ci) {  
  3.     if (type == AppType.APPTYPE_USIM || type == AppType.APPTYPE_SIM) {  
  4.         return new SIMRecords(this, c, ci);  
  5.     } else if (type == AppType.APPTYPE_RUIM || type == AppType.APPTYPE_CSIM){  
  6.         return new RuimRecords(this, c, ci);  
  7.     } else if (type == AppType.APPTYPE_ISIM) {  
  8.         return new IsimUiccRecords(this, c, ci);  
  9.     } else {  
  10.         return null;  
  11.     }  
  12. }  

        我们仍然挑选典型的SIMRecords来分析。


一、SIMRecords的主要作用


        我们来看其提供的主要public方法:
  1. public String getIMSI() {}  
  2. public String getMsisdnNumber() {}  
  3. public String getGid1() {}  
  4. public UsimServiceTable getUsimServiceTable() {}  
  5. public void setMsisdnNumber(String alphaTag, String number, Message onComplete) {}  
  6. public String getMsisdnAlphaTag() {}  
  7. public String getVoiceMailNumber() {}  
  8. public void setVoiceMailNumber(String alphaTag, String voiceNumber, Message onComplete) {}  
  9. public String getVoiceMailAlphaTag(){}  
  10. public void setVoiceMessageWaiting(int line, int countWaiting) {}  
  11. public boolean getVoiceCallForwardingFlag() {}  
  12. public String getOperatorNumeric() {}  
  13. public int getDisplayRule(String plmn) {}  
  14. public boolean isCspPlmnEnabled() {}  
        同时来看一下其集成的父类IccRecords提供的public方法:
  1. public AdnRecordCache getAdnCache() {}  
  2. public String getIccId() {}  
  3. public void setImsi(String imsi) {}  
  4. public String getServiceProviderName() {}  
  5. public boolean getVoiceMessageWaiting() {}  
  6. public int getVoiceMessageCount() {}  
  7. public boolean getRecordsLoaded() {}  
  8. public void setVoiceCallForwardingFlag(int line, boolean enable, String number) {}  
  9. public boolean isProvisioned () {}  
  10. public IsimRecords getIsimRecords() {}  
  11. public void registerForRecordsLoaded(Handler h, int what, Object obj) {}  
  12. public void registerForImsiReady(Handler h, int what, Object obj) {}  
  13. public void registerForRecordsEvents(Handler h, int what, Object obj) {}  
  14. public void registerForNewSms(Handler h, int what, Object obj) {}  
  15. public void registerForNetworkSelectionModeAutomatic( Handler h, int what, Object obj) {}  
        从这些方法看出,IccRecords的主要功能分为两部分:
        1、提供SIM卡常用信息的查询,包括IMSI、VoiceMail、ICCID、SIMRecords等信息;

        2、注册常用信息的监听器,包括SIMRecords、IMSI、RecordEvents、NewSms、NetworkSelection等事件;


二、SIMRecords的创建过程


        从其继承关系可以看出,他的 本质也是Handler
  1. public class SIMRecords extends IccRecords {}  
  2. public abstract class IccRecords extends Handler implements IccConstants {}  
        然后看他的构造函数:
  1. public SIMRecords(UiccCardApplication app, Context c, CommandsInterface ci) {  
  2.     //在父类中对mCi、mFh等变量初始化  
  3.     super(app, c, ci);  
  4.   
  5.     //创建Adn缓存,用于操作SIM卡联系人  
  6.     mAdnCache = new AdnRecordCache(mFh);  
  7.   
  8.     //创建VoiceMail缓存  
  9.     mVmConfig = new VoiceMailConstants();  
  10.     mSpnOverride = new SpnOverride();  
  11.   
  12.     mRecordsRequested = false;  // No load request is made till SIM ready  
  13.     mRecordsToLoad = 0;  
  14.   
  15.     mCi.setOnSmsOnSim(this, EVENT_SMS_ON_SIM, null);  
  16.     mCi.registerForIccRefresh(this, EVENT_SIM_REFRESH, null);  
  17.   
  18.     //初始化成员变量  
  19.     resetRecords();  
  20.     //监听UiccCardApplication的Ready状态  
  21.     mParentApp.registerForReady(this, EVENT_APP_READY, null);  
  22. }  
        以上的创建过程完成了两个重要任务:
        1、创建Adn和VoiceMail缓存,这里的Adn缓存用于SIM卡联系人的增、删、改、查等功能;

        2、监听UiccCardApplication的Ready状态;


三、SIMRecords的更新过程


        当监听的UiccCardApplication发送Ready状态之后,SIMRecords就会更新自己所拥有的Adn和VoiceMail缓存:
  1. protected void fetchSimRecords() {  
  2.     mRecordsRequested = true;  
  3.   
  4.     //获取SIM卡的IMSI  
  5.     mCi.getIMSIForApp(mParentApp.getAid(), obtainMessage(EVENT_GET_IMSI_DONE));  
  6.     mRecordsToLoad++;  
  7.   
  8.     //得到SIM卡的ICCID  
  9.     mFh.loadEFTransparent(EF_ICCID, obtainMessage(EVENT_GET_ICCID_DONE));  
  10.     mRecordsToLoad++;  
  11.   
  12.     new AdnRecordLoader(mFh).loadFromEF(EF_MSISDN, EF_EXT1, 1, obtainMessage(EVENT_GET_MSISDN_DONE));  
  13.     mRecordsToLoad++;  
  14.   
  15.     //更新VoiceMail  
  16.     mFh.loadEFLinearFixed(EF_MBI, 1, obtainMessage(EVENT_GET_MBI_DONE));  
  17.     mRecordsToLoad++;  
  18.   
  19.     mFh.loadEFTransparent(EF_AD, obtainMessage(EVENT_GET_AD_DONE));  
  20.     mRecordsToLoad++;  
  21.   
  22.     // Record number is subscriber profile  
  23.     mFh.loadEFLinearFixed(EF_MWIS, 1, obtainMessage(EVENT_GET_MWIS_DONE));  
  24.     mRecordsToLoad++;  
  25.   
  26.     mFh.loadEFTransparent( EF_VOICE_MAIL_INDICATOR_CPHS, obtainMessage(EVENT_GET_VOICE_MAIL_INDICATOR_CPHS_DONE));  
  27.     mRecordsToLoad++;  
  28.     mFh.loadEFLinearFixed(EF_CFIS, 1, obtainMessage(EVENT_GET_CFIS_DONE));  
  29.     mRecordsToLoad++;  
  30.     mFh.loadEFTransparent(EF_CFF_CPHS, obtainMessage(EVENT_GET_CFF_DONE));  
  31.     mRecordsToLoad++;  
  32.     getSpnFsm(truenull);  
  33.     mFh.loadEFTransparent(EF_SPDI, obtainMessage(EVENT_GET_SPDI_DONE));  
  34.     mRecordsToLoad++;  
  35.     mFh.loadEFLinearFixed(EF_PNN, 1, obtainMessage(EVENT_GET_PNN_DONE));  
  36.     mRecordsToLoad++;  
  37.     mFh.loadEFTransparent(EF_SST, obtainMessage(EVENT_GET_SST_DONE));  
  38.     mRecordsToLoad++;  
  39.     mFh.loadEFTransparent(EF_INFO_CPHS, obtainMessage(EVENT_GET_INFO_CPHS_DONE));  
  40.     mRecordsToLoad++;  
  41.     mFh.loadEFTransparent(EF_CSP_CPHS,obtainMessage(EVENT_GET_CSP_CPHS_DONE));  
  42.     mRecordsToLoad++;  
  43.     mFh.loadEFTransparent(EF_GID1, obtainMessage(EVENT_GET_GID1_DONE));  
  44.     mRecordsToLoad++;  
  45. }  

        从上面可以看出,SIMRecords的更新过程就是用IccFileHandler将常用的SIM卡信息,读取并保存,其中就包括IMSI和ICCID等信息。


四、VoiceMail的设置与读取


        由于VoiceMail的号码和名称比较常用,因此我们专门来看以下VoiceMail的读取和更新机制。
        首先普及一下语音信箱的使用方法: 语音信箱需要和呼叫转移相互搭配才能使用,通过呼叫转移来设置触发语音信箱的条件。比如可以设置“无应答转移”到语音信箱,这样一来,有来电时,如果长时间无应答,运营商就会将该来点转移到预先设置的语音信箱中,然后录取通话音频,并在用户拨打语音信箱时播放给用户。
        而不同的运营商的语音信箱号码是不固定的,有的运营商的语音信箱号码是固定的,有的是可以让用户设置的,对于中国移动来说,某个地区的语音信箱是一样的。
        针对以上情况,Google设计中,VoiceMail的来源可以有两个地方,一个是从SIM卡中读取,也就是运营商在SIM卡中预置。另一个是从内置的配置文件中读取,也就是针对一些常用的运营商,如果他们的语音信箱是固定的,那么就可以直接预置在代码中,此时 由于已知该运营商的语音信箱号码固定,所以用户是无法修改语音信箱号码的

        对于第二种途径可以进行更丰富的客制化操作。下面我们分别来看这两种途径。


4.1、从SIM卡中读取VoiceMail信息

        

        SIM卡中有两个地方可以存储VoiceMail信息(EF_MBDN、EF_MAILBOX_CPHS),我们可以通过读取EF_MBI分区来获取VoiceMail的存储位置。

        前面分析过,SIMRecords会在接收到UiccCardApplication的Ready通知后更新SIMRecords信息,其中就包括向Modem请求SIM中的VoiceMail的存储位置信息:
  1. @SIMRecords.java  
  2. protected void fetchSimRecords() {  
  3.     //更新VoiceMail  
  4.     mFh.loadEFLinearFixed(EF_MBI, 1, obtainMessage(EVENT_GET_MBI_DONE));  
  5.     mRecordsToLoad++;  
  6. }  
        当接收到EF_MBI中的信息后,由handleMessage()负责解析:
  1. public void handleMessage(Message msg) {  
  2.     switch (msg.what) {  
  3.         case EVENT_GET_MBI_DONE:  
  4.             boolean isValidMbdn;  
  5.             isRecordLoadResponse = true;  
  6.             ar = (AsyncResult)msg.obj;  
  7.             data = (byte[]) ar.result;  
  8.             isValidMbdn = false;  
  9.             if (ar.exception == null) {  
  10.                 mMailboxIndex = data[0] & 0xff;  
  11.                 if (mMailboxIndex != 0 && mMailboxIndex != 0xff) {  
  12.                     //得到VoiceMail的存储位置  
  13.                     isValidMbdn = true;  
  14.                 }  
  15.             }  
  16.             mRecordsToLoad += 1;  
  17.   
  18.             if (isValidMbdn) {  
  19.                 // Note: MBDN was not included in NUM_OF_SIM_RECORDS_LOADED  
  20.                 //读取EF_MBDN中的VoiceMail  
  21.                 new AdnRecordLoader(mFh).loadFromEF(EF_MBDN, EF_EXT6, mMailboxIndex, obtainMessage(EVENT_GET_MBDN_DONE));  
  22.             } else {  
  23.                 //读取EF_MAILBOX_CPHS中的VoiceMail  
  24.                 new AdnRecordLoader(mFh).loadFromEF(EF_MAILBOX_CPHS, EF_EXT1, 1, obtainMessage(EVENT_GET_CPHS_MAILBOX_DONE));  
  25.             }  
  26.             break;  
  27.     }  
  28. }  
        我们看到,经过对返回值的解析,得到了VoiceMail的存储位置,接着就向Modem申请该位置中的VoiceMail信息,当接收到Modem的反馈后,会再次进入handleMessage()中解析:
  1. public void handleMessage(Message msg) {  
  2.     switch (msg.what) {  
  3.         case EVENT_GET_CPHS_MAILBOX_DONE:  
  4.         case EVENT_GET_MBDN_DONE:  
  5.             mVoiceMailNum = null;  
  6.             mVoiceMailTag = null;  
  7.             isRecordLoadResponse = true;  
  8.             ar = (AsyncResult)msg.obj;  
  9.             adn = (AdnRecord)ar.result;  
  10.   
  11.             if (adn.isEmpty() && msg.what == EVENT_GET_MBDN_DONE) {  
  12.                 mRecordsToLoad += 1;  
  13.                 //如果当前的EF_MBDN分区读取失败,再次尝试使用EF_MAILBOX_CPHS分区读取VoiceMail信息  
  14.                 new AdnRecordLoader(mFh).loadFromEF( EF_MAILBOX_CPHS, EF_EXT1, 1, obtainMessage(EVENT_GET_CPHS_MAILBOX_DONE));  
  15.                 break;  
  16.             }  
  17.             //得到VoiceMail的Number和Tag  
  18.             mVoiceMailNum = adn.getNumber();  
  19.             mVoiceMailTag = adn.getAlphaTag();  
  20.             break;  
  21.     }  
  22. }  

        由此,我们就从SIM卡中读取到了VoiceMail信息。


4.2、从配置文件中读取VoiceMail信息

        

        在SIMRecords的构造函数中初始化了一个特殊的对象:

  1. public SIMRecords(UiccCardApplication app, Context c, CommandsInterface ci) {  
  2.     super(app, c, ci);  
  3.     mVmConfig = new VoiceMailConstants();  
  4. }  
        这里出现的VoiceMailConstants就是专门为VoiceMail的配置文件而创建的,他的主要作用就是, 读取并解析系统配置文件,并根据当前的MCC/MNC获取相应的VoiceMail
        先来看以下这个类的构造方法流程:
  1. @VoiceMailConstants.java  
  2. VoiceMailConstants () {  
  3.     CarrierVmMap = new HashMap<String, String[]>();  
  4.     loadVoiceMail();  
  5. }  
  6. private void loadVoiceMail() {  
  7.     FileReader vmReader;  
  8.     //获取配置文件,路径:"etc/voicemail-conf.xml"  
  9.     final File vmFile = new File(Environment.getRootDirectory(), PARTNER_VOICEMAIL_PATH);  
  10.     try {  
  11.         vmReader = new FileReader(vmFile);  
  12.     } catch (FileNotFoundException e) {  
  13.     }  
  14.   
  15.     try {  
  16.         XmlPullParser parser = Xml.newPullParser();  
  17.         parser.setInput(vmReader);  
  18.         XmlUtils.beginDocument(parser, "voicemail");  
  19.         while (true) {  
  20.             //解析XML文件  
  21.             XmlUtils.nextElement(parser);  
  22.   
  23.             String name = parser.getName();  
  24.             if (!"voicemail".equals(name)) {  
  25.                 break;  
  26.             }  
  27.   
  28.             String[] data = new String[SIZE];  
  29.             //获取预置的VoiceMail信息  
  30.             String numeric = parser.getAttributeValue(null"numeric");  
  31.             data[NAME]     = parser.getAttributeValue(null"carrier");  
  32.             data[NUMBER]   = parser.getAttributeValue(null"vmnumber");  
  33.             data[TAG]      = parser.getAttributeValue(null"vmtag");  
  34.   
  35.             //保存在CarrierVmMap的缓存中  
  36.             CarrierVmMap.put(numeric, data);  
  37.         }  
  38.     } catch (XmlPullParserException e) {  
  39.     } catch (IOException e) {  
  40.     } finally {  
  41.     }  
  42. }  
        在VoiceMailConstants的创建过程中,解析系统"etc/voicemail-conf.xml"文件,并把里面的每一项(包含numeric、carrier、vmnumber、vmtag信息)都保存在CarrierVmMap缓存中,以便查询。
        然后我们来看什么情况下会用该缓存设置VoiceMail:
        在fetchSimRecords()更新SIMRecords的过程中,需要向Modem请求多次SIM卡信息的请求,每发送一次都会记录下发送的数目:
  1. protected void fetchSimRecords() {  
  2.     mCi.getIMSIForApp(mParentApp.getAid(), obtainMessage(EVENT_GET_IMSI_DONE));  
  3.     //记录下发送请求的个数  
  4.     mRecordsToLoad++;  
  5. }  
        然后在handleMessage()的finally中,会将当前已经处理过的Event数目减掉:
  1. public void handleMessage(Message msg) {  
  2.     try {   
  3.         switch (msg.what) {  
  4.         }  
  5.     }catch (RuntimeException exc) {  
  6.     }finally {  
  7.         //每个曾加+1的Event接收到回应后都会是isRecordLoadResponse==true  
  8.         if (isRecordLoadResponse) {  
  9.             onRecordLoaded();  
  10.         }  
  11.     }  
  12. }  
  13. protected void onRecordLoaded() {  
  14.     //当该Event处理完毕后,要将mRecordsToLoad数目-1  
  15.     mRecordsToLoad -= 1;  
  16.     if (mRecordsToLoad == 0 && mRecordsRequested == true) {  
  17.         //表明所有Event处理完毕  
  18.         onAllRecordsLoaded();  
  19.     } else if (mRecordsToLoad < 0) {  
  20.         mRecordsToLoad = 0;  
  21.     }  
  22. }  
        也就是说,当请求的所有Event都处理完毕后,就会进入onAllRecordsLoaded()中继续处理:
  1. protected void onAllRecordsLoaded() {  
  2.     //得到当前的MCC+MNC  
  3.     String operator = getOperatorNumeric();  
  4.     //根据的当前的MCC+MNC去配置文件中查找相应的VoiceMail  
  5.     setVoiceMailByCountry(operator);  
  6. }  
        继续看:
  1. private void setVoiceMailByCountry (String spn) {  
  2.     if (mVmConfig.containsCarrier(spn)) {  
  3.         mIsVoiceMailFixed = true;  
  4.         //读取XML中的VoiceMail信息  
  5.         mVoiceMailNum = mVmConfig.getVoiceMailNumber(spn);  
  6.         mVoiceMailTag = mVmConfig.getVoiceMailTag(spn);  
  7.     }  
  8. }  

        到这里我们发现,最终是通过当前SIM卡的MCC+MNC去配置文件中匹配相应的VoiceMail信息。


4.3、设置VoiceMail信息


        用户可以在手机的通话设置中更改当前的VoiceMail信息,当用户修改完成点击确认之后,就会调用到setVoiceMailNumber()方法,向Modem发送请求,这里需要注意,如果当前的VoiceMail信息是从配置文件中读取时,用户是无法修改VoiceMail的(这种情况说明,该运营商的语音信箱号码是固定的,无需修改)。而且设置VoiceMail信息时需要区分当前VoiceMail所在的分区信息。
  1. public void setVoiceMailNumber(String alphaTag, String voiceNumber, Message onComplete) {  
  2.     if (mIsVoiceMailFixed) {  
  3.         //从配置文件中读取的VoiceMail无法修改  
  4.         AsyncResult.forMessage((onComplete)).exception = new IccVmFixedException("Voicemail number is fixed by operator");  
  5.         onComplete.sendToTarget();  
  6.         return;  
  7.     }  
  8.   
  9.     mNewVoiceMailNum = voiceNumber;  
  10.     mNewVoiceMailTag = alphaTag;  
  11.     AdnRecord adn = new AdnRecord(mNewVoiceMailTag, mNewVoiceMailNum);  
  12.   
  13.     if (mMailboxIndex != 0 && mMailboxIndex != 0xff) {  
  14.         //向EF_MBDN分区更新Voicemail  
  15.         new AdnRecordLoader(mFh).updateEF(adn, EF_MBDN, EF_EXT6, mMailboxIndex, null, obtainMessage(EVENT_SET_MBDN_DONE, onComplete));  
  16.   
  17.     } else if (isCphsMailboxEnabled()) {  
  18.         //向EF_MAILBOX_CPHS分区更新Voicemail  
  19.         new AdnRecordLoader(mFh).updateEF(adn, EF_MAILBOX_CPHS, EF_EXT1, 1null, obtainMessage(EVENT_SET_CPHS_MAILBOX_DONE, onComplete));  
  20.   
  21.     } else {  
  22.         //异常处理  
  23.         AsyncResult.forMessage((onComplete)).exception = new IccVmNotSupportedException("Update SIM voice mailbox error");  
  24.         onComplete.sendToTarget();  
  25.     }  
  26. }  
        更新完成之后就会在handleMessage()中更新mVoiceMailNum、mVoiceMailTag的值:
  1. public void handleMessage(Message msg) {  
  2.     switch (msg.what) {  
  3.         case EVENT_SET_MBDN_DONE:  
  4.             //EF_MBDN分区的更新结果  
  5.             isRecordLoadResponse = false;  
  6.             ar = (AsyncResult)msg.obj;  
  7.   
  8.             if (ar.exception == null) {  
  9.                 //重新设置mVoiceMailNum、mVoiceMailTag的值  
  10.                 mVoiceMailNum = mNewVoiceMailNum;  
  11.                 mVoiceMailTag = mNewVoiceMailTag;  
  12.             }  
  13.   
  14.             if (isCphsMailboxEnabled()) {  
  15.                 adn = new AdnRecord(mVoiceMailTag, mVoiceMailNum);  
  16.                 Message onCphsCompleted = (Message) ar.userObj;  
  17.   
  18.                 if (ar.exception == null && ar.userObj != null) {  
  19.                     AsyncResult.forMessage(((Message) ar.userObj)).exception = null;  
  20.                     //给申请者发送更改成功的回调信息  
  21.                     ((Message) ar.userObj).sendToTarget();  
  22.                     onCphsCompleted = null;  
  23.                 }  
  24.   
  25.                 //更新AdnRecord  
  26.                 new AdnRecordLoader(mFh).  updateEF(adn, EF_MAILBOX_CPHS, EF_EXT1, 1null,  
  27.                             obtainMessage(EVENT_SET_CPHS_MAILBOX_DONE,  
  28.                                 onCphsCompleted));  
  29.             } else {  
  30.                 if (ar.userObj != null) {  
  31.                     AsyncResult.forMessage(((Message) ar.userObj)).exception = ar.exception;  
  32.                     //给申请者发送更改失败的回调信息  
  33.                     ((Message) ar.userObj).sendToTarget();  
  34.                 }  
  35.             }  
  36.             break;  
  37.         case EVENT_SET_CPHS_MAILBOX_DONE:  
  38.             //EF_MAILBOX_CPHS分区的更新结果  
  39.             isRecordLoadResponse = false;  
  40.             ar = (AsyncResult)msg.obj;  
  41.             if(ar.exception == null) {  
  42.                 //重新设置mVoiceMailNum、mVoiceMailTag的值  
  43.                 mVoiceMailNum = mNewVoiceMailNum;  
  44.                 mVoiceMailTag = mNewVoiceMailTag;  
  45.             } else {  
  46.                 if (DBG) log("Set CPHS MailBox with exception: " + ar.exception);  
  47.             }  
  48.             if (ar.userObj != null) {  
  49.                 //给申请者发送更改的结果  
  50.                 AsyncResult.forMessage(((Message) ar.userObj)).exception = ar.exception;  
  51.                 ((Message) ar.userObj).sendToTarget();  
  52.             }  
  53.             break;  
  54.     }  
  55. }  
        由此,我们就完成了更改Voicemail信息的任务。
        下一章节将介绍 CatService相关知识。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值