路由配置信息的获取有多种方式,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主要负责处理APN参数,数据重连机制,通知上层等工作。
这里分为两个小节分析
1.处理apn参数
(1).初始化ApnContext
(2).激活ApnContext
(3).筛选apn
(4).发起请求
2.请求网络的结果处理
(1).网络请求成功
(2).数据重连
(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类型为例:
"中国联通 3g 网络 (China Unicom)"
mcc="460"
mnc="01"
apn="3gnet"
type="default,supl"
/>
"中国联通 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, generation);
dcac.bringUp(apnContext, profileId, radioTech, msg, generation);
(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 dcList) {
mRil.processIndication(indicationType);
ArrayList 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 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 dataCallResponseListByCid =
new HashMap();
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 dcsToRetry = new ArrayList();
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 apnsToCleanup = new ArrayList();
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 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");
}
}
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 pair =
new Pair(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()
以下三个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]
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