PDP重试机制探讨与优化
Android Telephony Data Call Lost And Retry 1、Android Telephony PDP连接断开分为以下三种情况:
1、bringUp call建立失败;
2、Modem上报UNSOL_DATA_CALL_LIST_CHANGED的处理;
3、规定时间内没有上下行数据,或只有上行数据包且超过一定的包没有得到服务器ACK。
2、bringUp失败
2、1 DataConnection处理bringUp call
DataConnection在setupData的时候会调用如下操作去bringUp发起激活数据连接
Message msg = obtainMessage();
msg.what = DctConstants.EVENT_DATA_SETUP_COMPLETE;
....
dcac.bringUp(apnContext, profileId, radioTech, msg, generation);
往后会一直调到setupDataCall并向RIL发起SETUP_DATA_CALL
DataConnection在收到SETUP_DATA_CALL结果后,用Message
通知
关于发布提成方案的通知关于xx通知关于成立公司筹建组的通知关于红头文件的使用公开通知关于计发全勤奖的通知
DcTracker处理: /*
*A SETUP (aka bringUp) has completed, possibly with an error. If
* there is an error this method will call {@link #onDataSetupCompleteError}.
*/
private void onDataSetupComplete(AsyncResult ar) {
ApnContext apnContext = getValidApnContext(ar, "onDataSetupComplete");
if (apnContext == null) return;//如果APN为空,直接返回
if (ar.exception == null) {
//没有异常,
表
关于同志近三年现实表现材料材料类招标技术评分表图表与交易pdf视力表打印pdf用图表说话 pdf
示连接成功
......
} else {
//连接失败,获取失败原因
cause = (DcFailCause) (ar.result);
......
if (cause.isRestartRadioFail() || apnContext.restartOnError(cause.getErrorCode())) { sendRestartRadio();
}
// If the data call failure cause is a permanent failure, we mark the APN as permanent failed
if (isPermanentFail(cause)) {
apnContext.markApnPermanentFailed(apn);
}
handleError = true;
}
if (handleError) {
//处理失败的情况
onDataSetupCompleteError(ar);
}
if (!mDataEnabledSettings.isInternalDataEnabled()) {//数据开关未打卡
cleanUpAllConnections(Phone.REASON_DATA_DISABLED);
}
}
2、2处理Error:
如果apnContext中的所有waiting APN都失败了,且不是每个都发生permanent fail(永久性错误),则设置delay并重新发起这次连接
如果apnContext中仍有没有尝试的waiting APN,则设置delay并尝试用下一个APN去连接private void onDataSetupCompleteError(AsyncResult ar) {
ApnContext apnContext = getValidApnContext(ar, "onDataSetupCompleteError");
//已经尝试过所有APN直接return
if (apnContext == null) return;
//获取next apn重试时间
long delay = apnContext.getDelayForNextApn(mFailFast);
// Check if we need to retry or not.
if (delay >= 0) {
apnContext.setState(DctConstants.State.SCANNING);
startAlarmForReconnect(delay, apnContext);
} else {
....
}
}
2.3设置重试定时器
private void startAlarmForReconnect(long delay, ApnContext apnContext) {
.....
PendingIntent alarmIntent = PendingIntent.getBroadcast(mPhone.getContext(), 0,
intent, PendingIntent.FLAG_UPDATE_CURRENT);
apnContext.setReconnectIntent(alarmIntent);//apn定时重试
//设置定时唤醒
mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime() + delay, alarmIntent);
}
这里重点关注定时器的时间:
long delay = apnContext.getDelayForNextApn(mFailFast);//SETUP_DATA_CALL获取时间处理下一个APN
/**
* Get the delay for trying the next APN setting if the current one failed.
* @param failFastEnabled True if fail fast mode enabled. In this case we'll use a shorter delay.
* @return The delay in milliseconds
*/
public long getDelayForNextApn(boolean failFastEnabled) {
return mRetryManager.getDelayForNextApn(failFastEnabled || isFastRetryReason());
}
RetryManager后续分析完之后统一分析。
2、4 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.
* getDataConnectionState() maps State to DataState
* FAILED or IDLE : DISCONNECTED
* RETRYING or CONNECTING or SCANNING: CONNECTING
* CONNECTED : CONNECTED or DISCONNECTING
*/
public enum State {
IDLE,
CONNECTING,
SCANNING,
CONNECTED,
DISCONNECTING,
FAILED,
RETRYING
}
3 RIL_UNSOL_DATA_CALL_LIST_CHANGED消息处理
3、1DcController处理
RIL_UNSOL_DATA_CALL_LIST_CHANGED
DcController监听RIL_UNSOL_DATA_CALL_LIST_CHANGED消息,获得每一个数据连接的更新:
mPhone.mCi.registerForDataNetworkStateChanged(getHandler(),
DataConnection.EVENT_DATA_STATE_CHANGED, null);
3、2RIL上报DATA_CALL_LIST_CHANGED时会带上当前的Modem中的DataCall list,DcController将此dataCall list和上
层的active list做对比:
已经丢失及断开的连接APN将会重试;
发生变化和发生永久错误的链接APN则需要清除。
private void onDataStateChanged(ArrayList
dcsList) {
...
//如果上报的dcsList中并没有找到对应的active的链接,则默认连接丢失并加入重试List
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);
}
}
....
for (DataCallResponse newState : dcsList) {
...
if (newState.active == DATA_CONNECTION_ACTIVE_PH_LINK_INACTIVE) { //连接INACTIVE,按照错误类型区分处理
DcFailCause failCause = DcFailCause.fromInt(newState.status);
if (failCause.isRestartRadioFail()) {
//恢复需要重启radio
mDct.sendRestartRadio();
} else if (mDct.isPermanentFail(failCause)) {
//链接发生不可恢复的错误,需要Cleanup
apnsToCleanup.addAll(dc.mApnContexts.keySet());
} else {
for (ApnContext apnContext : dc.mApnContexts.keySet()) {
if (apnContext.isEnabled()) {
//apn是enabled状态,重试
dcsToRetry.add(dc);
break;
} else {
//apn已经disabled,需要cleanup
apnsToCleanup.add(apnContext);
}
}
}
} else {
//LinkProperty发生变化
UpdateLinkPropertyResult result = dc.updateLinkProperty(newState);
......
if (needToClean) {
apnsToCleanup.addAll(dc.mApnContexts.keySet());
} else {
.......
} else {
//interface发生改变,cleanUp这个old connection
apnsToCleanup.addAll(dc.mApnContexts.keySet());
.....
}
}
//清除链接,有条件重试
for (ApnContext apnContext : apnsToCleanup) {
mDct.sendCleanUpConnection(true, apnContext);
}
//通知DataConnection链接丢失,并重试, DataConnection收到这个消息之后进入DcInactiveState状态,而后进入上面所说的那个处理流程
for (DataConnection dc : dcsToRetry) {
dc.sendMessage(DataConnection.EVENT_LOST_CONNECTION, dc.mTag);
}
}
}
3、3sendCleanUpConnection清除当前连接
在DcTracker里收到这个消息之后处理如下:
case DctConstants.EVENT_CLEAN_UP_CONNECTION:
boolean tearDown = (msg.arg1 == 0) ? false : true;
if (msg.obj instanceof ApnContext) {
cleanUpConnection(tearDown, (ApnContext)msg.obj);
} else {
onCleanUpConnection(tearDown, msg.arg2, (String) msg.obj);
}
break;
private void onCleanUpConnection(boolean tearDown, int apnId, String reason) {
if (DBG) log("onCleanUpConnection");
//根据apnid获取APN
ApnContext apnContext = mApnContextsById.get(apnId);
if (apnContext != null) {
apnContext.setReason(reason);
cleanUpConnection(tearDown, apnContext);
}
}
protected void cleanUpConnection(boolean tearDown, ApnContext apnContext) { ......
} else {
//如果连接仍然存在,就清除
// Connection is still there. Try to clean up.
......
Message msg = obtainMessage(DctConstants.EVENT_DISCONNECT_DONE, pair);//发送断开的消息
if (disconnectAll) {
apnContext.getDcAc().tearDownAll(apnContext.getReason(), msg);
} else {
apnContext.getDcAc()
.tearDown(apnContext, apnContext.getReason(), msg);
.....
处理EVENT_DISCONNECT_DONE消息
/**
* Called when EVENT_DISCONNECT_DONE is received.
*/
private void onDisconnectDone(AsyncResult ar) {
....
if (isDisconnected()) {
.....
}
** //~~If APN is still enabled, try to bring it back up automatically~~**
if (mAttached.get() && apnContext.isReady() && retryAfterDisconnected(apnContext)) {
// 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");
//获取重试时间,进入与2类似的重试环节
long delay = apnContext.getInterApnDelay(mFailFast);
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 {
.....正常断开处理流程
}
4规定时间内或指定上行包数量没有收到ACK引起的断网重连
4、1在Data完成连接后,DcTracker会定时检查TX/RX的更新,
如果RX的值持续没有更新并超过设置的上限值,就会触发
Recovery动作。
首先来看
方法
快递客服问题件处理详细方法山木方法pdf计算方法pdf华与华方法下载八字理论方法下载
onDataStallAlarm,它由Alarm定时触发,执行这些操作:
更新TX/RX数据->判断是否需要Recover并执行->重新设置Alarm来触发下一次检查。
protected void onDataStallAlarm(int tag) {
//更新mSentSinceLastRecv
updateDataStallInfo();
//默认值是10
int hangWatchdogTrigger = Settings.Global.getInt(mResolver,
Settings.Global.PDP_WATCHDOG_TRIGGER_PACKET_COUNT,
NUMBER_SENT_PACKETS_OF_HANG);
boolean suspectedStall = DATA_STALL_NOT_SUSPECTED;
if (mSentSinceLastRecv >= hangWatchdogTrigger) {
//一段时间没有RX,且超过watchdog的值,需要recover
suspectedStall = DATA_STALL_SUSPECTED;
sendMessage(obtainMessage(DctConstants.EVENT_DO_RECOVERY));
} else {
......
}
//重新设置Alarm任务,一段时间后再次执行本方法(onDataStallAlarm)
startDataStallAlarm(suspectedStall);
}
updateDataStallInfo()负责记数,处理分3种情况:
有TX也有RX ->正常,重置计数和Recovery action(Recovery action后面会写到)
有TX没有RX ->异常,累计TX数据
没有TX只有RX ->正常,重置计数和Recovery action
private void updateDataStallInfo() {
long sent, received;
TxRxSum preTxRxSum = new TxRxSum(mDataStallTxRxSum);
mDataStallTxRxSum.updateTxRxSum();
sent = mDataStallTxRxSum.txPkts - preTxRxSum.txPkts;
received = mDataStallTxRxSum.rxPkts - preTxRxSum.rxPkts;
//收发正常,RecoveryAction重置
if ( sent > 0 && received > 0 ) {
if (VDBG_STALL) log("updateDataStallInfo: IN/OUT");
mSentSinceLastRecv = 0;
putRecoveryAction(RecoveryAction.GET_DATA_CALL_LIST);
} else if (sent > 0 && received == 0) {
//没有RX;若不在通话状态则需要累计本次发送量
if (isPhoneStateIdle()) {
mSentSinceLastRecv += sent;
} else {
mSentSinceLastRecv = 0;
}
//没有发数据,RecoveryAction重置
} else if (sent == 0 && received > 0) {
if (VDBG_STALL) log("updateDataStallInfo: IN");
mSentSinceLastRecv = 0;
putRecoveryAction(RecoveryAction.GET_DATA_CALL_LIST);
} else {
if (VDBG_STALL) log("updateDataStallInfo: NONE");
}
}
TX/RX数据由TrafficStats提供的静态方法获得,是native层方法统计所有Mobile的iface后返回的数据:
public void updateTxRxSum() {
this.txPkts = TrafficStats.getMobileTcpTxPackets();
this.rxPkts = TrafficStats.getMobileTcpRxPackets();
}
4、2doRecovery方法如何执行恢复数据。
doRecovery方法中有5种不同的Recovery action对应着各自的处理:
向Modem主动查询DATA CALL LIST//会发起重试
清除现有的数据链接//会发起重试
重新驻网//直接脱网
重启Radio//相当于开启飞行模式
深度重启Radio(根据高通的注释,这个操作涉及到RIL的
设计
领导形象设计圆作业设计ao工艺污水处理厂设计附属工程施工组织设计清扫机器人结构设计
)//相当于重启modem
如果一种方法执行之后,连接依然有问题,则执行下一种恢复方法,顺序类似于循环链表,直到恢复正常后updateDataStallInfo()将Action重置:
protected void doRecovery() {
if (getOverallState() == DctConstants.State.CONNECTED) {
// Go through a series of recovery steps, each action transitions to the next action
int recoveryAction = getRecoveryAction();
switch (recoveryAction) {
case RecoveryAction.GET_DATA_CALL_LIST:
mPhone.mCi.getDataCallList(obtainMessage(DctConstants.EVENT_DATA_STATE_CHA NGED));
putRecoveryAction(RecoveryAction.CLEANUP);
break;
case RecoveryAction.CLEANUP:
cleanUpAllConnections(Phone.REASON_PDP_RESET);
putRecoveryAction(RecoveryAction.REREGISTER);
break;
case RecoveryAction.REREGISTER:
mPhone.getServiceStateTracker().reRegisterNetwork(null);
putRecoveryAction(RecoveryAction.RADIO_RESTART);
break;
case RecoveryAction.RADIO_RESTART:
putRecoveryAction(RecoveryAction.RADIO_RESTART_WITH_PROP);
restartRadio();
break;
case RecoveryAction.RADIO_RESTART_WITH_PROP:
// This is in case radio restart has not recovered the data.
// It will set an additional "gsm.radioreset" property to tell
// RIL or system to take further action.
// The implementation of hard reset recovery action is up to OEM product.
// Once RADIO_RESET property is consumed, it is expected to set back
// to false by RIL.
EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_RADIO_RESTART_ WITH_PROP, -1);
if (DBG) log("restarting radio with gsm.radioreset to true");
SystemProperties.set(RADIO_RESET_PROPERTY, "true");
// give 1 sec so property change can be notified.
try {
Thread.sleep(1000);
} catch (InterruptedException e) {}
restartRadio();
putRecoveryAction(RecoveryAction.GET_DATA_CALL_LIST);
break;
default:
throw new RuntimeException("doRecovery: Invalid recoveryAction=" +
recoveryAction);
}
mSentSinceLastRecv = 0;
}
}
5、RetryManager获取重试时间
5.1 getDelayForNextApn分析
/**
* Get the delay for trying the next waiting APN from the list.
* @param failFastEnabled True if fail fast mode enabled. In this case we'll use a shorter
* delay.
* @return delay in milliseconds
*/
public long getDelayForNextApn(boolean failFastEnabled) {
if (mWaitingApns == null || mWaitingApns.size() == 0) {
log("Waiting APN list is null or empty.");
//APN是空的,不做重试
return NO_RETRY;
}
if (mModemSuggestedDelay == NO_RETRY) {
log("Modem suggested not retrying.");
//底层建议不重试
return NO_RETRY;
}
if (mModemSuggestedDelay != NO_SUGGESTED_RETRY_DELAY &&
mSameApnRetryCount < MAX_SAME_APN_RETRY) {
// If the modem explicitly suggests a retry delay, we should use it, even in fail fast
//此部分是modem上报上来的,无法更改,具体上报的流程是发送RIL_ERROR的时候dataconnection直接获取的数据,此流程不往下深入
// mode.
log("Modem suggested retry in " + mModemSuggestedDelay + " ms.");
return mModemSuggestedDelay;
}
// In order to determine the delay to try next APN, we need to peek the next available APN.
// Case 1 - If we will start the next round of APN trying,
// we use the exponential-growth delay. (e.g. 5s, 10s, 30s...etc.)
// Case 2 - If we are still within the same round of APN trying,
// we use the fixed standard delay between APNs. (e.g. 20s)
int index = mCurrentApnIndex;
while (true) {
if (++index >= mWaitingApns.size()) index = 0;
// Stop if we find the non-failed APN.
if (mWaitingApns.get(index).permanentFailed == false) break;
// If we've already cycled through all the APNs, that means all APNs have
// permanently failed
if (index == mCurrentApnIndex) {
log("All APNs have permanently failed.");
return NO_RETRY;
}
long delay;
if (index <= mCurrentApnIndex) {
// Case 1, if the next APN is in the next round.
if (!mRetryForever && mRetryCount + 1 > mMaxRetryCount) {
log("Reached maximum retry count " + mMaxRetryCount + ".");
return NO_RETRY;
}
//如果下一个apn在下一轮,启动如下方式获取,分支一
delay = getRetryTimer();
++mRetryCount;
} else {
// Case 2, if the next APN is still in the same round.
//满足此条件,采用inter方式获取,分支二
delay = mInterApnDelay;
}
if (failFastEnabled && delay > mFailFastInterApnDelay) {
// If we enable fail fast mode, and the delay we got is longer than
// fail-fast delay (mFailFastInterApnDelay), use the fail-fast delay.
// If the delay we calculated is already shorter than fail-fast delay,
// then ignore fail-fast delay.
//如果快速失败按钮打开,则直接使用最快恢复的时间,分支三
delay = mFailFastInterApnDelay;
}
return delay;
}
5.1.1分析分支一:getRetryTimer
/**
* Return the timer that should be used to trigger the data reconnection
*/
private int getRetryTimer() {
int index;
if (mRetryCount < mRetryArray.size()) {
index = mRetryCount;
} else {
index = mRetryArray.size() - 1;
}
int retVal;
if ((index >= 0) && (index < mRetryArray.size())) {
retVal = mRetryArray.get(index).mDelayTime + nextRandomizationTime(index); } else {
retVal = 0;
if (DBG) log("getRetryTimer: " + retVal);
return retVal;
}
从代码中可以看出,时间是从mRetryArray这个list中取出的,跟踪代码知道这个list的初始化在configure(String configStr)这个函数中,而configureRetry它调用了这个函数,此函数的关键代码如下
CarrierConfigManager configManager = (CarrierConfigManager)
mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
PersistableBundle b = configManager.getConfigForSubId(mPhone.getSubId());
// Load all retry patterns for all different APNs.
String[] allConfigStrings = b.getStringArray(
CarrierConfigManager.KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS);如果不满足此条件,会进入默认的时间配置
configString = DEFAULT_DATA_RETRY_CONFIG;
到此,就明白了,原来所有的重试时间都是配置好的,先看默认的配置:
private static final String DEFAULT_DATA_RETRY_CONFIG = "max_retries=3, 5000, 5000, 5000";统一5s重试,很显然这个时间比较短。
再看系统配置的时间:
继续追查代码发现在CarrierConfigManager.java类里进行了时间的初始化
sDefaults.putStringArray(KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS, new String[]{
"default:default_randomization=2000,5000,10000,20000,40000,80000:5000,160000:5000,"
+ "320000:5000,640000:5000,1280000:5000,1800000:5000",
"mms:default_randomization=2000,5000,10000,20000,40000,80000:5000,160000:5000,"
+ "320000:5000,640000:5000,1280000:5000,1800000:5000",
"others:max_retries=3, 5000, 5000, 5000"});
然而这是一串奇怪的数字,具体如何重试,仍然不知所以。为此重新回到解析这串字符的算法 /**
* Configure for using string which allow arbitrary
* sequences of times. See class comments for the
* string format.
*
* @return true if successful
*/
private boolean configure(String configStr) {
// Strip quotes if present.
if ((configStr.startsWith("\"") && configStr.endsWith("\""))) {
configStr = configStr.substring(1, configStr.length() - 1);
}
// Reset the retry manager since delay, max retry count, etc...will be reset.
reset();
if (DBG) log("configure: '" + configStr + "'");
mConfig = configStr;
if (!TextUtils.isEmpty(configStr)) {
int defaultRandomization = 0;
if (VDBG) log("configure: not empty");
String strArray[] = configStr.split(",");
for (int i = 0; i < strArray.length; i++) {
if (VDBG) log("configure: strArray[" + i + "]='" + strArray[i] + "'");
Pair value;
String splitStr[] = strArray[i].split("=", 2);
splitStr[0] = splitStr[0].trim();
if (VDBG) log("configure: splitStr[0]='" + splitStr[0] + "'");
if (splitStr.length > 1) {
splitStr[1] = splitStr[1].trim();
if (VDBG) log("configure: splitStr[1]='" + splitStr[1] + "'");
if (TextUtils.equals(splitStr[0], "default_randomization")) {
value = parseNonNegativeInt(splitStr[0], splitStr[1]);
if (!value.first) return false;
defaultRandomization = value.second;
} else if (TextUtils.equals(splitStr[0], "max_retries")) {
if (TextUtils.equals("infinite", splitStr[1])) {
mRetryForever = true;
} else {
value = parseNonNegativeInt(splitStr[0], splitStr[1]);
if (!value.first) return false;
mMaxRetryCount = value.second;
}
} else {
Rlog.e(LOG_TAG, "Unrecognized configuration name value pair: "
+ strArray[i]);
return false;
}
} else {
/**
* Assume a retry time with an optional randomization value
* following a ":"
*/
splitStr = strArray[i].split(":", 2);
splitStr[0] = splitStr[0].trim();
RetryRec rr = new RetryRec(0, 0);
value = parseNonNegativeInt("delayTime", splitStr[0]);
if (!value.first) return false;
rr.mDelayTime = value.second;
// Check if optional randomization value present
if (splitStr.length > 1) {
splitStr[1] = splitStr[1].trim();
if (VDBG) log("configure: splitStr[1]='" + splitStr[1] + "'");
value = parseNonNegativeInt("randomizationTime", splitStr[1]);
if (!value.first) return false;
rr.mRandomizationTime = value.second;
} else {
rr.mRandomizationTime = defaultRandomization;
}
mRetryArray.add(rr);
}
}
if (mRetryArray.size() > mMaxRetryCount) {
mMaxRetryCount = mRetryArray.size();
if (VDBG) log("configure: setting mMaxRetryCount=" + mMaxRetryCount);
}
} else {
log("configure: cleared");
}
if (VDBG) log("configure: true");
return true;
}
为了更清晰的看到这串字符如何解析,我写了一个调试程序,模拟此算法,打开所有调试,结果如下:
configure:
'default_randomization=2000,5000,10000,20000,40000,80000:5000,160000:5000,320000:5000,64000 0:5000,1280000:5000,1800000:5000'
getRetryTimer: 5610
getRetryTimer: 11248
getRetryTimer: 21627
getRetryTimer: 40808
getRetryTimer: 80145
getRetryTimer: 163281
getRetryTimer: 323621
getRetryTimer: 642408
getRetryTimer: 1280380
getRetryTimer: 1802659
从中清晰看到,整个解析过程,从5s+-2s,10s+-5s…如此循环,游标每向右移动一格,时间就加大一个等级,最低5s最高1800s。
从抓回的log来看,重试时间的确也遵循了这个算法一次递减,但是递减的速度以及第一次重试的时间间隔都太短,不符合可穿戴式设备的要求,因此改动这个参数很有必要。
5.1.2 mInterApnDelay
分析过程如上所示,不同之处是他的系统配置值不一样
mInterApnDelay = b.getLong(
CarrierConfigManager.KEY_CARRIER_DATA_CALL_APN_DELAY_DEFAULT_LO NG,
DEFAULT_INTER_APN_DELAY);//如不配置,默认3s
sDefaults.putLong(KEY_CARRIER_DATA_CALL_APN_DELAY_DEFAULT_LONG, 20000);
固定20s,时间也比较短,可以斟酌是否需要改动。
5.1.3 mFailFastInterApnDelay
如mInterApnDelay相似,也是系统配置
mFailFastInterApnDelay = b.getLong(
CarrierConfigManager.KEY_CARRIER_DATA_CALL_APN_DELAY_FASTER_LON G,
DEFAULT_INTER_APN_DELAY_FOR_PROVISIONING);//如不配置,默认3s sDefaults.putLong(KEY_CARRIER_DATA_CALL_APN_DELAY_FASTER_LONG, 3000);
固定3s,时间太短。
5.2 getInterApnDelay
此函数实现如下:
/**
* Get the delay between APN setting trying. This is the fixed delay used for APN setting trying * within the same round, comparing to the exponential delay used for different rounds.
* @param failFastEnabled True if fail fast mode enabled, which a shorter delay will be used
* @return The delay in milliseconds
*/
public long getInterApnDelay(boolean failFastEnabled) {
return (failFastEnabled) ? mFailFastInterApnDelay : mInterApnDelay;
}
从中,我们得知,他的两个值mFailFastInterApnDelay、mInterApnDelay都已经做了较为详细的研究,故此不做分析。
5.3 ERR_RilError发生RIL错误的时候处理
在SETUP_DATA_CALL进行激活PDP连接的时候,会发生一个特殊错误ERR_RilError,处理如下
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);
cp.mApnContext.setModemSuggestedDelay(delay);
.....
break;
此时的重置时间有modem上报,从目前的log来看,modem返回的时间都是-2,因此都进入了正常的错误处理流程由onDataSetupCompleteError处理。
6总结
近期统计用户大数据断网重连的时候,发现在弱网环境下很容易失去PDP连接,而他在短时间内不断的重试,由于用户所处的网络环境异常的复杂,导致重试时间不断地被重置,基本在衰减到第三个等级的时候又重新来过,从上面的断开重连的分析得知前三个重试时间分别为5、10、20s这样本来已经衰减的很慢了然后又不断的重置,导致上层高频次的去请求建立PDP连接非常耗电,为此,我们针对此可以简单的做一个延长衰减时间的
方案
气瓶 现场处置方案 .pdf气瓶 现场处置方案 .doc见习基地管理方案.doc关于群访事件的化解方案建筑工地扬尘治理专项方案下载
,去掉快速重试机制,将重试间隔拉长。具体优化如下:
sDefaults.putStringArray(KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS, new String[]{
- "default:default_randomization=2000,5000,10000,20000,40000,80000:5000,160000:5000," - + "320000:5000,640000:5000,1280000:5000,1800000:5000",
+ "default:default_randomization=2000,60000,180000,360000,720000,1080000:5000,12000 00:5000,"
+ + "1800000:5000,2400000:5000,3000000:5000,3600000:5000",
"mms:default_randomization=2000,5000,10000,20000,40000,80000:5000,160000:5000,"
+ "320000:5000,640000:5000,1280000:5000,1800000:5000",
"others:max_retries=3, 5000, 5000, 5000"});
- sDefaults.putLong(KEY_CARRIER_DATA_CALL_APN_DELAY_DEFAULT_LONG, 20000);
+ sDefaults.putLong(KEY_CARRIER_DATA_CALL_APN_DELAY_DEFAULT_LONG, 200000);