1. 程式人生 > >DcTracker and DataConnection --- Telephony data Part II

DcTracker and DataConnection --- Telephony data Part II

本篇主要講DataConnection的建立過程以及涉及到的一些知識點,主要分成下面幾部分:
1. DcTracker的初始化
2. ApnContext的初始化
3. 開機Data connection的建立

3.1 DataConnection建立流程圖
3.2 DcTracker.isDataAllowed方法
3.3 waiting apn
3.4 modem反饋結果的處理

4. Data recovery

5. 小知識點

5.1 Prefer apn
5.2 DcController
5.3 update link property
5.4 MTU的配置
5.5 data call list

1. DcTracker的初始化

在phone程序啟動時,DcTracker會在GsmsCdmaPhone物件的建構函式中通過TelephonyComponentFactory.makeDcTracker方法建立。
DcTracker的建構函式裡完成了很多工作,其中比較重要的有:
1. 註冊廣播監聽。
2. 註冊Ril監聽 (registerForAllEvents)。
3. 註冊ServiceStateTracker監聽 (registerServiceStateTrackerEvents)。
4. 註冊Setting監聽 (registerSettingsObserver)。
5. 初始化ApnContext (initApnContexts)。
6. 呼叫UiccController.registerForIccChanged方法註冊了Icc changed的監聽。
7. 呼叫update()–>onUpdateIcc()獲取SimRecords資訊,並呼叫SimRecords.registerForRecordsLoaded註冊了監聽。
8. 還監聽了Carriers表中的apn變化(Telephony.Carriers.CONTENT_URI)。
DcTracker註冊了這麼多監聽,所以內部有很多邏輯操作,一一羅列介紹不太可能,可以根據監聽對應的EVENT,看看DcTracker對於每一個變化都做了哪些操作。

DcTracker監聽的廣播如下:

        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_SCREEN_ON);
        filter.addAction(Intent.ACTION_SCREEN_OFF);
        filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
        filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
filter.addAction(INTENT_DATA_STALL_ALARM); filter.addAction(INTENT_PROVISIONING_APN_ALARM); filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); // TODO - redundent with update call below? mDataEnabledSettings.setUserDataEnabled(getDataEnabled()); mPhone.getContext().registerReceiver(mIntentReceiver, filter, null, mPhone);

2. ApnContext的初始化

DcTracker呼叫initApnContexts方法對ApnContext進行了初始化。Apn型別有很多(ConnectivityManager.TYPE_MOBILE, ConnectivityManager.TYPE_WIFI等),每個ApnContext物件對應了一種Apn型別,而手機支援哪些型別的網路連線是由硬體決定的,所以可以根據手機的硬體情況,將手機支援的網路型別配置成下面格式的xml資源。initApnContexts方法的邏輯就是根據配置構建ApnContext物件,並儲存。

    <!--下面是原始碼的配置,每個裝置都應該根據硬體有一份單獨的配置,connectivity manager會根據這個配置
    來決定哪些網路可以共存-->
    <!--陣列的格式如下:
    [Connection name],[ConnectivityManager.TYPE_xxxx],[associated radio-type],[priority],[restoral-timer(ms)],[dependencyMet]-->
    <!--第5個引數"restoral-timer"用於表明在自動回覆預設連線之前應該delay的毫秒數。如果連線不需要自動恢復,
    那麼可以設定為"-1"-->
    <!--第6個引數是用於表示開機時當前apn是否滿足了運營商的要求-->
    <!-- the 6th element indicates boot-time dependency-met value. -->
    <string-array translatable="false" name="networkAttributes">
        <item>"wifi,1,1,1,-1,true"</item>
        <item>"mobile,0,0,0,-1,true"</item>
        <item>"mobile_mms,2,0,2,60000,true"</item>
        <item>"mobile_supl,3,0,2,60000,true"</item>
        <item>"mobile_dun,4,0,2,60000,true"</item>
        <item>"mobile_hipri,5,0,3,60000,true"</item>
        <item>"mobile_fota,10,0,2,60000,true"</item>
        <item>"mobile_ims,11,0,2,60000,true"</item>
        <item>"mobile_cbs,12,0,2,60000,true"</item>
        <item>"wifi_p2p,13,1,0,-1,true"</item>
        <item>"mobile_ia,14,0,2,-1,true"</item>
        <item>"mobile_emergency,15,0,2,-1,true"</item>
    </string-array>

