本篇仅以USIM卡为例进行代码分析
从之前对于UiccController.java文件进行分析中,我们知道UiccController中创建了UiccCard,而在UiccCard中创建了一个UiccCardAppliaction对象,而在UiccCardApplication的构造方法中,调用了如下方法:
UiccCardApplication(UiccCard uiccCard,
IccCardApplicationStatus as,
Context c,
CommandsInterface ci) {
......
mIccFh = createIccFileHandler(as.app_type);
mIccRecords = createIccRecords(as.app_type, mContext, mCi);
......
}
通过createIccFileHandler和createIccRecords方法,创建了IccFileHandler和IccRecords对象
private IccFileHandler createIccFileHandler(AppType type) {
switch (type) {
case APPTYPE_SIM:
return new SIMFileHandler(this, mAid, mCi);
case APPTYPE_RUIM:
return new RuimFileHandler(this, mAid, mCi);
case APPTYPE_USIM:
return new UsimFileHandler(this, mAid, mCi);
case APPTYPE_CSIM:
return new CsimFileHandler(this, mAid, mCi);
case APPTYPE_ISIM:
return new IsimFileHandler(this, mAid, mCi);
default:
return null;
}
}
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 {
// Unknown app type (maybe detection is still in progress)
return null;
}
}
由于是USIM卡,因此创建的是SIMRecords和UsimFileHandler对象,接下来我们进入SIMRecords.java的构造方法,传入的参数分别为当前的UiccCardApplication对象,上下文对象,和RIL对象(从之前的分析中我们知道)
public SIMRecords(UiccCardApplication app, Context c, CommandsInterface ci) {
// 调用父类IccRecords的构造方法
super(app, c, ci);
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();
mParentApp.registerForReady(this, EVENT_APP_READY, null);
mParentApp.registerForLocked(this, EVENT_APP_LOCKED, null);
if (DBG) log("SIMRecords X ctor this=" + this);
}
首先其调用的是父类IccRecords的构造方法
public IccRecords(UiccCardApplication app, Context c, CommandsInterface ci) {
mContext = c;
mCi = ci;
// 这个就是刚刚我们在UiccCardApplication中通过createIccFileHandler创建的UsimFileHandler对象
mFh = app.getIccFileHandler();
mParentApp = app;
mTelephonyManager = (TelephonyManager) mContext.getSystemService(
Context.TELEPHONY_SERVICE);
}
可以看到,从SIMRecords的构造方法中一共做了如下几件事:
1. 首先初始化了一些对象,将上下文对象、RIL对象、UsimFileHandler对象,UiccCardApplication对象、SpnOverride对象等,并将其关联在一起
2. 为RIL对象注册了几个消息,EVENT_SMS_ON_SIM,EVENT_SIM_REFRESH消息,并在SIMRecords中处理这些消息
3. 为UiccCardApplication注册EVENT_APP_READY,EVENT_APP_LOCKED消息,并在SIMRecords中处理这些消息
关于registerForReady和registerForLocked方法,暂不做分析,确认结果是,当SIM卡准备好后,会向上发送EVENT_APP_READY消息,并在SIMRecords中进行处理(不考虑SIMLOCK情况)
查看SIMRecords接收到EVENT_APP_READY方法后的处理
@Override
public void handleMessage(Message msg) {
...
try { switch (msg.what) {
case EVENT_APP_READY:
onReady();
break;
}
......
}
@Override
public void onReady() {
fetchSimRecords();
}
protected void fetchSimRecords() {
......
getSpnFsm(true, null);
......
}
private void getSpnFsm(boolean start, AsyncResult ar) {
byte[] data;
if (start) {
// Check previous state to see if there is outstanding
// SPN read
if(mSpnState == GetSpnFsmState.READ_SPN_3GPP ||
mSpnState == GetSpnFsmState.READ_SPN_CPHS ||
mSpnState == GetSpnFsmState.READ_SPN_SHORT_CPHS ||
mSpnState == GetSpnFsmState.INIT) {
// Set INIT then return so the INIT code
// will run when the outstanding read done.
mSpnState = GetSpnFsmState.INIT;
return;
} else {
mSpnState = GetSpnFsmState.INIT;
}
}
switch(mSpnState){
case INIT:
setServiceProviderName(null);
mFh.loadEFTransparent(EF_SPN,
obtainMessage(EVENT_GET_SPN_DONE));
mRecordsToLoad++;
mSpnState = GetSpnFsmState.READ_SPN_3GPP;
break;
case READ_SPN_3GPP:
if (ar != null && ar.exception == null) {
data = (byte[]) ar.result;
mSpnDisplayCondition = 0xff & data[0];
setServiceProviderName(IccUtils.adnStringFieldToString(
data, 1, data.length - 1));
if (DBG) log("Load EF_SPN: " + getServiceProviderName()
+ " spnDisplayCondition: " + mSpnDisplayCondition);
mTelephonyManager.setSimOperatorNameForPhone(
mParentApp.getPhoneId(), getServiceProviderName());
mSpnState = GetSpnFsmState.IDLE;
} else {
mFh.loadEFTransparent( EF_SPN_CPHS,
obtainMessage(EVENT_GET_SPN_DONE));
mRecordsToLoad++;
mSpnState = GetSpnFsmState.READ_SPN_CPHS;
// See TS 51.011 10.3.11. Basically, default to
// show PLMN always, and SPN also if roaming.
mSpnDisplayCondition = -1;
}
break;
case READ_SPN_CPHS:
if (ar != null && ar.exception == null) {
data = (byte[]) ar.result;
setServiceProviderName(IccUtils.adnStringFieldToString(data, 0, data.length));
if (DBG) log("Load EF_SPN_CPHS: " + getServiceProviderName());
mTelephonyManager.setSimOperatorNameForPhone(
mParentApp.getPhoneId(), getServiceProviderName());
mSpnState = GetSpnFsmState.IDLE;
} else {
mFh.loadEFTransparent(
EF_SPN_SHORT_CPHS, obtainMessage(EVENT_GET_SPN_DONE));
mRecordsToLoad++;
mSpnState = GetSpnFsmState.READ_SPN_SHORT_CPHS;
}
break;
case READ_SPN_SHORT_CPHS:
if (ar != null && ar.exception == null) {
data = (byte[]) ar.result;
setServiceProviderName(IccUtils.adnStringFieldToString(data, 0, data.length));
if (DBG) log("Load EF_SPN_SHORT_CPHS: " + getServiceProviderName());
mTelephonyManager.setSimOperatorNameForPhone(
mParentApp.getPhoneId(), getServiceProviderName());
}else {
if (DBG) log("No SPN loaded in either CHPS or 3GPP");
}
mSpnState = GetSpnFsmState.IDLE;
break;
default:
mSpnState = GetSpnFsmState.IDLE;
}
}
主要是
1. 在接收到EVENT_APP_READY消息后,调用了onReady方法,在onReady方法中调用了fetchSimRecords(从名字上来看,就知道是获取SIMRecords的数据),然后在fetchSimRecords方法中调用了getSpnFsm方法,并且开始传入的参数为true和null
2. 第一次调用getSpnFsm方法后,由于mSpnState的值为GetSpnFsmState.IDLE,因此首次会将mSpnState的值设置为GetSpnFsmState.INIT,然后进入INIT对应的分支,设置ServiceProviderName为null,后调用UsimFileHandler对象的loadEFTransparent方法,并且将mSpnState设置为GetSpnFsmState.READ_SPN_3GPP
3. UsimFileHandler对象的loadEFTransparent方法主要是通过RIL对象的iccIOForApp方法,最终会返回给SIMRecords,发送EVENT_GET_SPN_DONE消息给SIMRecords对象处理,处理时,第二次调用SIMRecords对象的getSpnFsm方法的,只是传入的第一个参数变为false,而且由于mSpnState之前被设置为GetSpnFsmState.READ_SPN_3GPP,所以会进入getSpnFsm方法的GetSpnFsmState.READ_SPN_3GPP分支
4. 进入GetSpnFsmState.READ_SPN_3GPP分支后,根据RIL层传过来的数据,设置ServiceProviderName,若传上来的数据有异常,则继续将mSpnState的值设置为GetSpnFsmState.READ_SPN_CPHS,继续第3步,若无异常,则将mSpnState设置为IDLE,然后退出
5. 依照4的步骤,getSpnFsm完成ServiceProviderName的设置
最后,返回SIMRecords.java的handleMessage方法,我们看到在default分支中调用了一次onRecordLoaded
@Override
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;
}
}
当所有的数据加载完成后,会调用onAllRecordsLoaded方法
@Override
protected void onAllRecordsLoaded() {
......
setSpnFromConfig(operator);
......
}
调用了setSpnFromConfig方法
private void setSpnFromConfig(String carrier) {
if (mSpnOverride.containsCarrier(carrier)) {
setServiceProviderName(mSpnOverride.getSpn(carrier));
mTelephonyManager.setSimOperatorNameForPhone(
mParentApp.getPhoneId(), getServiceProviderName());
}
}
我们通过分析SpnOverride对象,知道,这个类主要是解析项目/system/etc/spn-conf.xml文件,得到默认的SPN名称
因此setSpnFromConfig方法主要是通过确认spn-conf.xml中有没有设置自定义的SPN名称,如果有,则将ServiceProviderName设置为我们自定义的SPN
至此,SPN名称加载完毕