APN概念
APN(Access Point Name),即“接入點名稱”,用來標識GPRS的業務種類,目前分為兩大類:CMWAP(通過GPRS訪問WAP業務)、CMNET(除了WAP以外的服務目前都用CMNET,比如連線因特網等)。是通過手機上網時必須配置的一個引數,它決定了您的手機通過哪種接入方式來訪問網路。業務流程GPRS專網系統終端上網登入伺服器平臺的流程為:
1)使用者發出GPRS登入請求,請求中包括由移動公司為GPRS專網系統專門分配的專網APN;
2)根據請求中的APN,SGSN向DNS伺服器發出查詢請求,找到與企業伺服器平臺連線的GGSN,並將使用者請求通過GTP隧道封裝送給GGSN;
3)GGSN將使用者認證資訊(包括手機號碼、使用者賬號、密碼等)通過專線送至Radius進行認證;
4)Radius認證伺服器看到手機號等認證資訊,確認是合法使用者發來的請求,向DHCP伺服器請求分配使用者地址;
5)Radius認證通過後,由Radius向GGSN傳送攜帶使用者地址的確認資訊;
6)使用者得到了IP地址,就可以攜帶資料包,對GPRS專網系統資訊查詢和業務處理平臺進行訪問。
Android自帶的內部APN配置檔案
frameworks/base/core/res/res/xml/apns.xml檔案內容:
<!-- If you edit this version, also edit the version in the partner-supplied
apns-conf.xml configuration file -->
<apns version="7">
</apns>
該檔案被編譯到res.apk中,通過android的資源管理器進行訪問。
第三方提供的APN配置檔案
在Android原始碼build目錄下,通過搜尋apns-conf.xml可以找到在各個board中分別有配置:
在編譯該product時會將device/generic/goldfish/data/etc/apns-conf.xml檔案拷貝到system/etc/目錄下,最後打包到system.img中。
APN配置載入
packages/providers/TelephonyProvider/src/com/android/providers/telephony/TelephonyProvider.java檔案中的內部類DatabaseHelper用於建立telephony.db資料庫
public void onCreate(SQLiteDatabase db) {
// Set up the database schema
db.execSQL("CREATE TABLE " + CARRIERS_TABLE +
"(_id INTEGER PRIMARY KEY," +
"name TEXT," +
"numeric TEXT," +
"mcc TEXT," +
"mnc TEXT," +
"apn TEXT," +
"user TEXT," +
"server TEXT," +
"password TEXT," +
"proxy TEXT," +
"port TEXT," +
"mmsproxy TEXT," +
"mmsport TEXT," +
"mmsc TEXT," +
"authtype INTEGER," +
"type TEXT," +
"current INTEGER," +
"protocol TEXT," +
"roaming_protocol TEXT," +
"carrier_enabled BOOLEAN," +
"preset BOOLEAN default false," +
"bearer INTEGER);");
//從APN配置xml檔案中讀取APN配置,並儲存到資料表carriers中
initDatabase(db);
}
APN配置資訊載入分為兩部分,首先從Android自帶的內部APN配置檔案中讀取配置資訊,然後在讀取第三方提供的APN配置檔案資訊。
private void initDatabase(SQLiteDatabase db) {
// Read internal APNS data
Resources r = mContext.getResources();
//讀取frameworks/base/core/res/res/xml/apns.xml檔案
XmlResourceParser parser = r.getXml(com.android.internal.R.xml.apns);
int publicversion = -1;
try {
XmlUtils.beginDocument(parser, "apns");
//讀取APN配置版本資訊
publicversion = Integer.parseInt(parser.getAttributeValue(null, "version"));
//載入APN配置資訊,並儲存到資料表中
loadApns(db, parser);
} catch (Exception e) {
Log.e(TAG, "Got exception while loading APN database.", e);
} finally {
parser.close();
}
// Read external APNS data (partner-provided)
XmlPullParser confparser = null;
// Environment.getRootDirectory() is a fancy way of saying ANDROID_ROOT or "/system".
//讀取system/etc/apns-conf.xml檔案
File confFile = new File(Environment.getRootDirectory(), PARTNER_APNS_PATH);
FileReader confreader = null;
try {
confreader = new FileReader(confFile);
confparser = Xml.newPullParser();
confparser.setInput(confreader);
XmlUtils.beginDocument(confparser, "apns");
// 讀取第三方提供的APN配置版本號
int confversion = Integer.parseInt(confparser.getAttributeValue(null, "version"));
//判斷第三方提供的APN配置版本號是否與Android自帶的APN配置版本號相同
if (publicversion != confversion) {
throw new IllegalStateException("Internal APNS file version doesn't match "
+ confFile.getAbsolutePath());
}
//如果版本號相同,讀取APN配置資訊
loadApns(db, confparser);
} catch (FileNotFoundException e) {
// It's ok if the file isn't found. It means there isn't a confidential file
// Log.e(TAG, "File not found: '" + confFile.getAbsolutePath() + "'");
} catch (Exception e) {
Log.e(TAG, "Exception while parsing '" + confFile.getAbsolutePath() + "'", e);
} finally {
try { if (confreader != null) confreader.close(); } catch (IOException e) { }
}
}
從APN資訊載入原始碼中可以知道,第三方提供的APN配置資訊版本必須與內部APN配置資訊的版本相同。自此APN配置資訊就儲存在carriers表中了,並且通過TelephonyProvider向外提供訪問介面。