initApnContexts方法在儲存ApnContext物件時呼叫的是DcTracker.addApnContext方法:

    private ApnContext addApnContext(String type, NetworkConfig networkConfig) {
        ApnContext apnContext = new ApnContext(mPhone, type, LOG_TAG, networkConfig, this);
        mApnContexts.put(type, apnContext);
        mApnContextsById.put(ApnContext.apnIdForApnName(type), apnContext);
        mPrioritySortedApnContexts.add(apnContext);
        return apnContext;
    }

mApnContexts,mApnContextsById和mPrioritySortedApnContexts都會儲存ApnContext物件,但是三個集合中ApnContext的順序不一樣,這三個集合適用於不同的場景。


3. 開機Data connection的建立

3.1 DataConnection建立的流程圖

DcTracker在初始化的過程中呼叫DcTracker.onUpdateIcc方法獲取了SimRecords,並呼叫SimRecords.registerForRecordsLoaded註冊了監聽; 當Sim中的資料載入完成後,DcTracker會收到回撥message,message Id是EVENT_RECORDS_LOADED,處理該訊息時onRecordsLoadedOrSubIdChanged方法會被呼叫,Data connection的建立就此開始。下面的順序圖展示了這一過程:
Dylan_Sen
DcTracker.createAllApnList會從SimRecords中獲取mccmnc, 然後從carrier資料表中查詢可用的apn資料; 並根據獲取的apn資料構造ApnSetting物件,一條資料對應一個ApnSetting物件,所有的ApnSetting物件都會放到mAllApnSettings集合中儲存。另外,createAllApnList還會呼叫setDataProfilesAsNeeded方法將欄位”modemCognitive”為‘true’的apn資料以DataProfile的形式設定到modem側(通過Ril.setDataProfile方法)。

setInitialAttachApn方法會遍歷之前儲存在mAllApnSettings集合中的ApnSetting資料,然後依據下面的順序選出合適的initial attach apn,並將該Apn資料以DataProfile的形式通過Ril.setInitialAttachApn方法傳到modem側。
1) APN_TYPE_IA (Initial Attach)
2) mPreferredApn, i.e. the current preferred apn
3) The first apn that than handle APN_TYPE_DEFAULT
4) The first APN we can find.

