數據業務建立流程之常規APN參數的創建

    區別於前面的緊急APN,這裏的APN參數主要指常規的SIM卡APN參數,手機在上網時必須傳遞正確的APN參數給運營商纔可以接入移動網絡,而常規APN參數的創建是由監聽器觸發的。
        前面在DcTracker初始化過程中註冊了大量監聽器,其中有兩個監聽器可以觸發APN的創建過程:1、SIM載入完畢;2、APN改變。這兩個事件所導致的APN創建流程也都是類似的,分別是:
        【當SIM載入完畢時】,將會觸發onRecordsLoaded():
  1. private void onRecordsLoaded() {  
  2.     mAutoAttachOnCreationConfig = mPhone.getContext().getResources().getBoolean(com.android.internal.R.bool.config_auto_attach_data_on_creation);  
  3.     //創建APN參數  
  4.     createAllApnList();  
  5.     setInitialAttachApn();  
  6.     if (mPhone.mCi.getRadioState().isOn()) {  
  7.         notifyOffApnsOfAvailability(Phone.REASON_SIM_LOADED);  
  8.     }  
  9.     //嘗試發起數據業務  
  10.     setupDataOnConnectableApns(Phone.REASON_SIM_LOADED);  
  11. }  
        【當APN改變時】,將會觸發onApnChanged():
  1. private void onApnChanged() {  
  2.     DctConstants.State overallState = getOverallState();  
  3.     boolean isDisconnected = (overallState == DctConstants.State.IDLE || overallState == DctConstants.State.FAILED);  
  4.   
  5.   
  6.     if (mPhone instanceof GSMPhone) {  
  7.         ((GSMPhone)mPhone).updateCurrentCarrierInProvider();  
  8.     }  
  9.     //創建APN參數  
  10.     createAllApnList();  
  11.     setInitialAttachApn();  
  12.     //清除舊的連接  
  13.     cleanUpAllConnections(!isDisconnected, Phone.REASON_APN_CHANGED);  
  14.     //嘗試發起數據業務  
  15.     setupDataOnConnectableApns(Phone.REASON_APN_CHANGED);  
  16. }  
        從上面兩個過程對比我們發現,他們都通過兩個步驟進行APN的創建,分別是createAllApnList()和setInitialAttachApn(),他們的作用分別是創建APN和設置默認APN。

        下面分別來介紹這個過程。


一、創建APN過程


        創建APN是通過createAllApnList()來完成的
  1. private void createAllApnList() {  
  2.     mAllApnSettings = new ArrayList<ApnSetting>();  
  3.     IccRecords r = mIccRecords.get();  
  4.     //獲取該SIM的PLMN  
  5.     String operator = (r != null) ? r.getOperatorNumeric() : "";  
  6.     if (operator != null) {  
  7.         String selection = "numeric = '" + operator + "'";  
  8.         //查詢當前SIM的APN數據庫  
  9.         Cursor cursor = mPhone.getContext().getContentResolver().query(Telephony.Carriers.CONTENT_URI, null, selection, nullnull);  
  10.         if (cursor != null) {  
  11.             if (cursor.getCount() > 0) {  
  12.                 //根據APN參數創建APN列表  
  13.                 mAllApnSettings = createApnList(cursor);  
  14.             }  
  15.             cursor.close();  
  16.         }  
  17.     }  
  18.     //添加緊急APN  
  19.     addEmergencyApnSetting();  
  20.     //合併類似的APN  
  21.     dedupeApnSettings();  
  22.     if (mAllApnSettings.isEmpty()) {  
  23.         mPreferredApn = null;  
  24.     } else {  
  25.         //尋找prefer APN  
  26.         mPreferredApn = getPreferredApn();  
  27.         if (mPreferredApn != null && !mPreferredApn.numeric.equals(operator)) {  
  28.             mPreferredApn = null;  
  29.             setPreferredApn(-1);  
  30.         }  
  31.     }  
  32.     setDataProfilesAsNeeded();  
  33. }  
        這個方法主要經歷了三個步驟:
        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參數:
  1. private void dedupeApnSettings() {  
  2.     ArrayList<ApnSetting> resultApns = new ArrayList<ApnSetting>();  
  3.     int i = 0;  
  4.     while (i < mAllApnSettings.size() - 1) {  
  5.         ApnSetting first = mAllApnSettings.get(i);  
  6.         ApnSetting second = null;  
  7.         int j = i + 1;  
  8.         while (j < mAllApnSettings.size()) {  
  9.             second = mAllApnSettings.get(j);  
  10.             if (apnsSimilar(first, second)) {  
  11.                 ApnSetting newApn = mergeApns(first, second);  
  12.                 mAllApnSettings.set(i, newApn);  
  13.                 first = newApn;  
  14.                 mAllApnSettings.remove(j);  
  15.             } else {  
  16.                 j++;  
  17.             }  
  18.         }  
  19.         i++;  
  20.     }  
  21. }  
        這裏就一個去重的算法問題,這個算法的原理就是,經過一個循環,可以找到某個參數所有相同的組合。
        再然後就需要從當前衆多的APN參數中尋找一個當前合適的(prefer)APN參數,該APN要求其對應的PLMN屬於當前的SIM。他的來源是跟隨其他預置的APN一起被添加到數據庫中的,其特別之處就在於多了“preferapn_no_update”的屬性。他的作用就是作爲備用APN來發起數據連接。
  1. private ApnSetting getPreferredApn() {  
  2.     if (mAllApnSettings.isEmpty()) {  
  3.         return null;  
  4.     }  
  5.     Cursor cursor = mPhone.getContext().getContentResolver().query( PREFERAPN_NO_UPDATE_URI, new String[] { "_id""name""apn" },  
  6.             nullnull, Telephony.Carriers.DEFAULT_SORT_ORDER);  
  7.   
  8.   
  9.     if (cursor != null) {  
  10.         mCanSetPreferApn = true;  
  11.     } else {  
  12.         mCanSetPreferApn = false;  
  13.     }  
  14.     if (mCanSetPreferApn && cursor.getCount() > 0) {  
  15.         int pos;  
  16.         cursor.moveToFirst();  
  17.         pos = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID));  
  18.         for(ApnSetting p : mAllApnSettings) {  
  19.             log("getPreferredApn: apnSetting=" + p);  
  20.             if (p.id == pos && p.canHandleType(mRequestedApnType)) {  
  21.                 log("getPreferredApn: X found apnSetting" + p);  
  22.                 cursor.close();  
  23.                 return p;  
  24.             }  
  25.         }  
  26.     }  
  27.     if (cursor != null) {  
  28.         cursor.close();  
  29.     }  
  30.     return null;  
  31. }  

        從其獲取途徑可以看到,他的URI("content://telephony/carriers/preferapn_no_update")中多了"preferapn_no_update"的參數,這也是該APN的特殊之處。


