为了正常的体验网站,请在浏览器设置里面开启Javascript功能!

PDP重试机制探讨与优化

2019-04-02 39页 doc 82KB 70阅读

用户头像

is_353097

暂无简介

举报
PDP重试机制探讨与优化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在s...
PDP重试机制探讨与优化
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通知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) { //没有异常,表示连接成功 ...... } 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动作。 首先来看方法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的)//相当于重启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连接非常耗电,为此,我们针对此可以简单的做一个延长衰减时间的,去掉快速重试机制,将重试间隔拉长。具体优化如下: 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);
/
本文档为【PDP重试机制探讨与优化】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。 本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。 网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。

历史搜索

    清空历史搜索