setupDataOnConnectableApns方法會遍歷mPrioritySortedApnContexts中的ApnContext物件,即優先使用高優先順序的Apn建立Data connection。

    private void setupDataOnConnectableApns(String reason, RetryFailures retryFailures) {

        //...省略...

        for (ApnContext apnContext : mPrioritySortedApnContexts) {
            ArrayList<ApnSetting> waitingApns = null;

            if (VDBG) log("setupDataOnConnectableApns: apnContext " + apnContext);

            /*對FAILED和SCANNING狀態的ApnContext進行特殊處理*/
            if (apnContext.getState() == DctConstants.State.FAILED
                    || apnContext.getState() == DctConstants.State.SCANNING) {
                if (retryFailures == RetryFailures.ALWAYS) {//本次呼叫retryFailures為RetryFailures.ALWAYS
                    apnContext.releaseDataConnection(reason);
                } else if (apnContext.isConcurrentVoiceAndDataAllowed() == false &&
                        mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
                    // RetryFailures.ONLY_ON_CHANGE - check if voice concurrency has changed
                    apnContext.releaseDataConnection(reason);
                } else {
                    // RetryFailures.ONLY_ON_CHANGE - check if the apns have changed
                    int radioTech = mPhone.getServiceState().getRilDataRadioTechnology();
                    ArrayList<ApnSetting> originalApns = apnContext.getWaitingApns();
                    if (originalApns != null && originalApns.isEmpty() == false) {
                        waitingApns = buildWaitingApns(apnContext.getApnType(), radioTech);//重新構造waiting apn
                        if (originalApns.size() != waitingApns.size() ||
                                originalApns.containsAll(waitingApns) == false) {
                            apnContext.releaseDataConnection(reason);
                        } else {
                            continue;
                        }
                    } else {
                        continue;
                    }
                }
            }

            /**
              *只有可連線的apnContext才可以建立DataConnection; 當SIM資訊載入完成,嘗試建立DataConnection時,
              *default型別的apn已經是可連線狀態,至於ApnContext是什麼時候才是可連線的,下篇再講。
              */
            if (apnContext.isConnectable()) {
                log("isConnectable() call trySetupData");
                apnContext.setReason(reason);
                trySetupData(apnContext, waitingApns);
            }
        }
    }

只有可連線的apnContext(apnContext.isConnectable())才可以被用來建立data connection; 當SIM資訊載入完成,嘗試建立data connection時,default型別的apn已經是可連線狀態,至於ApnContext什麼時候置成了可連線狀態,下篇再講。

3.2 DcTracker.isDataAllowed方法

