路由配置信息的获取

路由配置信息的获取有多种方式,wifi,mobile data,Tethering,VPN。这里主要分析由mobile data获取路由配置信息的过程。
结合ConnectivityService类,其大致的类图与流程图如下:
这里写图片描述

从打开数据开关到获取到路由配置信息的时序图如下:

这里写图片描述
本篇博客只介绍DcTracker,DataConnection,GsmCdmaPhone,RIL层的AT指令以及modem相关信令等内容,其他的ConnectivityService,NetworkFactory,NetworkMonitor等内容可以参考:ConnectivityService框架初识

DcTracker与DataConnection的分工不同,DcTracker主要处理APN参数,数据重连机制,通知上层等一些琐碎的工作;DataConnection则负责与RIL层通讯,它本身就是一个状态机,用于处理网络请求过程中产生的各种状态,网络请求成功后生成NetworkAgent对象参与ConnectivityService中的网络评分工作。

本篇分为三个小节来分析。
一.DcTracker
二.DataConnection
三.RIL层的AT指令以及modem相关的信令

一.DcTracker

DcTracker主要负责处理APN参数,数据重连机制,通知上层等工作。
这里分为两个小节分析
1.处理apn参数
(1).初始化ApnContext
(2).激活ApnContext
(3).筛选apn
(4).发起请求
2.请求网络的结果处理
(1).网络请求成功
(2).数据重连

1.处理APN参数

(1).初始化ApnContext

ApnContext简介:
ApnContext用来建立指定apn类型的apn上下文,ApnContext中定义了此apn类型所需的重要属性。

       private final String mApnType; //apn的类型
       public final int priority; //网络连接类型的优先级
       private final RetryManager mRetryManager;
       private final DcTracker mDcTracker;
       private int mRefCount = 0;
       private ApnSetting mApnSetting; //此apn类型的apn参数
       String mReason;
       private DctConstants.State mState;//ApnContext所处的状态

apn的类型定义在PhoneConstants.java中:

    public static final String APN_TYPE_ALL = "*";
    public static final String APN_TYPE_DEFAULT = "default";
    public static final String APN_TYPE_MMS = "mms";
    public static final String APN_TYPE_SUPL = "supl";
    public static final String APN_TYPE_DUN = "dun";
    public static final String APN_TYPE_HIPRI = "hipri";
    public static final String APN_TYPE_FOTA = "fota";
    public static final String APN_TYPE_IMS = "ims";
    public static final String APN_TYPE_CBS = "cbs";
    public static final String APN_TYPE_IA = "ia";
    public static final String APN_TYPE_EMERGENCY = "emergency";
    public static final String APN_TYPE_DM = "dm";
    public static final String APN_TYPE_WAP = "wap";
    public static final String APN_TYPE_NET = "net";
    public static final String APN_TYPE_CMMAIL = "cmmail";
    public static final String APN_TYPE_TETHERING = "tethering";
    public static final String APN_TYPE_RCSE = "rcse";
    public static final String APN_TYPE_XCAP = "xcap";
    public static final String APN_TYPE_RCS = "rcs";
    public static final String APN_TYPE_BIP = "bip";

apn类型的优先级读取自文件frameworks\base\core\res\res\values\config.xml

    <!-- An Array of "[Connection name],[ConnectivityManager.TYPE_xxxx],
         [associated radio-type],[priority],[restoral-timer(ms)],[dependencyMet]  -->
    <string-array translatable="false" name="networkAttributes">
        <item>"wifi,1,1,2,-1,true"</item>
        <item>"tedongle,49,49,1,-1,true"</item>
        <item>"mobile,0,0,0,-1,true"</item>
        <item>"mobile_mms,2,0,2,300000,true"</item>
        <item>"mobile_supl,3,0,2,300000,true"</item>
        <item>"mobile_dun,4,0,3,300000,true"</item>
        <item>"mobile_hipri,5,0,3,300000,true"</item>
        <item>"bluetooth,7,7,0,-1,true"</item>
        <item>"mobile_fota,10,0,2,300000,true"</item>
        <item>"mobile_ims,11,0,-1,-1,true"</item>
        <item>"mobile_cbs,12,0,2,300000,true"    </item>
        <item>"mobile_dm,34,0,3,300000,true"</item>
        <item>"mobile_wap,35,0,3,300000,true"</item>
        <item>"mobile_net,36,0,3,300000,true"</item>
        <item>"mobile_cmmail,37,0,3,300000,true"</item>
        <item>"mobile_rcse,38,0,3,300000,true"</item>
        <item>"mobile_ia,14,0,2,-1,true"</item>
        <item>"mobile_emergency,15,0,2,-1,true"</item>
        <item>"mobile_xcap,40,0,3,300000,true"</item>
        <item>"mobile_rcs,41,0,3,300000,true"</item>
        <item>"mobile_bip,42,0,3,300000,true"</item>
        <item>"mobile_vsim,43,0,-1,300000,true"</item>
</string-array>

ApnContext的状态

     * IDLE: ready to start data connection setup, default state
     * CONNECTING: state of issued startPppd() but not finish yet
     * SCANNING: data connection fails with one apn but other apns are available
     *           ready to start data connection on other apns (before INITING)
     * CONNECTED: IP connection is setup
     * DISCONNECTING: Connection.disconnect() has been called, but PDP
     *                context is not yet deactivated
     * FAILED: data connection fail for all apns settings
     * RETRYING: data connection failed but we're going to retry.

ApnContext的初始化:因为apn的类型有很多种,任意一种都可能使用到,因此需要初始化所有apn类型的ApnContext。在DcTracker对象创建的时候初始化

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;
}
    private void initApnContexts() {
        log("initApnContexts: E");
        // Load device network attributes from resources
        String[] networkConfigStrings = mPhone.getContext().getResources().getStringArray(
                com.android.internal.R.array.networkAttributes);
        for (String networkConfigString : networkConfigStrings) {
            NetworkConfig networkConfig = new NetworkConfig(networkConfigString);
            ApnContext apnContext = null;
            switch (networkConfig.type) {
            case ConnectivityManager.TYPE_MOBILE:
                apnContext = addApnContext(PhoneConstants.APN_TYPE_DEFAULT, networkConfig);
                break;
            case ConnectivityManager.TYPE_MOBILE_MMS:
                apnContext = addApnContext(PhoneConstants.APN_TYPE_MMS, networkConfig);
                break;
             .....
             }
          }
       }

(2).激活ApnContext

激活ApnContext就是设置apnContext.setEnabled(true)。

①.激活默认apn类型的ApnContext