二、設置默認APN過程


        經過前面的過程,APN的各項參數都已經準備就緒,接下來就是通過setInitialAttachApn()方法向Modem設置Attach默認APN過程。
  1. protected void setInitialAttachApn() {  
  2.     ApnSetting iaApnSetting = null;  
  3.     ApnSetting defaultApnSetting = null;  
  4.     ApnSetting firstApnSetting = null;  
  5.     if (mAllApnSettings != null && !mAllApnSettings.isEmpty()) {  
  6.         firstApnSetting = mAllApnSettings.get(0);  
  7.   
  8.   
  9.         for (ApnSetting apn : mAllApnSettings) {  
  10.             if (ArrayUtils.contains(apn.types, PhoneConstants.APN_TYPE_IA) && apn.carrierEnabled) {  
  11.                 iaApnSetting = apn;  
  12.                 break;  
  13.             } else if ((defaultApnSetting == null) && (apn.canHandleType(PhoneConstants.APN_TYPE_DEFAULT))) {  
  14.                 //找到類型是APN_TYPE_DEFAULT的APN參數作爲默認attach用  
  15.                 log("setInitialApn: defaultApnSetting=" + apn);  
  16.                 defaultApnSetting = apn;  
  17.             }  
  18.         }  
  19.     }  
  20.   
  21.   
  22.     ApnSetting initialAttachApnSetting = null;  
  23.     if (iaApnSetting != null) {  
  24.         initialAttachApnSetting = iaApnSetting;  
  25.     } else if (mPreferredApn != null) {  
  26.         initialAttachApnSetting = mPreferredApn;  
  27.     } else if (defaultApnSetting != null) {  
  28.         initialAttachApnSetting = defaultApnSetting;  
  29.     } else if (firstApnSetting != null) {  
  30.         initialAttachApnSetting = firstApnSetting;  
  31.     }  
  32.   
  33.   
  34.     if (initialAttachApnSetting == null) {  
  35.     } else {  
  36.         //設置Attach用的APN參數  
  37.         mPhone.mCi.setInitialAttachApn(initialAttachApnSetting.apn,  
  38.                 initialAttachApnSetting.protocol, initialAttachApnSetting.authType,  
  39.                 initialAttachApnSetting.user, initialAttachApnSetting.password, null);  
  40.     }  
  41. }  
        在上面這個過程中,遍歷當前所有的APN列表,尋找類型是APN_TYPE_DEFAULT的APN,然後將該APN參數傳遞給Modem用於初始的Attach。
        至此,所有APN準備工作就緒,接下來就是等待需要上網時,將當前APN激活,然後發起數據連接過程。
發佈了16 篇原創文章 · 獲贊 9 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章