DcTracker.trySetupData方法在呼叫DcTracker.setupData方法之前會先判斷建立data connection的條件是否滿足,其中DcTracker.isDataAllowed方法會被呼叫,DataAllowFailReason物件也會在這個方法中填充內容。

    private boolean isDataAllowed(DataAllowFailReason failureReason) {
        final boolean internalDataEnabled;
        internalDataEnabled = mDataEnabledSettings.isInternalDataEnabled();

        //DcTracker在ServiceStateTracker中註冊了監聽,當data註冊狀態是ServiceState.STATE_IN_SERVICE時
        //mAttach的值就會被設定為true。
        boolean attachedState = mAttached.get();
        //當前想要設定的radio 狀態
        boolean desiredPowerState = mPhone.getServiceStateTracker().getDesiredPowerState();
        boolean radioStateFromCarrier = mPhone.getServiceStateTracker().getPowerStateFromCarrier();
        //當前註冊網路的data technology,比如LTE,HSPAP等。
        int radioTech = mPhone.getServiceState().getRilDataRadioTechnology();
        if (radioTech == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN) {
            desiredPowerState = true;
            radioStateFromCarrier = true;
        }

        IccRecords r = mIccRecords.get();
        boolean recordsLoaded = false;
        if (r != null) {
            recordsLoaded = r.getRecordsLoaded();
            if (DBG && !recordsLoaded) log("isDataAllowed getRecordsLoaded=" + recordsLoaded);
        }

        int dataSub = SubscriptionManager.getDefaultDataSubscriptionId();
        boolean defaultDataSelected = SubscriptionManager.isValidSubscriptionId(dataSub);

        PhoneConstants.State state = PhoneConstants.State.IDLE;
        // Note this is explicitly not using mPhone.getState.  See b/19090488.
        // mPhone.getState reports the merge of CS and PS (volte) voice call state
        // but we only care about CS calls here for data/voice concurrency issues.
        // Calling getCallTracker currently gives you just the CS side where the
        // ImsCallTracker is held internally where applicable.
        // This should be redesigned to ask explicitly what we want:
        // voiceCallStateAllowDataCall, or dataCallAllowed or something similar.
        if (mPhone.getCallTracker() != null) {
            state = mPhone.getCallTracker().getState();
        }

        if (failureReason != null) failureReason.clearAllReasons();
        //如果ServiceState不是ServiceState.STATE_IN_SERVICE狀態,那麼不可以建立data connection
        if (!(attachedState || mAutoAttachOnCreation.get())) {
            if(failureReason == null) return false;
            failureReason.addDataAllowFailReason(DataAllowFailReasonType.NOT_ATTACHED);
        }
        //如果SIM records沒有載入完成,也不可以建立data connection
        if (!recordsLoaded) {
            if(failureReason == null) return false;
            failureReason.addDataAllowFailReason(DataAllowFailReasonType.RECORD_NOT_LOADED);
        }
        //如果CS call當前不是idle狀態,而voice和data又不能共存,那麼也不可以建立data connection
        if (state != PhoneConstants.State.IDLE &&
                !mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
            if(failureReason == null) return false;
            failureReason.addDataAllowFailReason(DataAllowFailReasonType.INVALID_PHONE_STATE);
            failureReason.addDataAllowFailReason(
                    DataAllowFailReasonType.CONCURRENT_VOICE_DATA_NOT_ALLOWED);
        }
        //一般只有在手機處於Emergency call或者Emergency call back mode時internalDataEnabled才是false,其他情況都是true。
        if (!internalDataEnabled) {
            if(failureReason == null) return false;
            failureReason.addDataAllowFailReason(DataAllowFailReasonType.INTERNAL_DATA_DISABLED);
        }
        //是否已經設定了預設資料卡, 如果沒有那麼也不可以建立data connection。
        if (!defaultDataSelected) {
            if(failureReason == null) return false;
            failureReason.addDataAllowFailReason(
                    DataAllowFailReasonType.DEFAULT_DATA_UNSELECTED);
        }
        //如果是roaming狀態,但是roaming data設定沒有開啟,那麼也不能建立data connection
        if (mPhone.getServiceState().getDataRoaming() && !getDataRoamingEnabled()) {
            if(failureReason == null) return false;
            failureReason.addDataAllowFailReason(DataAllowFailReasonType.ROAMING_DISABLED);
        }
        //如果PS被限制了,那麼也不可以建立data connection
        if (mIsPsRestricted) {
            if(failureReason == null) return false;
            failureReason.addDataAllowFailReason(DataAllowFailReasonType.PS_RESTRICTED);
        }
        //如果設定的radio 狀態是off,那麼也不可以建立data connection
        if (!desiredPowerState) {
            if(failureReason == null) return false;
            failureReason.addDataAllowFailReason(DataAllowFailReasonType.UNDESIRED_POWER_STATE);
        }
        //radioStateFromCarrier一般是由CarrierDefaultBroadcastReceiver發起的action set,
        //當CarrierDefaultBroadcastReceiver收到廣播後會設定radioStateFromCarrier。
        if (!radioStateFromCarrier) {
            if(failureReason == null) return false;
            failureReason.addDataAllowFailReason(DataAllowFailReasonType.RADIO_DISABLED_BY_CARRIER);
        }

        return failureReason == null || !failureReason.isFailed();
    }
3.3 waiting apn

如果建立data connection的條件都已經滿足,那麼DcTtracker.trySetupData方法會根據傳入的ApnContext構建合適的apn即waiting apn。DcTracker.buildWaitingApns方法用於構建waiting apn,在構建的過程中會先確認DUN apn和prefer apn的情況,然後再遍歷篩選mAllApnSettings中儲存的Apn配置資料,構建好的waiting apn會被儲存在ApnContext中。該方法的傳入引數是apn type和radio data technology是篩選的依據,radio data technology會用於確認bearer bit mask, 另外mcc mnc 也會用到。

根據網路請求的型別,可以確定Apn的型別即對應的ApnContext,可是在建立data connection時還需要對應的Apn配置(ApnSetting),waiting apn就是在用指定Apn型別的ApnContext建立data connection時可以使用的具體apn配置(ApnSetting)。

