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
 }
 }
 ......
 }