Platform:Android-7.1.1_r22
1 什么是APN
APN的全称为Access Point Name,即“接入点名称”,它决定了手机可以访问哪些类型的外部网络。
例如:
APN类型 可接入的网络(用途)
default 数据网络(普通上网)
mms MMS(彩信)
APN配置中的字段
name:“APN配置”的名称,可随意指定,非APN名称。
apn:APN名称,由运营商指定,如cmnet、3gnet等
proxy:代理服务器IP地址
port:代理服务器端口
user:用户名
password:密码
server:
mmsc:彩信中心地址
mmsProxy:彩信代理服务器IP地址
mmsPort:彩信代理服务器端口
mcc:移动国家码
mnc:移动网络码
authType:认证类型,PAP、CHAP、PAP/CHAP
apnType:APN类型,如default、mms等
protocol:网络协议,IPV4、IPV6、IPV4/IPV6
roamingProtocol:漫游状态下的网络协议
carrierEnabled:
bearerMulti:
mvnoType:
mvnoMatchData:
2 APN配置的加载
不同运营商都定义了不同的APN,但我们使用手机时,一般是不需要手动进行APN相关设置的,这是因为手机厂已经在手机中预置了运营商的APN配置,而手机就会根据SIM卡中的mccmnc来自动选择合适的APN。
APN的配置保存在apns.xml和apns-conf.xml中,手机开机时,会将其中的内容全部加载到数据库telephony.db的carriers表中。
device/generic/goldfish/data/etc/apns-conf.xml
frameworks/base/core/res/res/xml/apns.xml AOSP中这个是为空的
<!-- If you edit this version, also edit the version in the partner-supplied
apns-conf.xml configuration file -->
<apns version="8">
</apns>
TelephonyProvider.java
private static class DatabaseHelper extends SQLiteOpenHelper {
......
public void onCreate(SQLiteDatabase db) {
if (DBG) log("dbh.onCreate:+ db=" + db);
createSimInfoTable(db);
createCarriersTable(db, CARRIERS_TABLE); // 在数据库telephony.db中建立表carriers
initDatabase(db); // 加载数据到表carriers
if (DBG) log("dbh.onCreate:- db=" + db);
}
......
}
程序首先会从apns.xml中读取APN配置;
然后再从apns-conf.xml中读取,而且会使用以下三个文件中最新的文件:
etc/apns-conf.xml
telephony/apns-conf.xml
misc/apns-conf.xml
private void initDatabase(SQLiteDatabase db) {
if (VDBG) log("dbh.initDatabase:+ db=" + db);
// Read internal APNS data
Resources r = mContext.getResources();
XmlResourceParser parser = r.getXml(com.android.internal.R.xml.apns); // 首先从apns.xml中读取APN配置,哪个路径下的?
int publicversion = -1;
try {
XmlUtils.beginDocument(parser, "apns");
publicversion = Integer.parseInt(parser.getAttributeValue(null, "version"));
loadApns(db, parser); // 把读出来的APN配置保存到数据库
} catch (Exception e) {
loge("Got exception while loading APN database." + e);
} finally {
parser.close();
}
// 从apns-conf.xml读取APN配置
// Read external APNS data (partner-provided)
XmlPullParser confparser = null;
File confFile = getApnConfFile(); // 获取最新的apns-conf.xml
FileReader confreader = null;
if (DBG) log("confFile = " + confFile);
try {
confreader = new FileReader(confFile);
confparser = Xml.newPullParser();
confparser.setInput(confreader);
XmlUtils.beginDocument(confparser, "apns");
// Sanity check. Force internal version and confidential versions to agree
int confversion = Integer.parseInt(confparser.getAttributeValue(null, "version"));
if (publicversion != confversion) {
log("initDatabase: throwing exception due to version mismatch");
throw new IllegalStateException("Internal APNS file version doesn't match "
+ confFile.getAbsolutePath());
}
loadApns(db, confparser); // 把读出来的APN配置保存到数据库
} catch (FileNotFoundException e) {
......
}
SIM卡加载后,会触发DcTracker.onRecordsLoadedOrSubIdChanged() → createAllApnList()。
private void createAllApnList() {
mMvnoMatched = false;
mAllApnSettings = new ArrayList<ApnSetting>();
IccRecords r = mIccRecords.get();
String operator = (r != null) ? r.getOperatorNumeric() : ""; // 获取mccmnc
if (operator != null) {
String selection = "numeric = '" + operator + "'";
String orderBy = "_id";
......
// 根据mccmnc从数据库读取apn配置
Cursor cursor = mPhone.getContext().getContentResolver().query(
Telephony.Carriers.CONTENT_URI, null, selection, null, orderBy);
if (cursor != null) {
if (cursor.getCount() > 0) {
// 从数据库读取的apn配置不为空,则保存到mAllApnSettings
mAllApnSettings = createApnList(cursor);
}
cursor.close();
}
}
addEmergencyApnSetting();
dedupeApnSettings();
if (mAllApnSettings.isEmpty()) {
......
} else {
mPreferredApn = getPreferredApn();
if (mPreferredApn != null && !mPreferredApn.numeric.equals(operator)) {
mPreferredApn = null;
setPreferredApn(-1);
}
if (DBG) log("createAllApnList: mPreferredApn=" + mPreferredApn);
}
if (DBG) log("createAllApnList: X mAllApnSettings=" + mAllApnSettings);
setDataProfilesAsNeeded();
}
3 PreferredApn
当APN配置加载到数据库后,Preferred APN仍然是没有的,进入APN设置菜单后我们可发现所有APN都未被选中。
而当建立数据连接时,程序会从数据库中选取所有可用的APN,待数据连接建立成功后,建立数据连接成功的APN将会被设置为Preferred APN。
当然,我们也可以进入APN设置菜单手动设置Preferred APN。
数据连接建立过程中会调用到DcTracker.buildWaitingApns(),当Preferred APN为空时,就会把所有可用的APN加入WaitingApn列表,然后再用这些APN去建立数据连接,一旦某个APN建立数据连接成功后,就会被设为Preferred APN。
private ArrayList<ApnSetting> buildWaitingApns(String requestedApnType, int radioTech) {
......
if (usePreferred) {
mPreferredApn = getPreferredApn(); // 获取Preferred APN,结果为null
}
......
if (usePreferred && mCanSetPreferApn && mPreferredApn != null &&
mPreferredApn.canHandleType(requestedApnType)) {
......
// mPreferredApn为null,不会走到这里面
}
if (mAllApnSettings != null) {
if (DBG) log("buildWaitingApns: mAllApnSettings=" + mAllApnSettings);
// 在mAllApnSettings中选取所有可用的APN
for (ApnSetting apn : mAllApnSettings) {
if (apn.canHandleType(requestedApnType)) {
if (ServiceState.bitmaskHasTech(apn.bearerBitmask, radioTech)) {
if (DBG) log("buildWaitingApns: adding apn=" + apn);
apnList.add(apn);
} else {
......
}
......
return apnList;
}
数据连接建立成功后会调用DcTracker.onDataSetupComplete(),进而把当前建立数据连接的APN设置为Preferred APN。
Preferred APN是保存在TelephonyProvider的SharedPreferences中的。
private void onDataSetupComplete(AsyncResult ar) {
......
if (ar.exception == null) {
DcAsyncChannel dcac = apnContext.getDcAc();
......
if (dcac == null) {
......
} else {
......
// everything is setup
if(TextUtils.equals(apnContext.getApnType(),PhoneConstants.APN_TYPE_DEFAULT)) {
......
if (mCanSetPreferApn && mPreferredApn == null) {
if (DBG) log("onDataSetupComplete: PREFERRED APN is null");
mPreferredApn = apn;
if (mPreferredApn != null) {
setPreferredApn(mPreferredApn.id); // 设置Preferred APN
}
}
......
}