Phone进程中的DcTracker构造方法如下,
1,调用父类DcTrackerBase的构造方法,
super(p);
DcTrackerBase的构造方法主要逻辑如下,
监听SIM卡的变化,
mUiccController = UiccController.getInstance();
mUiccController.registerForIccChanged(this, DctConstants.EVENT_ICC_CHANGED, null);
也就是说,如果SIM卡有变化, UiccController会向DcTrackerBase发送EVENT_ICC_CHANGED消息,
DcTrackerBase的handleMessage方法对该消息处理如下,
case DctConstants.EVENT_ICC_CHANGED: {
onUpdateIcc();
break;
}
会回调DcTracker的onUpdateIcc方法。
2,注册telephony.db数据库的变化,
mApnObserver = new ApnChangeObserver();
p.getContext().getContentResolver().registerContentObserver(
Telephony.Carriers.CONTENT_URI, true, mApnObserver);
ApnChangeObserver是DcTracker的内部类,定义如下,
private class ApnChangeObserver extends ContentObserver {
public ApnChangeObserver () {
super(mDataConnectionTracker);
}
@Override
public void onChange(boolean selfChange) {
sendMessage(obtainMessage(DctConstants.EVENT_APN_CHANGED));
}
}
而DcTrackerBase继承Handler,如下,
public abstract class DcTrackerBase extends Handler {
因此,一旦telephony.db数据库发生变化,会调用ApnChangeObserver 的onChange方法,然后调用Handler
的sendMessage方法发送EVENT_APN_CHANGED消息。DcTracker的handleMessage方法对该消息处理如下,
case DctConstants.EVENT_APN_CHANGED:
onApnChanged();
break;
因此, 一旦telephony.db数据库发生变化,就会调用DcTracker的onApnChanged方法。
3, 调用initApnContexts方法初始化不同APN类型对应的网络,
initApnContexts();
4,其他
for (ApnContext apnContext : mApnContexts.values()) {
// Register the reconnect and restart actions.
IntentFilter filter = new IntentFilter();
filter.addAction(INTENT_RECONNECT_ALARM + '.' + apnContext.getApnType());
filter.addAction(INTENT_RESTART_TRYSETUP_ALARM + '.' + apnContext.getApnType());
mPhone.getContext().registerReceiver(mIntentReceiver, filter, null, mPhone);
}
// Add Emergency APN to APN setting list by default to support EPDN in sim absent cases
initEmergencyApnSetting();
addEmergencyApnSetting();
这个章节论述onUpdateIcc方法设置默认APN流程,下个章节分析onApnChanged方法流程。
DcTracker的onUpdateIcc方法主要逻辑如下,
调用IccRecords的registerForRecordsLoaded方法进行注册,监听卡信息是否载入完成
if (newIccRecords != null) {
if (mPhone.getSubId() >= 0) {
log("New records found.");
mIccRecords.set(newIccRecords);
newIccRecords.registerForRecordsLoaded(
this, DctConstants.EVENT_RECORDS_LOADED, null);
}
} else {
onSimNotReady();//SIM卡还未读出
}
如果SIM卡信息载入完成,则IccRecords会向DcTracker发送EVENT_RECORDS_LOADED,
DcTracker的handleMessage对该消息处理如下,
case DctConstants.EVENT_RECORDS_LOADED:
onRecordsLoaded();
break;
DcTracker的onRecordsLoaded方法主要逻辑如下,
1,调用createAllApnList方法创建当前SIM卡的APN,
createAllApnList();
2,调用setInitialAttachApn方法设置初始使用的APN,
setInitialAttachApn();
3,调用setupDataOnConnectableApns方法发起拨号请求,
setupDataOnConnectableApns(Phone.REASON_SIM_LOADED);
当然, setupDataOnConnectableApns 方法也是通过调用trySetupData方法进行拨号的。
如果当前SIM卡未打开数据业务,则不会拨号成功。下面三个小节分别论述这三个方法。
DcTracker的createAllApnList方法调用流程图如下,
createAllApnList方法主要逻辑如下,
1,在telephony.db数据库的carriers表单中查询当前SIM卡对应的APN信息,
并调用createApnList方法构造ApnSetting对象。如此,一般一个APN对应一个ApnSetting对象,
这样就完成了数据库中APN到ApnSetting对象的映射。
Cursor cursor = mPhone.getContext().getContentResolver().query(
Telephony.Carriers.CONTENT_URI, null, selection, null, orderBy);
if (cursor != null) {
if (cursor.getCount() > 0) {
mAllApnSettings = createApnList(cursor, mIccRecords.get());
}
cursor.close();
}
2,添加emergencyApnSettings并删除重复的APN,
addEmergencyApnSetting();
dedupeApnSettings();
3,调用getPreferredApn方法从telephony.db数据库的siminfo表中读出默认的APN,一般第一次为空,如果该APN和
当前卡的APN不匹配,则调用setPreferredApn方法删除telephony.db数据库的siminfo表中APN信息,
mPreferredApn = getPreferredApn(mAllApnSettings);
if (mPreferredApn != null && !mPreferredApn.numeric.equals(operator)) {
mPreferredApn = null;
setPreferredApn(-1);
}
setPreferredApn方法如下,
String subId = Long.toString(mPhone.getSubId());
Uri uri = Uri.withAppendedPath(PREFERAPN_NO_UPDATE_URI_USING_SUBID, subId);
log("setPreferredApn: delete");
ContentResolver resolver = mPhone.getContext().getContentResolver();
resolver.delete(uri, null, null); //首先调用delete方法删除
if (pos >= 0) { //当前pos为 -1,因此后面的insert方法不执行。
log("setPreferredApn: insert");
ContentValues values = new ContentValues();
values.put(APN_ID, pos);
resolver.insert(uri, values); //插入默认的APN信息
}
createAllApnList方法如下,
if (cursor.moveToFirst()) {
do {
ApnSetting apn = makeApnSetting(cursor);
•••
if (apn.hasMvnoParams()) {
if (r != null && ApnSetting.mvnoMatches(r, apn.mvnoType, apn.mvnoMatchData)) {
mvnoApns.add(apn);//虚拟运营商
}
} else {
mnoApns.add(apn); //运营商
}
} while (cursor.moveToNext());
利用cursor对象循环调用makeApnSetting方法构造ApnSetting对象,
makeApnSetting方法就是利用cursor在telephony.db数据库的carriers表单中获取SIM卡对应的APN信息,
然后利用APN信息构造一个ApnSetting对象, makeApnSetting方法的部分代码如下,
String[] types = parseTypes(
cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.TYPE)));
ApnSetting apn = new ApnSetting(
cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID)),
•••
ApnSetting 是一个比较轻量级的类,主要包含APN的信息,以及相关解析APN信息的方法。
parseTypes方法如下,
String[] result;
// If unset, set to DEFAULT.
if (types == null || types.equals("")) {
result = new String[1];
result[0] = PhoneConstants.APN_TYPE_ALL;
} else {
result = types.split(",");
}
return result;
这段代码是解析APN的type字段。APN的type域,决定了它提供的网络能力。
telephony.db数据库的carriers表单部分APN的type域如下,
APN type包含default时,利用这个APN建立的网络就具有Mobile能力,即能够用数据网络访问Internet;
当APN type包含mms时,利用这个APN建立的网络就具有发送彩信的能力。从parseTypes函数可以看出,
当APN的type为空时,即没有配置时,APN的type被定义为APN_TYPE_ALL。 利用APN_TYPE_ALL建立的网络,将具有全部的网络能力。
正常情况下,这种设计是合理的:
运营商会不同的服务定义不同的网络,于是通过APN的type域进行区分;
但是,有的运营商在某些地区会用同一个网络支持所有的功能(例如在非洲的一些国家),此时将APN的type域写成
”default, mms, supl, dun, hipri, fota, ims…….”是件繁琐的事,
于是,就规定APN的type域为”“时,可以支持所有网络能力。然而,这种设计成为了Android的一个漏洞,在某些场景下,
将带来数据连接无法断开的问题。
到此,已经在telephony.db数据库中查询到了SIM卡对应的APN信息,并且构造出了对应的ApnSetting对象。这些对象最终
保存在DcTrackerBase的mAllApnSettings变量中,定义如下,
protected ArrayList mAllApnSettings = null;
以后很多地方都会使用到mAllApnSettings变量,例如拨号上网。
并且默认APN信息保存在mPreferredApn变量中,定义如下,
protected ApnSetting mPreferredApn = null;