區別於前面的緊急APN,這裏的APN參數主要指常規的SIM卡APN參數,手機在上網時必須傳遞正確的APN參數給運營商纔可以接入移動網絡,而常規APN參數的創建是由監聽器觸發的。
前面在DcTracker初始化過程中註冊了大量監聽器,其中有兩個監聽器可以觸發APN的創建過程:1、SIM載入完畢;2、APN改變。這兩個事件所導致的APN創建流程也都是類似的,分別是:
【當SIM載入完畢時】,將會觸發onRecordsLoaded():
【當APN改變時】,將會觸發onApnChanged():
從上面兩個過程對比我們發現,他們都通過兩個步驟進行APN的創建,分別是createAllApnList()和setInitialAttachApn(),他們的作用分別是創建APN和設置默認APN。
這個方法主要經歷了三個步驟:
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_TYPE_DEFAULT的APN,然後將該APN參數傳遞給Modem用於初始的Attach。
至此,所有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 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過程
- 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++;
- }
- }
再然後就需要從當前衆多的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;
- }
從其獲取途徑可以看到,他的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);
- }
- }
至此,所有APN準備工作就緒,接下來就是等待需要上網時,將當前APN激活,然後發起數據連接過程。