默认的apn类型是default。当SUBSCRIPTION_CHANGED状态变化时,激活默认的ApnContext。SUBSCRIPTION_CHANGED状态变化包括:插拔SIM卡,切换默认SIM卡。且ApnContext跟卡槽无关,默认apn类型的ApnContext被激活后,无论卡1还是卡2都可使用此默认类型的apn,无须再次激活。参见下图,PhoneSwitcher和TelephonyNetworkFactory都监听了SUBSCRIPTION_CHANGED状态的变化。

11-25 15:23:43.294723  3731  3731 D TelephonyNetworkFactory[1]: onActivePhoneSwitch(true, true)
11-25 15:23:43.295819  3731  3731 D DCT     : [ApnContext:default] ApnContext.incRefCount - mRefCount == 0 
11-25 15:23:43.845274  3731  3731 D DCT     : [1]onEnableApn: apnContext={mApnType=default mState=IDLE mWaitingApns={null} mApnSetting={null} mReason=dataDisabled mDataEnabled=false mDependencyMet=true mWifiApns={null}} call applyNewState
11-25 15:23:43.845340  3731  3731 D DCT     : [1]applyNewState(default, true(false), true(true))

这里写图片描述

②.激活其他apn类型的ApnContext

主动发起网络请求needNetworkFor()时,激活其他类型的ApnContext。参见上图,PhoneSwitcher接收到EVENT_REQUEST_NETWORK和EVENT_RELEASE_NETWORK事件消息后会调用onEvaluate(),如果满足条件,将会激活特定apn类型的ApnContext。同理,如果有新的网络请求和网络评分变化的话,也会引发TelephonyNetworkFactory激活特定apn类型的ApnContext。

发送彩信过程中激活mms类型的ApnContext:

09-25 14:06:39.521012  1324  1324 D TelephonyNetworkFactory[0]: onNeedNetworkFor NetworkRequest [ id=19, legacyType=-1, [ Transports: CELLULAR Capabilities: MMS&NOT_RESTRICTED&TRUSTED&NOT_VPN Specifier: <1>] ]
09-25 14:06:39.522575  1324  1324 D DCT     : [ApnContext:mms] ApnContext.incRefCount - mRefCount == 0 
09-25 14:06:39.523173  1324  1324 D DCT     : [0]handleMessage msg={ when=0 what=270349 arg1=1 arg2=1 target=com.android.internal.telephony.dataconnection.DcTracker }
09-25 14:06:39.523377  1324  1324 D DCT     : [0]onEnableApn: apnContext={mApnType=mms mState=IDLE mWaitingApns={null} mApnSetting={null} mReason=dataEnabled mDataEnabled=false mDependencyMet=true mWifiApns={null}} call applyNewState
09-25 14:06:39.523482  1324  1324 D DCT     : [0]applyNewState(mms, true(false), true(true))

DEBUG:applyNewState|TelephonyNetworkFactory

(3).筛选apn

讲筛选apn之前,我们需要先了解buildWaitingApns(String requestedApnType, int radioTech)。由于apns-conf.xml中我们配置了很多apn参数,同一个apn 类型可能有多个apn参数。以中国联通default类型为例:

  <apn carrier="中国联通 3g 网络 (China Unicom)"
      mcc="460"
      mnc="01"
      apn="3gnet"
      type="default,supl"
  />

  <apn carrier="中国联通 GPRS (China Unicom)"
      mcc="460"
      mnc="01"
      apn="uninet"
      type="default,supl"
  />

apn参数列表中用户可以指定某个apn参数参与请求网络,这就产生了优先级。因此,buildWaitingApns()的作用就是把指定类型的所有apn参数都收集起来用ArrayList来保存,用户指定的apn参数放在首位,保证优先使用此apn参与请求网络过程。具体细节可以看buildWaitingApns()的实现方法,这里不贴代码。

筛选apn主要由两个方法完成:setupDataOnConnectableApns()和trySetupData()
setupDataOnConnectableApns():为筛选apn做准备工作。

循环所有apn类型的ApnContext。
如果循环到的ApnContext状态为FAILED和SCANNING,说明此apn类型的ApnContext先前使用过,且处于以下状态:一直尝试重连;数据不可用状态;重新加载waitingApns并与此ApnContext保存的waitingApns对比,如果不相同,说明apn发生了变化,执行releaseDataConnection()释放掉此ApnContext的连接。ApnContext的状态重新设置为Idle。
首次使用此apn类型的ApnContext,waitingApns为null

ApnContext必须为激活状态且处于IDLE,SCANNING,RETRYING,FAILED状态才可以trySetupData:

if (apnContext.isConnectable()) {
    log("setupDataOnConnectableApns: isConnectable() call trySetupData");
    apnContext.setReason(reason);
    trySetupData(apnContext, waitingApns);
}
ApnContext.java

public boolean isConnectable() {
    return isReady() && ((mState == DctConstants.State.IDLE)
                            || (mState == DctConstants.State.SCANNING)
                            || (mState == DctConstants.State.RETRYING)
                            || (mState == DctConstants.State.FAILED));
}
public boolean isReady() {
    return mDataEnabled.get() && mDependencyMet.get();
}

trySetupData():筛选出合适的apn和做条件判断
条件判断:主要有以下的条件
apnContext.isConnectable():apnContext是否已激活
isEmergencyApn :是否是紧急apn
isDataAllowed() :总结一下就是SIM卡加载,数据开关,默认数据选择,漫游,PS域是否注册成功(recordsLoaded,internalDataEnabled,defaultDataSelected,
getDataRoaming,mIsPsRestricted)
加载waitingApns:如果是首次使用此apn类型的apnContext,需要加载waitingApns

  waitingApns = buildWaitingApns(apnContext.getApnType(), radioTech);

发起请求:

  boolean retValue = setupData(apnContext, radioTech);

(4).发起请求

①.是否是单通模式。如果是单通模式,且当前有更高优先级的ApnContext已被激活并连接的情况下,放弃此次拨号过程。如果当前拨号的ApnContext优先级更高,则释放其他低优先级的拨号连接,建立当前的拨号连接。(MTK)

②.创建DataConnection对象,调用bringUp()发起下一步的拨号动作。更新ApnContext的状态为CONNECTING。如果DataConnection处理拨号过程结束,会通过EVENT_DATA_SETUP_COMPLETE事件通知DcTracker并调用onDataSetupComplete()

    DataConnection conn = DataConnection.makeDataConnection(mPhone,
            getPdpConnectionPoolSize(), this, mDcTesterFailBringUpAll, mDcc);
    mDataConnections.put(getPdpConnectionPoolSize(), conn);
    dcac = new DcAsyncChannel(conn, LOG_TAG);
    int status = dcac.fullyConnectSync(mPhone.getContext(),
            this, conn.getHandler());
    ......
    apnContext.setDataConnectionAc(dcac);
    apnContext.setApnSetting(apnSetting);
    apnContext.setState(DctConstants.State.CONNECTING);
    mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());

    Message msg = obtainMessage();
    msg.what = DctConstants.EVENT_DATA_SETUP_COMPLETE;
    msg.obj = new Pair<ApnContext, Integer>(apnContext, generation);
    dcac.bringUp(apnContext, profileId, radioTech, msg, generation);            