DcTracker.setupData方法會從waiting apn list中取合適的apn資料(根據apn內的引數篩選),然後呼叫DcTracker.createDataConnection方法,而後者會呼叫DataConnection.makeDataConnection靜態方法建立DataConnection。DataConnection是StateMachine的子類,內部包含了DcDefaultState、DcInactiveState、DcActivatingState、DcActiveState、DcDisconnectingState和DcDisconnectionErrorCreatingConnection六種狀態; 初始化時DcDefaultState會被設定為其他狀態的母狀態,而DcInactiveState會作為初始化狀態。 DcTracker.createDataConnection建立DataConnection物件後,會使用DataConnection物件作為引數繼續構建DcAsyncChannel物件。DcTracker.setupData會呼叫DcAsyncChannel.bringUp方法,DcAsyncChannel.bringUp方法很簡單,只是給DataConnection傳送了一個DataConnection.EVENT_CONNECT 訊息,至於訊息傳送流程涉及到AsyncChannel,這裡不做介紹。

    public void bringUp(ApnContext apnContext, int profileId, int rilRadioTechnology,
                        boolean unmeteredUseOnly, Message onCompletedMsg,
                        int connectionGeneration) {
        if (DBG) {
            log("bringUp: apnContext=" + apnContext + "unmeteredUseOnly=" + unmeteredUseOnly
                    + " onCompletedMsg=" + onCompletedMsg);
        }
        sendMessage(DataConnection.EVENT_CONNECT,
                new ConnectionParams(apnContext, profileId, rilRadioTechnology, unmeteredUseOnly,
                        onCompletedMsg, connectionGeneration));
    }

DcInactiveState作為預設狀態首先會處理DataConnection.EVENT_CONNECT,然後DataConnection.onConnect方法會被呼叫,該方法會呼叫Ril.setupDataCall方法將請求發往modem; 做完這些操作後會轉向DcActivatingState狀態。

3.4 modem反饋結果的處理

Modem處理完請求後會反饋一個EVENT_SETUP_DATA_CONNECTION_DONE訊息,當前活躍狀態DcActivatingState會處理這個訊息,在這個過程中DataConnection.onSetupConnectionCompleted方法會被呼叫,用來處理modem反饋的引數,並返回一個DataCallResponse.SetupResult物件。

    private DataCallResponse.SetupResult onSetupConnectionCompleted(AsyncResult ar) {
        DataCallResponse response = (DataCallResponse) ar.result;
        ConnectionParams cp = (ConnectionParams) ar.userObj;
        DataCallResponse.SetupResult result;

        if (cp.mTag != mTag) {//"首先判斷反饋的結果是否對應當前DataConnection,如果不對應,返回ERR_Stale"
            if (DBG) {
                log("onSetupConnectionCompleted stale cp.tag=" + cp.mTag + ", mtag=" + mTag);
            }
            result = DataCallResponse.SetupResult.ERR_Stale;
        } else if (ar.exception != null) {//"如果結果中有異常"
            if (DBG) {
                log("onSetupConnectionCompleted failed, ar.exception=" + ar.exception +
                    " response=" + response);
            }

            if (ar.exception instanceof CommandException
                    && ((CommandException) (ar.exception)).getCommandError()
                    == CommandException.Error.RADIO_NOT_AVAILABLE) {//"如果是radio不可用的異常"
                result = DataCallResponse.SetupResult.ERR_BadCommand;
                result.mFailCause = DcFailCause.RADIO_NOT_AVAILABLE;
            } else {//"否則返回DataCallResponse.SetupResult.ERR_RilError,並返回對應fail cause"
                result = DataCallResponse.SetupResult.ERR_RilError;
                result.mFailCause = DcFailCause.fromInt(response.status);
            }
        } else if (response.status != 0) {//“如果沒有發生異常,但是沒有建立成功,同樣返回ERR_RilError和fail cause”
            result = DataCallResponse.SetupResult.ERR_RilError;
            result.mFailCause = DcFailCause.fromInt(response.status);
        } else {//"如果建立成功,那麼就獲取連結資訊,並更新link 屬性。"
            if (DBG) log("onSetupConnectionCompleted received successful DataCallResponse");
            mCid = response.cid;

            mPcscfAddr = response.pcscf;

            result = updateLinkProperty(response).setupResult;
        }

        return result;
    }

