区别于前面的紧急APN,这里的APN参数主要指常规的SIM卡APN参数,手机在上网时必须传递正确的APN参数给运营商才可以接入移动网络,而常规APN参数的创建是由监听器触发的。
前面在DcTracker初始化过程中注册了大量监听器,其中有两个监听器可以触发APN的创建过程:1、SIM载入完毕;2、APN改变。这两个事件所导致的APN创建流程也都是类似的,分别是:
【当SIM载入完毕时】,将会触发onRecordsLoaded():
1、创建一个APN的列表,其中包含:当前SIM对应的APN、紧急APN;
2、合并相同的APN;
3、寻找一个当前Prefer的APN参数;
前面介绍过,在TelephonyProvider初始化过程中从"etc/apns-conf.xml"配置文件中载入了预置的APN参数存入数据库,而现在需要根据当前SIM信息把匹配的APN读取出来。
而读取过程就是先获取当前SIM的PLMN,然后创建数据库查询条件"numeric = 当前SIM PLMN",然后通过createApnList()方法将数据库查到的信息创建为APN参数。
接下来又经历了一次添加紧急APN的过程,这个过程和前面初始化DcTracker时添加紧急APN过程完全一致。
然后就需要通过dedupeApnSettings()方法去掉APN列表中重复的APN参数:
再然后就需要从当前众多的APN参数中寻找一个当前合适的(prefer)APN参数,该APN要求其对应的PLMN属于当前的SIM。他的来源是跟随其他预置的APN一起被添加到数据库中的,其特别之处就在于多了“preferapn_no_update”的属性。他的作用就是作为备用APN来发起数据连接。
至此,所有APN准备工作就绪,接下来就是等待需要上网时,将当前APN激活,然后发起数据连接过程。
前面在DcTracker初始化过程中注册了大量监听器,其中有两个监听器可以触发APN的创建过程:1、SIM载入完毕;2、APN改变。这两个事件所导致的APN创建流程也都是类似的,分别是:
【当SIM载入完毕时】,将会触发onRecordsLoaded():
- private void onRecordsLoaded() {
- mAutoAttachOnCreationConfig = mPhone.getContext().getResources().getBoolean(com.android.internal.R.bool.config_auto_attach_data_on_creation);
- //创建APN参数
- createAllApnList();
- setInitialAttachApn();
- if (mPhone.mCi.getRadioState().isOn()) {
- notifyOffApnsOfAvailability(Phone.REASON_SIM_LOADED);
- }
- //尝试发起数据业务
- setupDataOnConnectableApns(Phone.REASON_SIM_LOADED);
- }
private void onRecordsLoaded() {
mAutoAttachOnCreationConfig = mPhone.getContext().getResources().getBoolean(com.android.internal.R.bool.config_auto_attach_data_on_creation);
//创建APN参数
createAllApnList();
setInitialAttachApn();
if (mPhone.mCi.getRadioState().isOn()) {
notifyOffApnsOfAvailability(Phone.REASON_SIM_LOADED);
}
//尝试发起数据业务
setupDataOnConnectableApns(Phone.REASON_SIM_LOADED);
}
【当APN改变时】,将会触发onApnChanged():
- private void onApnChanged() {
- DctConstants.State overallState = getOverallState();
- boolean isDisconnected = (overallState == DctConstants.State.IDLE || overallState == DctConstants.State.FAILED);
- if (mPhone instanceof GSMPhone) {
- ((GSMPhone)mPhone).updateCurrentCarrierInProvider();
- }
- //创建APN参数
- createAllApnList();
- setInitialAttachApn();
- //清除旧的连接
- cleanUpAllConnections(!isDisconnected, Phone.REASON_APN_CHANGED);
- //尝试发起数据业务
- setupDataOnConnectableApns(Phone.REASON_APN_CHANGED);
- }
private void onApnChanged() {
DctConstants.State overallState = getOverallState();
boolean isDisconnected = (overallState == DctConstants.State.IDLE || overallState == DctConstants.State.FAILED);
if (mPhone instanceof GSMPhone) {
((GSMPhone)mPhone).updateCurrentCarrierInProvider();
}
//创建APN参数
createAllApnList();
setInitialAttachApn();
//清除旧的连接
cleanUpAllConnections(!isDisconnected, Phone.REASON_APN_CHANGED);
//尝试发起数据业务
setupDataOnConnectableApns(Phone.REASON_APN_CHANGED);
}
从上面两个过程对比我们发现,他们都通过两个步骤进行APN的创建,分别是createAllApnList()和setInitialAttachApn(),他们的作用分别是创建APN和设置默认APN。
下面分别来介绍这个过程。
一、创建APN过程
- private void createAllApnList() {
- mAllApnSettings = new ArrayList<ApnSetting>();
- IccRecords r = mIccRecords.get();
- //获取该SIM的PLMN
- String operator = (r != null) ? r.getOperatorNumeric() : "";
- if (operator != null) {
- String selection = "numeric = '" + operator + "'";
- //查询当前SIM的APN数据库
- Cursor cursor = mPhone.getContext().getContentResolver().query(Telephony.Carriers.CONTENT_URI, null, selection, null, null);
- if (cursor != null) {
- if (cursor.getCount() > 0) {
- //根据APN参数创建APN列表
- mAllApnSettings = createApnList(cursor);
- }
- cursor.close();
- }
- }
- //添加紧急APN
- addEmergencyApnSetting();
- //合并类似的APN
- dedupeApnSettings();
- if (mAllApnSettings.isEmpty()) {
- mPreferredApn = null;
- } else {
- //寻找prefer APN
- mPreferredApn = getPreferredApn();
- if (mPreferredApn != null && !mPreferredApn.numeric.equals(operator)) {
- mPreferredApn = null;
- setPreferredApn(-1);
- }
- }
- setDataProfilesAsNeeded();
- }
private void createAllApnList() {
mAllApnSettings = new ArrayList<ApnSetting>();
IccRecords r = mIccRecords.get();
//获取该SIM的PLMN
String operator = (r != null) ? r.getOperatorNumeric() : "";
if (operator != null) {
String selection = "numeric = '" + operator + "'";
//查询当前SIM的APN数据库
Cursor cursor = mPhone.getContext().getContentResolver().query(Telephony.Carriers.CONTENT_URI, null, selection, null, null);
if (cursor != null) {
if (cursor.getCount() > 0) {
//根据APN参数创建APN列表
mAllApnSettings = createApnList(cursor);
}
cursor.close();
}
}
//添加紧急APN
addEmergencyApnSetting();
//合并类似的APN
dedupeApnSettings();
if (mAllApnSettings.isEmpty()) {
mPreferredApn = null;
} else {
//寻找prefer APN
mPreferredApn = getPreferredApn();
if (mPreferredApn != null && !mPreferredApn.numeric.equals(operator)) {
mPreferredApn = null;
setPreferredApn(-1);
}
}
setDataProfilesAsNeeded();
}
这个方法主要经历了三个步骤:
1、创建一个APN的列表,其中包含:当前SIM对应的APN、紧急APN;
2、合并相同的APN;
3、寻找一个当前Prefer的APN参数;
前面介绍过,在TelephonyProvider初始化过程中从"etc/apns-conf.xml"配置文件中载入了预置的APN参数存入数据库,而现在需要根据当前SIM信息把匹配的APN读取出来。
而读取过程就是先获取当前SIM的PLMN,然后创建数据库查询条件"numeric = 当前SIM PLMN",然后通过createApnList()方法将数据库查到的信息创建为APN参数。
接下来又经历了一次添加紧急APN的过程,这个过程和前面初始化DcTracker时添加紧急APN过程完全一致。
然后就需要通过dedupeApnSettings()方法去掉APN列表中重复的APN参数:
- private void dedupeApnSettings() {
- ArrayList<ApnSetting> resultApns = new ArrayList<ApnSetting>();
- int i = 0;
- while (i < mAllApnSettings.size() - 1) {
- ApnSetting first = mAllApnSettings.get(i);
- ApnSetting second = null;
- int j = i + 1;
- while (j < mAllApnSettings.size()) {
- second = mAllApnSettings.get(j);
- if (apnsSimilar(first, second)) {
- ApnSetting newApn = mergeApns(first, second);
- mAllApnSettings.set(i, newApn);
- first = newApn;
- mAllApnSettings.remove(j);
- } else {
- j++;
- }
- }
- i++;
- }
- }
private void dedupeApnSettings() {
ArrayList<ApnSetting> resultApns = new ArrayList<ApnSetting>();
int i = 0;
while (i < mAllApnSettings.size() - 1) {
ApnSetting first = mAllApnSettings.get(i);
ApnSetting second = null;
int j = i + 1;
while (j < mAllApnSettings.size()) {
second = mAllApnSettings.get(j);
if (apnsSimilar(first, second)) {
ApnSetting newApn = mergeApns(first, second);
mAllApnSettings.set(i, newApn);
first = newApn;
mAllApnSettings.remove(j);
} else {
j++;
}
}
i++;
}
}
这里就一个去重的算法问题,这个算法的原理就是,经过一个循环,可以找到某个参数所有相同的组合。
再然后就需要从当前众多的APN参数中寻找一个当前合适的(prefer)APN参数,该APN要求其对应的PLMN属于当前的SIM。他的来源是跟随其他预置的APN一起被添加到数据库中的,其特别之处就在于多了“preferapn_no_update”的属性。他的作用就是作为备用APN来发起数据连接。
- private ApnSetting getPreferredApn() {
- if (mAllApnSettings.isEmpty()) {
- return null;
- }
- Cursor cursor = mPhone.getContext().getContentResolver().query( PREFERAPN_NO_UPDATE_URI, new String[] { "_id", "name", "apn" },
- null, null, Telephony.Carriers.DEFAULT_SORT_ORDER);
- if (cursor != null) {
- mCanSetPreferApn = true;
- } else {
- mCanSetPreferApn = false;
- }
- if (mCanSetPreferApn && cursor.getCount() > 0) {
- int pos;
- cursor.moveToFirst();
- pos = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID));
- for(ApnSetting p : mAllApnSettings) {
- log("getPreferredApn: apnSetting=" + p);
- if (p.id == pos && p.canHandleType(mRequestedApnType)) {
- log("getPreferredApn: X found apnSetting" + p);
- cursor.close();
- return p;
- }
- }
- }
- if (cursor != null) {
- cursor.close();
- }
- return null;
- }
private ApnSetting getPreferredApn() {
if (mAllApnSettings.isEmpty()) {
return null;
}
Cursor cursor = mPhone.getContext().getContentResolver().query( PREFERAPN_NO_UPDATE_URI, new String[] { "_id", "name", "apn" },
null, null, Telephony.Carriers.DEFAULT_SORT_ORDER);
if (cursor != null) {
mCanSetPreferApn = true;
} else {
mCanSetPreferApn = false;
}
if (mCanSetPreferApn && cursor.getCount() > 0) {
int pos;
cursor.moveToFirst();
pos = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID));
for(ApnSetting p : mAllApnSettings) {
log("getPreferredApn: apnSetting=" + p);
if (p.id == pos && p.canHandleType(mRequestedApnType)) {
log("getPreferredApn: X found apnSetting" + p);
cursor.close();
return p;
}
}
}
if (cursor != null) {
cursor.close();
}
return null;
}
从其获取途径可以看到,他的URI("content://telephony/carriers/preferapn_no_update")中多了"preferapn_no_update"的参数,这也是该APN的特殊之处。
二、设置默认APN过程
- protected void setInitialAttachApn() {
- ApnSetting iaApnSetting = null;
- ApnSetting defaultApnSetting = null;
- ApnSetting firstApnSetting = null;
- if (mAllApnSettings != null && !mAllApnSettings.isEmpty()) {
- firstApnSetting = mAllApnSettings.get(0);
- for (ApnSetting apn : mAllApnSettings) {
- if (ArrayUtils.contains(apn.types, PhoneConstants.APN_TYPE_IA) && apn.carrierEnabled) {
- iaApnSetting = apn;
- break;
- } else if ((defaultApnSetting == null) && (apn.canHandleType(PhoneConstants.APN_TYPE_DEFAULT))) {
- //找到类型是APN_TYPE_DEFAULT的APN参数作为默认attach用
- log("setInitialApn: defaultApnSetting=" + apn);
- defaultApnSetting = apn;
- }
- }
- }
- ApnSetting initialAttachApnSetting = null;
- if (iaApnSetting != null) {
- initialAttachApnSetting = iaApnSetting;
- } else if (mPreferredApn != null) {
- initialAttachApnSetting = mPreferredApn;
- } else if (defaultApnSetting != null) {
- initialAttachApnSetting = defaultApnSetting;
- } else if (firstApnSetting != null) {
- initialAttachApnSetting = firstApnSetting;
- }
- if (initialAttachApnSetting == null) {
- } else {
- //设置Attach用的APN参数
- mPhone.mCi.setInitialAttachApn(initialAttachApnSetting.apn,
- initialAttachApnSetting.protocol, initialAttachApnSetting.authType,
- initialAttachApnSetting.user, initialAttachApnSetting.password, null);
- }
- }
protected void setInitialAttachApn() {
ApnSetting iaApnSetting = null;
ApnSetting defaultApnSetting = null;
ApnSetting firstApnSetting = null;
if (mAllApnSettings != null && !mAllApnSettings.isEmpty()) {
firstApnSetting = mAllApnSettings.get(0);
for (ApnSetting apn : mAllApnSettings) {
if (ArrayUtils.contains(apn.types, PhoneConstants.APN_TYPE_IA) && apn.carrierEnabled) {
iaApnSetting = apn;
break;
} else if ((defaultApnSetting == null) && (apn.canHandleType(PhoneConstants.APN_TYPE_DEFAULT))) {
//找到类型是APN_TYPE_DEFAULT的APN参数作为默认attach用
log("setInitialApn: defaultApnSetting=" + apn);
defaultApnSetting = apn;
}
}
}
ApnSetting initialAttachApnSetting = null;
if (iaApnSetting != null) {
initialAttachApnSetting = iaApnSetting;
} else if (mPreferredApn != null) {
initialAttachApnSetting = mPreferredApn;
} else if (defaultApnSetting != null) {
initialAttachApnSetting = defaultApnSetting;
} else if (firstApnSetting != null) {
initialAttachApnSetting = firstApnSetting;
}
if (initialAttachApnSetting == null) {
} else {
//设置Attach用的APN参数
mPhone.mCi.setInitialAttachApn(initialAttachApnSetting.apn,
initialAttachApnSetting.protocol, initialAttachApnSetting.authType,
initialAttachApnSetting.user, initialAttachApnSetting.password, null);
}
}
在上面这个过程中,遍历当前所有的APN列表,寻找类型是APN_TYPE_DEFAULT的APN,然后将该APN参数传递给Modem用于初始的Attach。
至此,所有APN准备工作就绪,接下来就是等待需要上网时,将当前APN激活,然后发起数据连接过程。