2.请求网络的结果处理

(1).网络请求成功

如果DataConnection处理网络请求过程结束,会通过EVENT_DATA_SETUP_COMPLETE事件通知DcTracker并调用onDataSetupComplete(),我们来看看onDataSetupComplete()的实现:
请求网络成功,通知上层:

①.拨号成功,通过DcAsyncChannel设置proxy,如果apn参数中proxy为空,默认为8080

if (TextUtils.isEmpty(port)) port = "8080";
ProxyInfo proxy = new ProxyInfo(apn.proxy,Integer.parseInt(port), null);
dcac.setLinkPropertiesHttpProxySync(proxy);

②.设置默认的apn参数。

setPreferredApn(mPreferredApn.id);

③.设置ApnContext的状态为CONNECTED。调用completeConnection()实现:通知Phone拨号成功。周期读取底层文件,判断终端是否有接收和发送数据包,启动一个线程,更新UI界面,主要是上下行图标,以及流量结算页面。

startNetStatPoll();
startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);

这里写图片描述

(2).数据重连

当网络请求发生异常时,framework层会根据异常的类型去重新建立数据连接。
目前的数据重连主要有四个方面:
①.SETUP_DATA_CALL成功,但是携带返回的数据异常。
②.EVENT_DISCONNECT_DONE数据连接断开后,尝试重新连接。
③.隔一段时间收不到数据包后触发doRecover机制
④.pdp链路中断
以上的数据重连,最后都是使用了startAlarmForReconnect()方法,借助于定时器,间隔一定的时间重新发起连接。

    protected void startAlarmForReconnect(long delay, ApnContext apnContext) {
        String apnType = apnContext.getApnType();

        Intent intent = new Intent(INTENT_RECONNECT_ALARM + "." + apnType);
        intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON, apnContext.getReason());
        intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_TYPE, apnType);
        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
        ......
        mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                SystemClock.elapsedRealtime() + delay, alarmIntent);
    }        

    private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver () {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            ......
            } else if (action.startsWith(INTENT_RECONNECT_ALARM)) {
                if (DBG) log("Reconnect alarm. Previous state was " + mState);
                onActionIntentReconnectAlarm(intent);
            }      
            ......     
    };    

    private void onActionIntentReconnectAlarm(Intent intent) {
        Message msg = obtainMessage(DctConstants.EVENT_DATA_RECONNECT);
        msg.setData(intent.getExtras());
        sendMessage(msg);
    }       


①.SETUP_DATA_CALL成功,但是携带返回的数据异常。

如果返回的结果ar.exception为空,且DataConnection的对象dcac 为空,说明DcTracker与DataConnection的交互出了问题,设置handleError为true,启动重连机制。如果ar.exception不为空,说明请求的结果有误,通知上层并标记请求失败的apn,启动重连机制。

    /**
     * A SETUP (aka bringUp) has completed, possibly with an error. If
     * there is an error this method will call {@link #onDataSetupCompleteError}.
     */
    protected void onDataSetupComplete(AsyncResult ar) {
        if (ar.exception == null) {
            ......
            if (dcac == null) {
                log("onDataSetupComplete: no connection to DC, handle as error");
                cause = DcFailCause.CONNECTION_TO_DATACONNECTIONAC_BROKEN;
                handleError = true;
            } else {
            ......
            }
        } else {
            ......
            //通知上层SETUP_DATA_CALL失败
            ApnSetting apn = apnContext.getApnSetting();
            mPhone.notifyPreciseDataConnectionFailed(apnContext.getReason(),
                    apnContext.getApnType(), apn != null ? apn.apn : "unknown", cause.toString());

            // Compose broadcast intent send to the specific carrier signaling receivers
            Intent intent = new Intent(TelephonyIntents
                    .ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED);
            intent.putExtra(TelephonyIntents.EXTRA_ERROR_CODE_KEY, cause.getErrorCode());
            intent.putExtra(TelephonyIntents.EXTRA_APN_TYPE_KEY, apnContext.getApnType());
            mPhone.getCarrierSignalAgent().notifyCarrierSignalReceivers(intent);
            //是否重新启动Radio
            if (cause.isRestartRadioFail(mPhone.getContext(), mPhone.getSubId()) ||
                    apnContext.restartOnError(cause.getErrorCode())) {
                if (DBG) log("Modem restarted.");
                sendRestartRadio();
            }
            //标记SETUP_DATA_CALL失败的apn
            if (isPermanentFailure(cause)
                    /// M: ignore for specific causes
                    || mtkIgnoredPermanentFailure(cause)) {
                log("cause = " + cause + ", mark apn as permanent failed. apn = " + apn);
                apnContext.markApnPermanentFailed(apn);
            }

            handleError = true;
        }
        if (handleError) {
            onDataSetupCompleteError(ar);
        }
    }        

尝试使用下一个合适的apn发起重连,delay 值取决于当前waitingApns。如果已经没有合适的apn发起重连,则apnContext设置状态为FAILED并通知上层SETUP_DATA_CALL失败

private void onDataSetupCompleteError(AsyncResult ar) {
    long delay = apnContext.getDelayForNextApn(mFailFast);
    // Check if we need to retry or not.
    if (delay >= 0) {
        if (DBG) log("onDataSetupCompleteError: Try next APN. delay = " + delay);
        apnContext.setState(DctConstants.State.SCANNING);
        // Wait a bit before trying the next APN, so that
        // we're not tying up the RIL command channel
        startAlarmForReconnect(delay, apnContext);
    } else {    
        // If we are not going to retry any APN, set this APN context to failed state.
        // This would be the final state of a data connection.
        apnContext.setState(DctConstants.State.FAILED);
        mPhone.notifyDataConnection(Phone.REASON_APN_FAILED, apnContext.getApnType());
        apnContext.setDataConnectionAc(null);
        log("onDataSetupCompleteError: Stop retrying APNs.");
    }
}   
根据delay设置定时器发起重连,重新SETUP_DATA_CALL
private void startAlarmForReconnect(long delay, ApnContext apnContext) {
    Intent intent = new Intent(INTENT_RECONNECT_ALARM + "." + apnType);
    ......
    mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
            SystemClock.elapsedRealtime() + delay, alarmIntent);        
}
private void onActionIntentReconnectAlarm(Intent intent) {
    ApnContext apnContext = mApnContexts.get(apnType);
    ......
    sendMessage(obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA, apnContext));
    apnContext.setReconnectIntent(null);        
}