DcActivatingState.processMessage方法會根據DataConnection.onSetupConnectionCompleted的不同反饋結果會轉換到不同的狀態,如果成功會轉換到DcActiveState狀態,如流程圖所示,DcActiveState.enter方法會發送DctConstants.EVENT_DATA_SETUP_COMPLETE通知DcTracker, 並建立一個DcNetworkAgent物件註冊在ConnectivityService中。
對於失敗的情況,我們看下ERR_RilError

 public boolean processMessage(Message msg) {
            //...
            switch (msg.what) {
                //...

                case EVENT_SETUP_DATA_CONNECTION_DONE:
                    ar = (AsyncResult) msg.obj;
                    cp = (ConnectionParams) ar.userObj;

                    DataCallResponse.SetupResult result = onSetupConnectionCompleted(ar);
                    //...
                    switch (result) {
                        //...
                        case ERR_RilError:
                            // Retrieve the suggested retry delay from the modem and save it.
                            // If the modem want us to retry the current APN again, it will
                            // suggest a positive delay value (in milliseconds). Otherwise we'll get
                            // NO_SUGGESTED_RETRY_DELAY here.
                            long delay = getSuggestedRetryDelay(ar);//獲取retry所需等待時間
                            cp.mApnContext.setModemSuggestedDelay(delay);//將retry所需時間儲存到ApnContext中。

                            /**省略log部分*/

                            if (cp.mApnContext != null) cp.mApnContext.requestLog(str);

                            // Save the cause. DcTracker.onDataSetupComplete will check this
                            // failure cause and determine if we need to retry this APN later
                            // or not.
                            mInactiveState.setEnterNotificationParams(cp, result.mFailCause);//為DcInactiveState狀態設定引數。
                            transitionTo(mInactiveState);//轉換到DcInactiveState狀態。
                            break;
                        case ERR_Stale:
                            loge("DcActivatingState: stale EVENT_SETUP_DATA_CONNECTION_DONE"
                                    + " tag:" + cp.mTag + " != mTag:" + mTag);
                            break;
                        default:
                            throw new RuntimeException("Unknown SetupResult, should not happen");
                    }
                    retVal = HANDLED;
                    break;

                case EVENT_GET_LAST_FAIL_DONE:
                    //...
                    break;

            }
            return retVal;
        }

失敗後會轉換到DcInactiveState狀態,DcInactiveState.enter方法會給DcTracker傳送DctConstants.EVENT_DATA_SETUP_COMPLETE訊息,然後DcTracker.onDataSetupComplete方法會被呼叫,該方法會完成data connection建立失敗的廣播發送等工作,同時會呼叫DcTracker.onDataSetupCompleteError方法檢查是否滿足retry的條件,如果滿足,則呼叫AlarmManager設定retry需要等待的時間,到時間後DcTracker會受到INTENT_RECONNECT_ALARM廣播,開始重新嘗試建立data connection。


4 Data recovery

如果只有傳送的資料包而沒有接收的資料包,而且傳送的包大於watch dog trigger(setting設定),那麼就需要Recovery data。Recovery時有五種action; action對應的操作是依次遞進關係,即上一個action執行過後,如果data還是stall狀態,那麼會執行下一個action,具體Action以及對應操作如下:

