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对象,并保存。

    
    
    
    
    
    <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的建立就此开始。下面的顺序图展示了这一过程:
DcTracker and DataConnection --- Telephony data Part II_第1张图片
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 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 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方法,下面是一个简单的流程图:
DcTracker and DataConnection --- Telephony data Part II_第2张图片
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)

结束!

你可能感兴趣的:(DcTracker and DataConnection --- Telephony data Part II)