②.EVENT_DISCONNECT_DONE数据连接断开后,尝试重新连接。

无论什么原因引起的数据连接断开,在执行EVENT_DISCONNECT_DONE事件后,都会取调用onDisconnectDone()方法并在这里判断是否应该尝试重新建立数据连接,重连的条件为:
mAttached.get() :当前仍然为attach状态
apnContext.isReady() :apnContext仍然为激活状态
retryAfterDisconnected(apnContext) :radio没有关闭
getRetryAfterDisconnectDelay() : 重连的时间间隔
startAlarmForReconnect() : 发起重连
onDisconnectDone()的具体代码为:

    /**
     * Called when EVENT_DISCONNECT_DONE is received.
     */
    protected void onDisconnectDone(AsyncResult ar) {
        ApnContext apnContext = getValidApnContext(ar, "onDisconnectDone");
        if (apnContext == null) return;

        if(DBG) log("onDisconnectDone: EVENT_DISCONNECT_DONE apnContext=" + apnContext);
        apnContext.setState(DctConstants.State.IDLE);

        mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
        // if all data connection are gone, check whether Airplane mode request was
        // pending.
        if (isDisconnected()) {
            if (mPhone.getServiceStateTracker().processPendingRadioPowerOffAfterDataOff()) {
                if (DBG) log("onDisconnectDone: radio will be turned off, no retries");
                // Radio will be turned off. No need to retry data setup
                apnContext.setApnSetting(null);
                apnContext.setDataConnectionAc(null);

                // Need to notify disconnect as well, in the case of switching Airplane mode.
                // Otherwise, it would cause 30s delayed to turn on Airplane mode.
                if (mDisconnectPendingCount > 0) {
                    mDisconnectPendingCount--;
                }

                if (mDisconnectPendingCount == 0) {
                    notifyDataDisconnectComplete();
                    notifyAllDataDisconnected();
                }
                return;
            }
        }
        //判断是否符合重连条件,并设置重连的间隔事件
        // If APN is still enabled, try to bring it back up automatically
        if (mAttached.get() && apnContext.isReady() && retryAfterDisconnected(apnContext)) {
            try {
                SystemProperties.set(PUPPET_MASTER_RADIO_STRESS_TEST, "false");
            } catch (RuntimeException ex) {
                log("Failed to set PUPPET_MASTER_RADIO_STRESS_TEST to false");
            }
            // Wait a bit before trying the next APN, so that
            // we're not tying up the RIL command channel.
            // This also helps in any external dependency to turn off the context.
            if (DBG) log("onDisconnectDone: attached, ready and retry after disconnect");
            long delay = apnContext.getRetryAfterDisconnectDelay();
            /// M: modify delay based on apn type @{
            delay = mtkModifyInterApnDelay(delay, apnContext.getApnType());
            /// @}
            if (delay > 0) {
                // Data connection is in IDLE state, so when we reconnect later, we'll rebuild
                // the waiting APN list, which will also reset/reconfigure the retry manager.
                startAlarmForReconnect(delay, apnContext);
            }
        } else {
            boolean restartRadioAfterProvisioning = mPhone.getContext().getResources().getBoolean(
                    com.android.internal.R.bool.config_restartRadioAfterProvisioning);

            if (apnContext.isProvisioningApn() && restartRadioAfterProvisioning) {
                log("onDisconnectDone: restartRadio after provisioning");
                restartRadio();
            }
            apnContext.setApnSetting(null);
            apnContext.setDataConnectionAc(null);
            if (isOnlySingleDcAllowed(mPhone.getServiceState().getRilDataRadioTechnology())) {
                if(DBG) log("onDisconnectDone: isOnlySigneDcAllowed true so setup single apn");
                setupDataOnConnectableApns(Phone.REASON_SINGLE_PDN_ARBITRATION);
            } else {
                if(DBG) log("onDisconnectDone: not retrying");
            }
        }                                

以上,如果modem侧attach成功后,因其他原因导致attach失败,但是很快又重新attach,那么在framework层,重新发起数据连接请求有个delay 的时间间隔,是正常现象。

DEBUG:

03-22 15:36:20.864416   975  1002 I AT      : [0] AT< +CGEV: NW PDN DEACT 0 (RIL_URC_READER, tid:493510243568)
03-22 15:36:21.086272  2882  3211 D MtkDC-1 : DcActiveState EVENT_LOST_CONNECTION dc={MtkDC-1: State=MtkDcActiveState mApnSetting=[ApnSettingV3] 中国联通 3g 网络 (China Unicom), 1498, 46001, 3gnet, , , , , , -1, default | supl, IP,
03-22 15:36:21.091562  2882  3211 D MtkDC-1 : DcInactiveState: enter notifyAllDisconnectCompleted failCause=LOST_CONNECTION
03-22 15:36:31.234433  2882  3211 D MtkDC-1 : DcInactiveState: mag.what=EVENT_CONNECT
03-22 15:36:31.247292  2882  3211 D RILJ    : [5643]> SETUP_DATA_CALL,radioTechnology=14,isRoaming=false,allowRoaming=false,DataProfile=0/3gnet/IP/0///0/0/0/0/true/5/IP/0/0///false [SUB0]
03-22 15:36:31.281095   975  1014 I AT      : [0] AT< +CGEV: ME PDN ACT 0 (RIL_CMD_READER_4, tid:493498844400)
03-22 15:36:31.315452  2882  3155 D RILJ    : [5643]< SETUP_DATA_CALL DataCallResponse: { status=0 retry=-1 cid=201 active=2 type=IP ifname=ccmni1 mtu=1500 addresses=[10.171.55.53] dnses=[112.65.184.255,210.22.84.3] gateways=[10.171.55.53] pcscf=[]} rat=1 [SUB0]


③.隔一段时间收不到数据包后触发doRecover机制

SETUP_DATA_CALL成功后,在DcTracker.java的completeConnection()中会启动一个线程,周期性读取底层文件,判断一个时间段内手机的收发包情况,如果隔一段时间收不到数据包,将会触发doRecover机制。
这里写图片描述
startDataStallAlarm()定时器:
定时时间:

private static final int DATA_STALL_ALARM_NON_AGGRESSIVE_DELAY_IN_MS_DEFAULT = 1000 * 60 * 6;
private static final int DATA_STALL_ALARM_AGGRESSIVE_DELAY_IN_MS_DEFAULT = 1000 * 60;//亮屏

TX/RX数据:
TX/RX数据由TrafficStats提供的静态方法获得,是native层方法统计所有Mobile的iface后返回的数据

TX/RX数据:
TX/RX数据由TrafficStats提供的静态方法获得,是native层方法统计所有Mobile的iface后返回的数据

TX/RX数据:
TX/RX数据由TrafficStats提供的静态方法获得,是native层方法统计所有Mobile的iface后返回的数据
onDataStallAlarm()调度:
定义TX最大值为10,如果TX超过10个,期间没有收到一个RX,则发起doRecovery():

private static final int NUMBER_SENT_PACKETS_OF_HANG = 10;
if (mSentSinceLastRecv >= hangWatchdogTrigger) {
suspectedStall = DATA_STALL_SUSPECTED;
sendMessage(obtainMessage(DctConstants.EVENT_DO_RECOVERY));
}

正常情况下,TX/RX都不为零,AP每隔一定时间向modem发起GET_DATA_CALL_LIST查询相关信息。如果TX/RX异常,则依次循环以下的case事件(startDataStallAlarm是循环的),clean所有的旧连接,重新注册网络,重启radio,直到TX/RX正常。

private void doRecovery() {
    if (getOverallState() == DctConstants.State.CONNECTED) {
        int recoveryAction = getRecoveryAction();
        switch (recoveryAction) {
        case RecoveryAction.GET_DATA_CALL_LIST:
            mPhone.mCi.getDataCallList(obtainMessage(DctConstants.EVENT_DATA_STATE_CHANGED));
            putRecoveryAction(RecoveryAction.CLEANUP);
            break;   
        case RecoveryAction.CLEANUP:
            cleanUpAllConnections(Phone.REASON_PDP_RESET);
            putRecoveryAction(RecoveryAction.REREGISTER);
            break;   
        case RecoveryAction.REREGISTER:
             mPhone.getServiceStateTracker().reRegisterNetwork(null); // AOSP
             putRecoveryAction(RecoveryAction.RADIO_RESTART);
             break;   
        case RecoveryAction.RADIO_RESTART:
             putRecoveryAction(RecoveryAction.RADIO_RESTART_WITH_PROP);
             restartRadio();
             break;   
        case RecoveryAction.RADIO_RESTART_WITH_PROP:
             restartRadio();
             putRecoveryAction(RecoveryAction.GET_DATA_CALL_LIST);
             break;
    }
}                                              

这里写图片描述

由此可见,这里的数据重连也是依据于onDisconnectDone()方法实现,与第二种数据重连的实现方式是一样的,只不过是其触发的原理不一样。

log打印:

DCT     : [0]updateDataStallInfo: OUT sent=46 mSentSinceLastRecv=46
DCT     : [0]onDataStallAlarm: tag=169604 do recovery action=1
DCT     : [0]doRecovery() cleanup all connections
DCT     : [0]cleanUpAllConnections: tearDown=true reason=pdpReset
DCT     : [0]cleanUpConnection: X tearDown=true reason=pdpReset apnContext={mApnType=default mState=DISCONNECTING mWaitingApns={[[ApnSettingV3] 中国联通 3g 网络 (China Unicom), 1525, 46001, 3gnet, , , , , , -1, default | supl | net, IP, IP, true, 0, 0, 0, false, 0, 0, 0, 0, , , false, 0]} mApnSetting={[ApnSettingV3] 中国联通 3g 网络 (China Unicom), 1525, 46001, 3gnet, , , , , , -1, default | supl | net, IP, IP, true, 0, 0, 0, false, 0, 0, 0, 0, , , false, 0} mReason=pdpReset mDataEnabled=true mDependencyMet=true mWifiApns={[]}} dcac=DC-2
DC-2    : tearDownData radio is on, call deactiv
DCT     : [0]onDisconnectDone: EVENT_DISCONNECT_DONE apnContext={mApnType=default mState=DISCONNECTING mWaitingApns={[[ApnSettingV3] 中国联通 3g 网络 (China Unicom), 1525, 46001, 3gnet, , , , , , -1, default | supl | net, IP, IP, true, 0, 0, 0, false, 0, 0, 0, 0, , , false, 0]} mApnSetting={[ApnSettingV3] 中国联通 3g 网络 (China Unicom), 1525, 46001, 3gnet, , , , , , -1, default | supl | net, IP, IP, true, 0, 0, 0, false, 0, 0, 0, 0, , , false, 0} mReason=pdpReset mDataEnabled=true mDependencyMet=true mWifiA
DCT     : [0]startAlarmForReconnect: delay=20000 action=com.android.internal.telephony.data-reconnect.default apn={mApnType=default mState=IDLE mWaitingApns={[[ApnSettingV3] 中国联通 3g 网络 (China Unicom), 1525, 46001, 3gnet, , , , , , -1, default | supl | net, IP, IP, true, 0, 0, 0, false, 0, 0, 0, 0, , , false, 0]} mApnSetting={[ApnSettingV3] 中国联通 3g 网络 (China Unicom), 1525, 46001, 3gnet, , , , , , -1, default | supl | net, IP, IP, true, 0, 0, 0, false, 0, 0, 0, 0, , , false, 0} mReason=pdpReset mDataEnabled=true mDependencyMet=true mWifiA
DCT     : [0]trySetupData for type:default due to pdpReset, mIsPsRestricted=false


③.pdp链路中断

DcController.java通过监听RIL_UNSOL_DATA_CALL_LIST_CHANGED消息来更新链路信息:

            mPhone.mCi.registerForDataCallListChanged(getHandler(),
                    DataConnection.EVENT_DATA_STATE_CHANGED, null);

这里写图片描述
SetupDataCallResult是hardware层用来保存链路信息的模板类,在framework层,我们使用DataCallResponse模板类来保存链路信息,因此需要转换。

RadioIndication.java

    public void dataCallListChanged(int indicationType, ArrayList<SetupDataCallResult> dcList) {
        mRil.processIndication(indicationType);

        ArrayList<DataCallResponse> response = new ArrayList<>();

        for (SetupDataCallResult dcResult : dcList) {
            response.add(RIL.convertDataCallResult(dcResult));
        }

        if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_DATA_CALL_LIST_CHANGED, response);

        mRil.mDataCallListChangedRegistrants.notifyRegistrants(
                new AsyncResult(null, response, null));
    }

DcController.java
onDataStateChanged():检查链路信息是否有更新,如果有更新,向DcTracker.java发送EVENT_LOST_CONNECTION消息,尝试重新建立连接,其数据重连过程也是在DcTracker.java的onDisconnectDone()方法中实现,跟前两种数据重连原理相同。

        protected void onDataStateChanged(ArrayList<DataCallResponse> dcsList) {
            if (DBG) {
                lr("onDataStateChanged: dcsList=" + dcsList
                        + " mDcListActiveByCid=" + mDcListActiveByCid);
            }
            if (VDBG) {
                log("onDataStateChanged: mDcListAll=" + mDcListAll);
            }
            //把RIL层上报的链路信息由ArrayList转换成HashMap保存
            // Create hashmap of cid to DataCallResponse
            HashMap<Integer, DataCallResponse> dataCallResponseListByCid =
                    new HashMap<Integer, DataCallResponse>();
            for (DataCallResponse dcs : dcsList) {
                dataCallResponseListByCid.put(dcs.cid, dcs);
            }
            //如果RIL上报的dcsList在已保存过的Active 列表中找不到,说明这个链路信息是新的,加入的dcsToRetry列表中,尝试建立链接
            // Add a DC that is active but not in the
            // dcsList to the list of DC's to retry
            ArrayList<DataConnection> dcsToRetry = new ArrayList<DataConnection>();
            for (DataConnection dc : mDcListActiveByCid.values()) {
                if (dataCallResponseListByCid.get(dc.mCid) == null) {
                    if (DBG) log("onDataStateChanged: add to retry dc=" + dc);
                    dcsToRetry.add(dc);
                }
            }
            if (DBG) log("onDataStateChanged: dcsToRetry=" + dcsToRetry);

            // Find which connections have changed state and send a notification or cleanup
            // and any that are in active need to be retried.
            ArrayList<ApnContext> apnsToCleanup = new ArrayList<ApnContext>();

            boolean isAnyDataCallDormant = false;
            boolean isAnyDataCallActive = false;
            //循环RIL上报的链路信息dcsList
            for (DataCallResponse newState : dcsList) {
                //链路信息是新的,在上面我们已经加入到dcsToRetry列表,处理过了,所以这里要跳过。
                DataConnection dc = mDcListActiveByCid.get(newState.cid);
                if (dc == null) {
                    // UNSOL_DATA_CALL_LIST_CHANGED arrived before SETUP_DATA_CALL completed.
                    loge("onDataStateChanged: no associated DC yet, ignore");
                    continue;
                }
                //此链路信息没有对应的apn,跳过
                if (dc.mApnContexts.size() == 0) {
                    if (DBG) loge("onDataStateChanged: no connected apns, ignore");
                } else {
                    // Determine if the connection/apnContext should be cleaned up
                    // or just a notification should be sent out.
                    if (DBG) log("onDataStateChanged: Found ConnId=" + newState.cid
                            + " newState=" + newState.toString());
                    //如果此链路信息是非激活状态,非attach状态和且非DcFailCause.SIGNAL_LOST状态,加入到apnsToCleanup列表中,需要清除此链路连接。
                    //否则,加入到dcsToRetry列表中,尝试建立此链路连接
                    if (newState.active == DATA_CONNECTION_ACTIVE_PH_LINK_INACTIVE) {
                        if (mDct.isCleanupRequired.get()) {
                            apnsToCleanup.addAll(dc.mApnContexts.keySet());
                            mDct.isCleanupRequired.set(false);
                        } else {
                            DcFailCause failCause = DcFailCause.fromInt(newState.status);
                            if (failCause.isRestartRadioFail(mPhone.getContext(),
                                        mPhone.getSubId())) {
                                if (DBG) {
                                    log("onDataStateChanged: X restart radio, failCause="
                                            + failCause);
                                }
                                mDct.sendRestartRadio();
                            } else if (mDct.isPermanentFailure(failCause)) {
                                if (DBG) {
                                    log("onDataStateChanged: inactive, add to cleanup list. "
                                            + "failCause=" + failCause);
                                }
                                apnsToCleanup.addAll(dc.mApnContexts.keySet());
                            } else {
                                if (DBG) {
                                    log("onDataStateChanged: inactive, add to retry list. "
                                            + "failCause=" + failCause);
                                }
                                dcsToRetry.add(dc);
                            }
                        }
                    //如果链路信息发生了变化,比如Interface name变化;Interface name相同但是Dnses,Routes,HttpProxy,Addresses不同,
                    //需要检测之前使用的LinkAddress和此链路信息的LinkAddress是否相同,如果相同,则此链路信息加入到apnsToCleanup中,等待清除链路连接
                    } else {
                        // Its active so update the DataConnections link properties
                        UpdateLinkPropertyResult result = dc.updateLinkProperty(newState);
                        if (result.oldLp.equals(result.newLp)) {
                            if (DBG) log("onDataStateChanged: no change");
                        } else {
                            if (result.oldLp.isIdenticalInterfaceName(result.newLp)) {
                                if (! result.oldLp.isIdenticalDnses(result.newLp) ||
                                        ! result.oldLp.isIdenticalRoutes(result.newLp) ||
                                        ! result.oldLp.isIdenticalHttpProxy(result.newLp) ||
                                        ! result.oldLp.isIdenticalAddresses(result.newLp)) {
                                    // If the same address type was removed and
                                    // added we need to cleanup
                                    CompareResult<LinkAddress> car =
                                        result.oldLp.compareAddresses(result.newLp);
                                    if (DBG) {
                                        log("onDataStateChanged: oldLp=" + result.oldLp +
                                                " newLp=" + result.newLp + " car=" + car);
                                    }
                                    boolean needToClean = false;
                                    for (LinkAddress added : car.added) {
                                        for (LinkAddress removed : car.removed) {
                                            if (NetworkUtils.addressTypeMatches(
                                                    removed.getAddress(),
                                                    added.getAddress())) {
                                                needToClean = true;
                                                break;
                                            }
                                        }
                                    }
                                    if (needToClean) {
                                        if (DBG) {
                                            log("onDataStateChanged: addr change," +
                                                    " cleanup apns=" + dc.mApnContexts +
                                                    " oldLp=" + result.oldLp +
                                                    " newLp=" + result.newLp);
                                        }
                                        apnsToCleanup.addAll(dc.mApnContexts.keySet());
                                    } else {
                                        if (DBG) log("onDataStateChanged: simple change");

                                        for (ApnContext apnContext : dc.mApnContexts.keySet()) {
                                             mPhone.notifyDataConnection(
                                                 PhoneConstants.REASON_LINK_PROPERTIES_CHANGED,
                                                 apnContext.getApnType());
                                        }
                                    }
                                } else {
                                    if (DBG) {
                                        log("onDataStateChanged: no changes");
                                    }
                                }
                            } else {
                                apnsToCleanup.addAll(dc.mApnContexts.keySet());
                                if (DBG) {
                                    log("onDataStateChanged: interface change, cleanup apns="
                                            + dc.mApnContexts);
                                }
                            }
                        }
                    }
                }
                if (newState.active == DATA_CONNECTION_ACTIVE_PH_LINK_UP) {
                    isAnyDataCallActive = true;
                }
                if (newState.active == DATA_CONNECTION_ACTIVE_PH_LINK_DORMANT) {
                    isAnyDataCallDormant = true;
                }
            }

            if (isAnyDataCallDormant && !isAnyDataCallActive) {
                // There is no way to indicate link activity per APN right now. So
                // Link Activity will be considered dormant only when all data calls
                // are dormant.
                // If a single data call is in dormant state and none of the data
                // calls are active broadcast overall link state as dormant.
                if (DBG) {
                    log("onDataStateChanged: Data Activity updated to DORMANT. stopNetStatePoll");
                }
                mDct.sendStopNetStatPoll(DctConstants.Activity.DORMANT);
            } else {
                if (DBG) {
                    log("onDataStateChanged: Data Activity updated to NONE. " +
                            "isAnyDataCallActive = " + isAnyDataCallActive +
                            " isAnyDataCallDormant = " + isAnyDataCallDormant);
                }
                if (isAnyDataCallActive) {
                    mDct.sendStartNetStatPoll(DctConstants.Activity.NONE);
                }
            }

            if (DBG) {
                lr("onDataStateChanged: dcsToRetry=" + dcsToRetry
                        + " apnsToCleanup=" + apnsToCleanup);
            }
            //清除列表中的链路连接
            // Cleanup connections that have changed
            for (ApnContext apnContext : apnsToCleanup) {
               mDct.sendCleanUpConnection(true, apnContext);
            }
            //尝试连接列表中的链路连接
            // Retry connections that have disappeared
            for (DataConnection dc : dcsToRetry) {
                if (DBG) log("onDataStateChanged: send EVENT_LOST_CONNECTION dc.mTag=" + dc.mTag);
                dc.sendMessage(DataConnection.EVENT_LOST_CONNECTION, dc.mTag);
            }

            if (VDBG) log("onDataStateChanged: X");
        }
    }                                                                                                                                                                                                                                                                                   

二.DataConnection

DcTracker与DataConnection是通过DcAsyncChannel来通讯的,AsyncChannel的工作原理可参考:AsyncChannel的工作机制
DataConnection是一个状态机,新创建的对象,其状态初始化在DcDefaultState,初始化结束后运行在DcInactiveState状态,因此从DcAsyncChannel发出bringUp()拨号后,会在DcInactiveState处理DataConnection.EVENT_CONNECT事件。

ConnectionParams cp = (ConnectionParams) msg.obj;
if (initConnection(cp)) {
    onConnect(mConnectionParams);
    transitionTo(mActivatingState);
} else {
    if (DBG) {
        log("DcInactiveState: msg.what=EVENT_CONNECT initConnection failed");
    }
    notifyConnectCompleted(cp, DcFailCause.UNACCEPTABLE_NETWORK_PARAMETER,
            false);
}

在onConnect()中调用RIL中的setupDataCall()发起下一步的拨号动作。

mPhone.mCi.setupDataCall(
        cp.mRilRat,
        cp.mProfileId,
        mApnSetting.apn, mApnSetting.user, mApnSetting.password,
        authType,
        protocol, mId+1, msg);

RIL中拨号过程处理结束,会发出EVENT_SETUP_DATA_CONNECTION_DONE消息通知DataConnection。注意,处理EVENT_SETUP_DATA_CONNECTION_DONE消息在DataConnection中的DcActivatingState状态中。这里的关键处理有两个方面:
1.请求网络成功,把RIL返回的结果保存到LinkProperties中,并赋值给NetworkAgent。
2.请求网络失败,根据失败的原因决定是否发起EVENT_FALLBACK_RETRY_CONNECTION事件消息,重新连接onConnect(mConnectionParams)。

DataCallResponse.SetupResult result = onSetupConnectionCompleted(ar);
result = updateLinkProperty(response).setupResult;
public UpdateLinkPropertyResult updateLinkProperty(DataCallResponse newState) {
    UpdateLinkPropertyResult result = new UpdateLinkPropertyResult(mLinkProperties);

    if (newState == null) return result;
    DataCallResponse.SetupResult setupResult;
    result.newLp = new LinkProperties();
    // set link properties based on data call response
    result.setupResult = setLinkProperties(newState, result.newLp);
    if (result.setupResult != DataCallResponse.SetupResult.SUCCESS) {
        if (DBG) log("updateLinkProperty failed : " + result.setupResult);
        return result;
    }
    // copy HTTP proxy as it is not part DataCallResponse.
    result.newLp.setHttpProxy(mLinkProperties.getHttpProxy());
    checkSetMtu(mApnSetting, result.newLp);
    mLinkProperties = result.newLp;
    updateTcpBufferSizes(mRilRat);
    if (result.newLp.equals(result.oldLp) == false &&
            mNetworkAgent != null) {
        mNetworkAgent.sendLinkProperties(mLinkProperties);
    }
    return result;
}
mLinkProperties.setTcpBufferSizes(sizes);

DcActivatingState状态在处理EVENT_SETUP_DATA_CONNECTION_DONE消息的时候,并没有返回retVal = HANDLED,而是直接return,表明EVENT_SETUP_DATA_CONNECTION_DONE消息只是在DcActivatingState状态中溜达了一圈,最后还需要抛出此消息,让其他状态处理。因为执行了transitionTo(mActivatingState),说明下一个状态是ActivatingState,那EVENT_SETUP_DATA_CONNECTION_DONE还需要在ActivatingState中处理。在切换到ActivatingState的时候,需要先执行enter()方法,其实,码执行到这里,说明拨号已经成功,相关的ip信息已经获取(SETUP_DATA_CALL),剩下的工作就是设置ip相关信息并通知。其关键代码为:

notifyAllOfConnected(Phone.REASON_CONNECTED);
if (mIsInVoiceCall && !mIsSupportConcurrent) {
    mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.SUSPENDED,
            mNetworkInfo.getReason(), null);
} else {
    mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED,
            mNetworkInfo.getReason(), null);
}
mNetworkInfo.setExtraInfo(mApnSetting.apn);
if (createNetworkAgent) {
    mNetworkAgent = new DcNetworkAgent(getHandler().getLooper(), mPhone.getContext(),
            "DcNetworkAgent", mNetworkInfo, makeNetworkCapabilities(), mLinkProperties,
            50, misc);
}

通知DcTracker的过程,DcTracker将会接收EVENT_DATA_SETUP_COMPLETE消息,并调用onDataSetupComplete()处理。

private void notifyAllOfConnected(String reason) {
    notifyAllWithEvent(null, DctConstants.EVENT_DATA_SETUP_COMPLETE, reason);
}
private void notifyAllWithEvent(ApnContext alreadySent, int event, String reason) {
    mNetworkInfo.setDetailedState(mNetworkInfo.getDetailedState(), reason,
            mNetworkInfo.getExtraInfo());
    for (ConnectionParams cp : mApnContexts.values()) {
        ApnContext apnContext = cp.mApnContext;
        if (apnContext == alreadySent) continue;
        if (reason != null) apnContext.setReason(reason);
        Pair<ApnContext, Integer> pair =
                new Pair<ApnContext, Integer>(apnContext, cp.mConnectionGeneration);
        Message msg = mDct.obtainMessage(event, pair);
        AsyncResult.forMessage(msg);
        msg.sendToTarget();
    }
}

接下来ActivatingState继续处理EVENT_SETUP_DATA_CONNECTION_DONE消息,以下都是只贴出部分代码:

case EVENT_SETUP_DATA_CONNECTION_DONE: {
    AsyncResult ar = (AsyncResult) msg.obj;
    ConnectionParams cp = (ConnectionParams) ar.userObj;
    DataCallResponse.SetupResult result = onSetupConnectionCompleted(ar);
    switch (result) {
        case SUCCESS:
            // All is well
            mDcFailCause = DcFailCause.NONE;
            resetRetryCount();
            break;

private DataCallResponse.SetupResult onSetupConnectionCompleted(AsyncResult ar) {
    DataCallResponse response = (DataCallResponse) ar.result;
    ConnectionParams cp = (ConnectionParams) ar.userObj;
    DataCallResponse.SetupResult result;
        mCid = response.cid;
        // M: VDF MMS over ePDG @{
        mRat = response.rat;
        /// @}
        mPcscfAddr = response.pcscf;

        result = updateLinkProperty(response).setupResult;
        // M: IPv6 RA update
        mInterfaceName = response.ifname;
        log("onSetupConnectionCompleted: ifname-" + mInterfaceName);

主要是把相关网络连接属性保存到LinkProperties中,并设置到mNetworkAgent中。

public UpdateLinkPropertyResult updateLinkProperty(DataCallResponse newState) {
    if (result.newLp.equals(result.oldLp) == false &&
        mNetworkAgent != null) {
        mNetworkAgent.sendLinkProperties(mLinkProperties);
    }
}

processMessage()处理完EVENT_SETUP_DATA_CONNECTION_DONE消息,将会执行exit()方法:设置NetworkInfo中的状态。

mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED,
        reason, mNetworkInfo.getExtraInfo());
if (mNetworkAgent != null) {
    mNetworkAgent.sendNetworkInfo(mNetworkInfo);
    mNetworkAgent = null;
}

经过以上ActivatingState状态的处理,此时拨号所获得的网络连接属性已经保存到NetworkAgent中。
mNetworkAgent.sendLinkProperties(mLinkProperties);
发出EVENT_NETWORK_PROPERTIES_CHANGED消息,通知ConnectivityService更新信息:updateLinkProperties(),而LinkProperties中的属性最后会经由NetworkManagementService
设置到底层(硬件),由此流程就到了netd层:

updateInterfaces(newLp, oldLp, netId);
updateMtu(newLp, oldLp);
updateRoutes(newLp, oldLp, netId);
updateDnses(newLp, oldLp, netId);

mNetworkAgent.sendNetworkInfo(mNetworkInfo);

发出EVENT_NETWORK_INFO_CHANGED消息,通知ConnectivityService更新信息:updateNetworkInfo()

三.RIL层的AT指令以及modem相关的信令

1.AT指令

以下三个AT指令分别是:IP地址,绑定端口,DNS地址,激活pdp上下文

01-01 12:02:07.843088  1556  1863 D RILJ    : [4115]> SETUP_DATA_CALL 14 0 3gnet   0 IP 1 [SUB0]
01-01 12:02:07.927239   947   971 I AT      : AT> AT+CGPADDR=0 (RIL_CMD_READER_4, tid:513820099664)
01-01 12:02:07.935767   947   986 I AT      : AT< +CGPADDR: 0, "172.19.168.176" (RIL_CMD_READER_4, tid:513820099664)
01-01 12:02:07.958998   947   971 I AT      : AT> AT+CGDATA="M-CCMNI",0,1 (RIL_CMD_READER_4, tid:513820099664)
01-01 12:02:07.967568   947   986 I AT      : AT< OK (RIL_CMD_READER_4, tid:513820099664)
01-01 12:02:07.978575   947   971 I AT      : AT> AT+CGPRCO? (RIL_CMD_READER_4, tid:513820099664)
01-01 12:02:07.981523   947   986 I AT      : AT< +CGPRCO: 0, "112.65.184.255", "210.22.84.3", "", "", 0 (RIL_CMD_READER_4, tid:513820099664)
01-01 12:00:56.736339   799   826 I AT      : AT> AT+CGACT? (RIL_CMD_READER_4, tid:531706238032)
01-01 12:00:56.746569   799   842 I AT      : AT< +CGACT: 1, 1 (RIL_CMD_READER_4, tid:531706238032)
01-01 12:02:08.053888  1556  1819 D RILJ    : [4115]< SETUP_DATA_CALL DataCallResponse: {version=11 status=0 retry=0 cid=0 active=2 type=IP ifname=ccmni0 mtu=0 rat=1 addresses=[172.19.168.176] dnses=[112.65.184.255,210.22.84.3] gateways=[172.19.168.176] pcscf=[]} [SUB0]

2.modem信令

2G:modem直接发起PDP REQUES信令

SM__ACTIVATE_PDP_CONTEXT_REQUEST
SM__ACTIVATE_PDP_CONTEXT_ACCEPT

3G: modem需要先发起SERVICE REQUEST,等SECURITY_MODE_COMPLETE完成才会发起 PDP REQUES信令:

GMM__SERVICE_REQUEST
SECURITY_MODE_COMPLETE
SM__ACTIVATE_PDP_CONTEXT_REQUEST
SM__ACTIVATE_PDP_CONTEXT_ACCEPT

4G: LTE引入了默认承载的概念,为实现”永久在线”,提高用户体验和保证信息的实时性,在Attached的时候就伴随着pdn连接的建立。Attached完成时建立默认承载:

ESM_MSG_PDN_CONNECTIVITY_REQUEST
EMM_Attach_Request
EMM_Attach_Accept
ESM_MSG_ACTIVATE_DEFAULT_EPS_BEARER_CONTEXT_REQUEST
ESM_MSG_ACTIVATE_DEFAULT_EPS_BEARER_CONTEXT_ACCEPT 
  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值