与IccFileHandler类似,UiccCardApplication也会根据当前SIM卡的类型创建不同的IccRecords对象,这个对象与IccFileHandler的区别在于,IccFileHandler是以SIM文件系统为操作对象,而IccRecords是以SIM存储内容为操作对象(IccFileHandler偏重底层实现,IccRecords偏重上层应用)。
下面是IccRecords不同的子类对象:
1、提供SIM卡常用信息的查询,包括IMSI、VoiceMail、ICCID、SIMRecords等信息;
1、创建Adn和VoiceMail缓存,这里的Adn缓存用于SIM卡联系人的增、删、改、查等功能;
首先普及一下语音信箱的使用方法: 语音信箱需要和呼叫转移相互搭配才能使用,通过呼叫转移来设置触发语音信箱的条件。比如可以设置“无应答转移”到语音信箱,这样一来,有来电时,如果长时间无应答,运营商就会将该来点转移到预先设置的语音信箱中,然后录取通话音频,并在用户拨打语音信箱时播放给用户。
而不同的运营商的语音信箱号码是不固定的,有的运营商的语音信箱号码是固定的,有的是可以让用户设置的,对于中国移动来说,某个地区的语音信箱是一样的。
针对以上情况,Google设计中,VoiceMail的来源可以有两个地方,一个是从SIM卡中读取,也就是运营商在SIM卡中预置。另一个是从内置的配置文件中读取,也就是针对一些常用的运营商,如果他们的语音信箱是固定的,那么就可以直接预置在代码中,此时 由于已知该运营商的语音信箱号码固定,所以用户是无法修改语音信箱号码的。
先来看以下这个类的构造方法流程:
然后我们来看什么情况下会用该缓存设置VoiceMail:
在fetchSimRecords()更新SIMRecords的过程中,需要向Modem请求多次SIM卡信息的请求,每发送一次都会记录下发送的数目:
下一章节将介绍 CatService相关知识。
下面是IccRecords不同的子类对象:
-
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来分析。
一、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() {}
-
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) {}
1、提供SIM卡常用信息的查询,包括IMSI、VoiceMail、ICCID、SIMRecords等信息;
2、注册常用信息的监听器,包括SIMRecords、IMSI、RecordEvents、NewSms、NetworkSelection等事件;
二、SIMRecords的创建过程
-
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);
-
}
1、创建Adn和VoiceMail缓存,这里的Adn缓存用于SIM卡联系人的增、删、改、查等功能;
2、监听UiccCardApplication的Ready状态;
三、SIMRecords的更新过程
-
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等信息。
四、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的存储位置信息:
-
java.
-
protected void fetchSimRecords () {
-
//更新VoiceMail
-
mFh.loadEFLinearFixed(EF_MBI, 1, obtainMessage(EVENT_GET_MBI_DONE));
-
mRecordsToLoad++;
-
}
-
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;
-
}
-
}
-
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信息。
4.2、从配置文件中读取VoiceMail信息
在SIMRecords的构造函数中初始化了一个特殊的对象:
-
public SIMRecords(UiccCardApplication app, Context c, CommandsInterface ci) {
-
super(app, c, ci);
-
mVmConfig = new 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 {
-
}
-
}
然后我们来看什么情况下会用该缓存设置VoiceMail:
在fetchSimRecords()更新SIMRecords的过程中,需要向Modem请求多次SIM卡信息的请求,每发送一次都会记录下发送的数目:
-
protected void fetchSimRecords() {
-
mCi.getIMSIForApp(mParentApp.getAid(), obtainMessage(EVENT_GET_IMSI_DONE));
-
//记录下发送请求的个数
-
mRecordsToLoad++;
-
}
-
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;
-
}
-
}
-
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信息。
4.3、设置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();
-
}
-
}
-
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;
-
}
-
}
下一章节将介绍 CatService相关知识。