Action 操作
GET_DATA_CALL_LIST 只是向modem查詢當前的call list
CLEANUP 清除當前的data連線,等清除成功後DcTracker會重新建立新連線
REREGISTER 通過ServiceStateTracker查詢當前的Preferred network type, ServiceStateTracker收到查詢結果後,會重新設定Preferred network type
RADIO_RESTART 關閉radio,ServiceStateTraker會負責重新開啟radio
RADIO_RESTART_WITH_PROP 同樣會關閉radio,不過還會設定gsm.radioreset,以便Ril和system採取進一步的措施來保證recovery data。

每個Action的觸發流程都是一樣的,呼叫startDataStallAlarm方法傳送 INTENT_DATA_STALL_ALARM,
INTENT_DATA_STALL_ALARM 會以Pending intent形式延遲1Min或者6Min(setting配置)傳送。
DcTracker收到這個廣播後會傳送EVENT_DATA_STALL_ALARM ,稍後處理這個message時會更新Data stall info,根據這些資訊判斷是否需要Recovery data; 最後呼叫startDataStallAlarm方法繼續傳送INTENT_DATA_STALL_ALARM。對於需要Recovery data的情況,DcTracker會發送 EVENT_DO_RECOVERY,然後doRecovery會被呼叫,根據當前Action做相應的處理。


5. 小知識點

下面是幾個小知識點,後續有機會再補充內容。

5.1 Prefer apn

Prefer apn其實就是selected apn,DcTracker和Apnsettings中都會用到。DcTracker在建立data 連線的時候會查詢,在連線建立成功後會更新儲存;Apnsettings是為使用者通過UI操作提供的介面,在顯示的時候會查詢,當用戶切換apn後會更新儲存。
雖然這兩個檔案所使用的Uri有差異,但是在TelephonyProvider中這兩個Uri會操作同一個sharedpreference檔案preferred-apn.xml。

File Uri
DcTracker “content://telephony/carriers/preferapn_no_update/subId/”
ApnSettings “content://telephony/carriers/preferapn/subId”
5.2 DcController

DcController內部有一個ArrayList型別的mDcListAll,用於儲存所有的DataConnection,每個DataConnection物件都會呼叫addDc和removeDc將自己新增到mDcListAll或者從mDcListAll刪除。

DcController內部維護了一個HashMap: mDcListActiveByCid, 用於儲存active的DataConnection; DataConnection會在DcActiveState.enter方法中呼叫DcController.addActiveDcByCid方法將自己放進mDcListActiveByCid,在DcInactiveState.enter方法中呼叫DcController.removeActiveDcByCid方法將自己從mDcListActiveByCid中刪除。
另外,DcController.DccDefaultState的enter方法在Ril.java內註冊了data call list的監聽(registerForDataCallListChanged),當modem上報data call list的變化時,便會呼叫DccDefaultState.onDataStateChanged方法對新的data call list進行處理。DccDefaultState.onDataStateChanged方法會根據mDcListActiveByCid中儲存的DataConnection和傳入的DataCallResponse資料對DataConnection進行更新,並通知DcTracker和相應的DataConnection進行相關操作。

當Ril收到Data call list變化的訊息後,會給DcController發message 即EVENT_DATA_STATE_CHANGED,DcController收到message後會呼叫onDataStateChanged方法進行處理,進而會呼叫到DataConnection內的updateLinkProperty方法,下面是一個簡單的流程圖:
這裡寫圖片描述
ConnectivityService.updateLinkProperties方法會更新MTU、TCP buffer、routes、DNS等連結屬性。

5.4 MTU的配置
  1. APN的配置。
  2. 網路側/modem側設定。
  3. 通過com.android.internal.R.integer.config_mobile_mtu配置。
5.5 data call list
  1. modem主動上報變化,通過dataCallListChanged方法,相關log: UNSOL_DATA_CALL_LIST_CHANGED(RIL_UNSOL_DATA_CALL_LIST_CHANGED)
  2. AP側通過getDataCallList方法主動查詢,相關log: DATA_CALL_LIST(RIL_REQUEST_DATA_CALL_LIST)

結束!