Add configurable data-retry.
Add RetryManager and change GsmDataConnectionTracker and CdmaDataConnectionTracker to use it. Add TelephonyUtilsTest which tests RetryManager.
This commit is contained in:
@@ -96,10 +96,11 @@ public abstract class DataConnectionTracker extends Handler {
|
||||
public static final int EVENT_CLEAN_UP_CONNECTION = 34;
|
||||
|
||||
//***** Constants
|
||||
protected static final int RECONNECT_DELAY_INITIAL_MILLIS = 5 * 1000;
|
||||
|
||||
/** Cap out with 30 min retry interval. */
|
||||
protected static final int RECONNECT_DELAY_MAX_MILLIS = 30 * 60 * 1000;
|
||||
/** Retry configuration: A doubling of retry times from 5secs to 30minutes */
|
||||
protected static final String DEFAULT_DATA_RETRY_CONFIG = "default_randomization=2000,"
|
||||
+ "5000,10000,20000,40000,80000:5000,160000:5000,"
|
||||
+ "320000:5000,640000:5000,1280000:5000,1800000:5000";
|
||||
|
||||
/** Slow poll when attempting connection recovery. */
|
||||
protected static final int POLL_NETSTAT_SLOW_MILLIS = 5000;
|
||||
|
||||
383
telephony/java/com/android/internal/telephony/RetryManager.java
Normal file
383
telephony/java/com/android/internal/telephony/RetryManager.java
Normal file
@@ -0,0 +1,383 @@
|
||||
/**
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.internal.telephony;
|
||||
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Retry manager allows a simple way to declare a series of
|
||||
* retires timeouts. After creating a RetryManager the configure
|
||||
* method is used to define the sequence. A simple linear series
|
||||
* may be initialized using configure with three integer parameters
|
||||
* The other configure method allows a series to be declared using
|
||||
* a string.
|
||||
*<p>
|
||||
* The format of the configuration string is a series of parameters
|
||||
* separated by a comma. There are two name value pair parameters plus a series
|
||||
* of delay times. The units of of these delay times is unspecified.
|
||||
* The name value pairs which may be specified are:
|
||||
*<ul>
|
||||
*<li>max_retries=<value>
|
||||
*<li>default_randomizationTime=<value>
|
||||
*</ul>
|
||||
*<p>
|
||||
* max_retries is the number of times that incrementRetryCount
|
||||
* maybe called before isRetryNeeded will return false.
|
||||
*
|
||||
* default_randomizationTime will be used as the randomizationTime
|
||||
* for delay times which have no supplied randomizationTime. If
|
||||
* default_randomizationTime is not defined it defaults to 0.
|
||||
*<p>
|
||||
* The other parameters define The series of delay times and each
|
||||
* may have an optional randomization value separated from the
|
||||
* delay time by a colon.
|
||||
*<p>
|
||||
* Examples:
|
||||
* <ul>
|
||||
* <li>3 retires with no randomization value which means its 0:
|
||||
* <ul><li><code>"1000, 2000, 3000"</code></ul>
|
||||
*
|
||||
* <li>10 retires with a 500 default randomization value for each and
|
||||
* the 4..10 retries all using 3000 as the delay:
|
||||
* <ul><li><code>"max_retries=10, default_randomization=500, 1000, 2000, 3000"</code></ul>
|
||||
*
|
||||
* <li>4 retires with a 100ms default randomization value for the first 2 values and
|
||||
* the other two having specified values of 500ms:
|
||||
* <ul><li><code>"default_randomization=100, 1000, 2000, 4000:500, 5000:500"</code></ul>
|
||||
* </ul>
|
||||
*
|
||||
* {@hide}
|
||||
*/
|
||||
public class RetryManager {
|
||||
static public final String LOG_TAG = "RetryManager";
|
||||
static public final boolean DBG = false;
|
||||
static public final int RETRYIES_NOT_STARTED = 0;
|
||||
static public final int RETRYIES_ON_GOING = 1;
|
||||
static public final int RETRYIES_COMPLETED = 2;
|
||||
|
||||
/**
|
||||
* Retry record with times in milli-seconds
|
||||
*/
|
||||
private static class RetryRec {
|
||||
RetryRec(int delayTime, int randomizationTime) {
|
||||
mDelayTime = delayTime;
|
||||
mRandomizationTime = randomizationTime;
|
||||
}
|
||||
|
||||
int mDelayTime;
|
||||
int mRandomizationTime;
|
||||
}
|
||||
|
||||
/** The array of retry records */
|
||||
private ArrayList<RetryRec> mRetryArray = new ArrayList<RetryRec>();
|
||||
|
||||
/** When true isRetryNeeded() will always return true */
|
||||
private boolean mRetryForever;
|
||||
|
||||
/**
|
||||
* The maximum number of retries to attempt before
|
||||
* isRetryNeeded returns false
|
||||
*/
|
||||
private int mMaxRetryCount;
|
||||
|
||||
/** The current number of retires */
|
||||
private int mRetryCount;
|
||||
|
||||
/** Random number generator */
|
||||
private Random rng = new Random();
|
||||
|
||||
/** Constructor */
|
||||
public RetryManager() {
|
||||
if (DBG) log("constructor");
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure for a simple linear sequence of times plus
|
||||
* a random value.
|
||||
*
|
||||
* @param maxRetryCount is the maximum number of retries
|
||||
* before isRetryNeeded returns false.
|
||||
* @param retryTime is a time that will be returned by getRetryTime.
|
||||
* @param randomizationTime a random value between 0 and
|
||||
* randomizationTime will be added to retryTime. this
|
||||
* parameter may be 0.
|
||||
* @return true if successfull
|
||||
*/
|
||||
public boolean configure(int maxRetryCount, int retryTime, int randomizationTime) {
|
||||
Pair<Boolean, Integer> value;
|
||||
|
||||
if (DBG) log("configure: " + maxRetryCount + ", " + retryTime + "," + randomizationTime);
|
||||
|
||||
if (!validateNonNegativeInt("maxRetryCount", maxRetryCount)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!validateNonNegativeInt("retryTime", retryTime)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!validateNonNegativeInt("randomizationTime", randomizationTime)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mMaxRetryCount = maxRetryCount;
|
||||
resetRetryCount();
|
||||
mRetryArray.clear();
|
||||
mRetryArray.add(new RetryRec(retryTime, randomizationTime));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure for using string which allow arbitary
|
||||
* sequences of times. See class comments for the
|
||||
* string format.
|
||||
*
|
||||
* @return true if successfull
|
||||
*/
|
||||
public boolean configure(String configStr) {
|
||||
if (DBG) log("configure: '" + configStr + "'");
|
||||
|
||||
if (!TextUtils.isEmpty(configStr)) {
|
||||
int defaultRandomization = 0;
|
||||
|
||||
if (DBG) log("configure: not empty");
|
||||
|
||||
mMaxRetryCount = 0;
|
||||
resetRetryCount();
|
||||
mRetryArray.clear();
|
||||
|
||||
String strArray[] = configStr.split(",");
|
||||
for (int i = 0; i < strArray.length; i++) {
|
||||
if (DBG) log("configure: strArray[" + i + "]='" + strArray[i] + "'");
|
||||
Pair<Boolean, Integer> value;
|
||||
String splitStr[] = strArray[i].split("=", 2);
|
||||
splitStr[0] = splitStr[0].trim();
|
||||
if (DBG) log("configure: splitStr[0]='" + splitStr[0] + "'");
|
||||
if (splitStr.length > 1) {
|
||||
splitStr[1] = splitStr[1].trim();
|
||||
if (DBG) 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")) {
|
||||
value = parseNonNegativeInt(splitStr[0], splitStr[1]);
|
||||
if (!value.first) return false;
|
||||
mMaxRetryCount = value.second;
|
||||
} else {
|
||||
Log.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 (DBG) 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 (DBG) log("configure: setting mMaxRetryCount=" + mMaxRetryCount);
|
||||
}
|
||||
if (DBG) log("configure: true");
|
||||
return true;
|
||||
} else {
|
||||
if (DBG) log("configure: false it's empty");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Report whether data reconnection should be retried
|
||||
*
|
||||
* @return {@code true} if the max retires has not been reached. {@code
|
||||
* false} otherwise.
|
||||
*/
|
||||
public boolean isRetryNeeded() {
|
||||
boolean retVal = mRetryForever || (mRetryCount < mMaxRetryCount);
|
||||
if (DBG) log("isRetryNeeded: " + retVal);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the timer that should be used to trigger the data reconnection
|
||||
*/
|
||||
public 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return retry count
|
||||
*/
|
||||
public int getRetryCount() {
|
||||
if (DBG) log("getRetryCount: " + mRetryCount);
|
||||
return mRetryCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increase the retry counter, does not change retry forever.
|
||||
*/
|
||||
public void increaseRetryCount() {
|
||||
mRetryCount++;
|
||||
if (mRetryCount > mMaxRetryCount) {
|
||||
mRetryCount = mMaxRetryCount;
|
||||
}
|
||||
if (DBG) log("increseRetryCount: " + mRetryCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set retry count to the specified value
|
||||
* and turns off retrying forever.
|
||||
*/
|
||||
public void setRetryCount(int count) {
|
||||
mRetryCount = count;
|
||||
if (mRetryCount > mMaxRetryCount) {
|
||||
mRetryCount = mMaxRetryCount;
|
||||
}
|
||||
|
||||
if (mRetryCount < 0) {
|
||||
mRetryCount = 0;
|
||||
}
|
||||
|
||||
mRetryForever = false;
|
||||
if (DBG) log("setRetryCount: " + mRetryCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset network re-registration indicator and clear the data-retry counter
|
||||
* and turns off retrying forever.
|
||||
*/
|
||||
public void resetRetryCount() {
|
||||
mRetryCount = 0;
|
||||
mRetryForever = false;
|
||||
if (DBG) log("resetRetryCount: " + mRetryCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retry forever using last timeout time.
|
||||
*/
|
||||
public void retryForeverUsingLastTimeout() {
|
||||
mRetryCount = mMaxRetryCount;
|
||||
mRetryForever = true;
|
||||
if (DBG) log("retryForeverUsingLastTimeout: " + mRetryForever + ", " + mRetryCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if retrying forever
|
||||
*/
|
||||
public boolean isRetryForever() {
|
||||
if (DBG) log("isRetryForever: " + mRetryForever);
|
||||
return mRetryForever;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an integer validating the value is not negative.
|
||||
*
|
||||
* @param name
|
||||
* @param stringValue
|
||||
* @return Pair.first == true if stringValue an integer >= 0
|
||||
*/
|
||||
private Pair<Boolean, Integer> parseNonNegativeInt(String name, String stringValue) {
|
||||
int value;
|
||||
Pair<Boolean, Integer> retVal;
|
||||
try {
|
||||
value = Integer.parseInt(stringValue);
|
||||
retVal = new Pair<Boolean, Integer>(validateNonNegativeInt(name, value), value);
|
||||
} catch (NumberFormatException e) {
|
||||
Log.e(LOG_TAG, name + " bad value: " + stringValue, e);
|
||||
retVal = new Pair<Boolean, Integer>(false, 0);
|
||||
}
|
||||
if (DBG) log("parseNonNetativeInt: " + name + ", " + stringValue + ", "
|
||||
+ retVal.first + ", " + retVal.second);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate an integer is >= 0 and logs an error if not
|
||||
*
|
||||
* @param name
|
||||
* @param value
|
||||
* @return Pair.first
|
||||
*/
|
||||
private boolean validateNonNegativeInt(String name, int value) {
|
||||
boolean retVal;
|
||||
if (value < 0) {
|
||||
Log.e(LOG_TAG, name + " bad value: is < 0");
|
||||
retVal = false;
|
||||
} else {
|
||||
retVal = true;
|
||||
}
|
||||
if (DBG) log("validateNonNegative: " + name + ", " + value + ", " + retVal);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return next random number for the index
|
||||
*/
|
||||
private int nextRandomizationTime(int index) {
|
||||
int randomTime = mRetryArray.get(index).mRandomizationTime;
|
||||
if (randomTime == 0) {
|
||||
return 0;
|
||||
} else {
|
||||
return rng.nextInt(randomTime);
|
||||
}
|
||||
}
|
||||
|
||||
private void log(String s) {
|
||||
Log.d(LOG_TAG, s);
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,7 @@ import android.os.Message;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.os.SystemClock;
|
||||
import android.os.SystemProperties;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.provider.Checkin;
|
||||
import android.telephony.ServiceState;
|
||||
@@ -45,6 +46,7 @@ import com.android.internal.telephony.DataCallState;
|
||||
import com.android.internal.telephony.DataConnection;
|
||||
import com.android.internal.telephony.DataConnection.FailCause;
|
||||
import com.android.internal.telephony.DataConnectionTracker;
|
||||
import com.android.internal.telephony.RetryManager;
|
||||
import com.android.internal.telephony.Phone;
|
||||
import com.android.internal.telephony.TelephonyEventLog;
|
||||
|
||||
@@ -61,8 +63,6 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
|
||||
|
||||
// Indicates baseband will not auto-attach
|
||||
private boolean noAutoAttach = false;
|
||||
long nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS;
|
||||
private boolean mReregisterOnReconnectFailure = false;
|
||||
private boolean mIsScreenOn = true;
|
||||
|
||||
//useful for debugging
|
||||
@@ -76,6 +76,9 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
|
||||
/** Currently active CdmaDataConnection */
|
||||
private CdmaDataConnection mActiveDataConnection;
|
||||
|
||||
/** Manage the behavior of data retry after failure */
|
||||
private final RetryManager mRetryMgr = new RetryManager();
|
||||
|
||||
/** Defined cdma connection profiles */
|
||||
private static final int EXTERNAL_NETWORK_DEFAULT_ID = 0;
|
||||
private static final int EXTERNAL_NETWORK_NUM_TYPES = 1;
|
||||
@@ -183,10 +186,19 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
|
||||
dataEnabled[EXTERNAL_NETWORK_DEFAULT_ID] =
|
||||
!sp.getBoolean(CDMAPhone.DATA_DISABLED_ON_BOOT_KEY, false);
|
||||
noAutoAttach = !dataEnabled[EXTERNAL_NETWORK_DEFAULT_ID];
|
||||
|
||||
if (!mRetryMgr.configure(SystemProperties.get("ro.cdma.data_retry_config"))) {
|
||||
if (!mRetryMgr.configure(DEFAULT_DATA_RETRY_CONFIG)) {
|
||||
// Should never happen, log an error and default to a simple linear sequence.
|
||||
Log.e(LOG_TAG, "Could not configure using DEFAULT_DATA_RETRY_CONFIG="
|
||||
+ DEFAULT_DATA_RETRY_CONFIG);
|
||||
mRetryMgr.configure(20, 2000, 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
//Unregister from all events
|
||||
// Unregister from all events
|
||||
phone.mCM.unregisterForAvailable(this);
|
||||
phone.mCM.unregisterForOffOrNotAvailable(this);
|
||||
mCdmaPhone.mRuimRecords.unregisterForRecordsLoaded(this);
|
||||
@@ -459,9 +471,7 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
|
||||
setState(State.CONNECTED);
|
||||
phone.notifyDataConnection(reason);
|
||||
startNetStatPoll();
|
||||
// reset reconnect timer
|
||||
nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS;
|
||||
mReregisterOnReconnectFailure = false;
|
||||
mRetryMgr.resetRetryCount();
|
||||
}
|
||||
|
||||
private void resetPollStats() {
|
||||
@@ -617,21 +627,13 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
|
||||
|
||||
private void reconnectAfterFail(FailCause lastFailCauseCode, String reason) {
|
||||
if (state == State.FAILED) {
|
||||
if (nextReconnectDelay > RECONNECT_DELAY_MAX_MILLIS) {
|
||||
if (mReregisterOnReconnectFailure) {
|
||||
// We have already tried to re-register to the network.
|
||||
// This might be a problem with the data network.
|
||||
nextReconnectDelay = RECONNECT_DELAY_MAX_MILLIS;
|
||||
} else {
|
||||
// Try to Re-register to the network.
|
||||
Log.d(LOG_TAG, "PDP activate failed, Reregistering to the network");
|
||||
mReregisterOnReconnectFailure = true;
|
||||
mCdmaPhone.mSST.reRegisterNetwork(null);
|
||||
nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For now With CDMA we never try to reconnect on
|
||||
* error and instead just continue to retry
|
||||
* at the last time until the state is changed.
|
||||
* TODO: Make this configurable?
|
||||
*/
|
||||
int nextReconnectDelay = mRetryMgr.getRetryTimer();
|
||||
Log.d(LOG_TAG, "Data Connection activate failed. Scheduling next attempt for "
|
||||
+ (nextReconnectDelay / 1000) + "s");
|
||||
|
||||
@@ -645,8 +647,7 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
|
||||
SystemClock.elapsedRealtime() + nextReconnectDelay,
|
||||
mReconnectIntent);
|
||||
|
||||
// double it for next time
|
||||
nextReconnectDelay *= 2;
|
||||
mRetryMgr.increaseRetryCount();
|
||||
|
||||
if (!shouldPostNotification(lastFailCauseCode)) {
|
||||
Log.d(LOG_TAG,"NOT Posting Data Connection Unavailable notification "
|
||||
@@ -723,10 +724,7 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
|
||||
* @override com.android.internal.telephony.DataConnectionTracker
|
||||
*/
|
||||
protected void onRadioOffOrNotAvailable() {
|
||||
// Make sure our reconnect delay starts at the initial value
|
||||
// next time the radio comes on
|
||||
nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS;
|
||||
mReregisterOnReconnectFailure = false;
|
||||
mRetryMgr.resetRetryCount();
|
||||
|
||||
if (phone.getSimulatedRadioControl() != null) {
|
||||
// Assume data is connected on the simulator
|
||||
@@ -802,9 +800,7 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
|
||||
resetPollStats();
|
||||
}
|
||||
} else {
|
||||
// reset reconnect timer
|
||||
nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS;
|
||||
mReregisterOnReconnectFailure = false;
|
||||
mRetryMgr.resetRetryCount();
|
||||
// in case data setup was attempted when we were on a voice call
|
||||
trySetupData(Phone.REASON_VOICE_CALL_ENDED);
|
||||
}
|
||||
@@ -840,7 +836,7 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
|
||||
} else {
|
||||
if (state == State.FAILED) {
|
||||
cleanUpConnection(false, Phone.REASON_CDMA_DATA_DETACHED);
|
||||
nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS;
|
||||
mRetryMgr.resetRetryCount();
|
||||
|
||||
CdmaCellLocation loc = (CdmaCellLocation)(phone.getCellLocation());
|
||||
int bsid = (loc != null) ? loc.getBaseStationId() : -1;
|
||||
|
||||
@@ -52,6 +52,7 @@ import com.android.internal.telephony.DataCallState;
|
||||
import com.android.internal.telephony.DataConnection;
|
||||
import com.android.internal.telephony.DataConnectionTracker;
|
||||
import com.android.internal.telephony.Phone;
|
||||
import com.android.internal.telephony.RetryManager;
|
||||
import com.android.internal.telephony.TelephonyEventLog;
|
||||
import com.android.internal.telephony.DataConnection.FailCause;
|
||||
|
||||
@@ -85,7 +86,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
|
||||
INetStatService netstat;
|
||||
// Indicates baseband will not auto-attach
|
||||
private boolean noAutoAttach = false;
|
||||
long nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS;
|
||||
|
||||
private boolean mReregisterOnReconnectFailure = false;
|
||||
private ContentResolver mResolver;
|
||||
|
||||
@@ -95,6 +96,11 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
|
||||
private int mPdpResetCount = 0;
|
||||
private boolean mIsScreenOn = true;
|
||||
|
||||
private final RetryManager mRetryMgr = new RetryManager();
|
||||
|
||||
/** Delay between APN attempts */
|
||||
protected static final int APN_DELAY_MILLIS = 5000;
|
||||
|
||||
//useful for debugging
|
||||
boolean failNextConnect = false;
|
||||
|
||||
@@ -247,6 +253,15 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
|
||||
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(phone.getContext());
|
||||
dataEnabled[APN_DEFAULT_ID] = !sp.getBoolean(GSMPhone.DATA_DISABLED_ON_BOOT_KEY, false);
|
||||
noAutoAttach = !dataEnabled[APN_DEFAULT_ID];
|
||||
|
||||
if (!mRetryMgr.configure(SystemProperties.get("ro.gsm.data_retry_config"))) {
|
||||
if (!mRetryMgr.configure(DEFAULT_DATA_RETRY_CONFIG)) {
|
||||
// Should never happen, log an error and default to a simple linear sequence.
|
||||
Log.e(LOG_TAG, "Could not configure using DEFAULT_DATA_RETRY_CONFIG="
|
||||
+ DEFAULT_DATA_RETRY_CONFIG);
|
||||
mRetryMgr.configure(20, 2000, 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
@@ -558,7 +573,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
|
||||
} else {
|
||||
if (state == State.FAILED) {
|
||||
cleanUpConnection(false, Phone.REASON_GPRS_ATTACHED);
|
||||
nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS;
|
||||
mRetryMgr.resetRetryCount();
|
||||
}
|
||||
trySetupData(Phone.REASON_GPRS_ATTACHED);
|
||||
}
|
||||
@@ -808,7 +823,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
|
||||
cleanUpConnection(isConnected, Phone.REASON_APN_CHANGED);
|
||||
if (!isConnected) {
|
||||
// reset reconnect timer
|
||||
nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS;
|
||||
mRetryMgr.resetRetryCount();
|
||||
mReregisterOnReconnectFailure = false;
|
||||
trySetupData(Phone.REASON_APN_CHANGED);
|
||||
}
|
||||
@@ -889,7 +904,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
|
||||
phone.notifyDataConnection(reason);
|
||||
startNetStatPoll();
|
||||
// reset reconnect timer
|
||||
nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS;
|
||||
mRetryMgr.resetRetryCount();
|
||||
mReregisterOnReconnectFailure = false;
|
||||
}
|
||||
|
||||
@@ -1175,20 +1190,21 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
|
||||
|
||||
private void reconnectAfterFail(FailCause lastFailCauseCode, String reason) {
|
||||
if (state == State.FAILED) {
|
||||
if (nextReconnectDelay > RECONNECT_DELAY_MAX_MILLIS) {
|
||||
if (!mRetryMgr.isRetryNeeded()) {
|
||||
if (mReregisterOnReconnectFailure) {
|
||||
// We have already tried to re-register to the network.
|
||||
// This might be a problem with the data network.
|
||||
nextReconnectDelay = RECONNECT_DELAY_MAX_MILLIS;
|
||||
// We've re-registerd once now just retry forever.
|
||||
mRetryMgr.retryForeverUsingLastTimeout();
|
||||
} else {
|
||||
// Try to Re-register to the network.
|
||||
// Try to re-register to the network.
|
||||
Log.d(LOG_TAG, "PDP activate failed, Reregistering to the network");
|
||||
mReregisterOnReconnectFailure = true;
|
||||
mGsmPhone.mSST.reRegisterNetwork(null);
|
||||
nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS;
|
||||
mRetryMgr.resetRetryCount();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int nextReconnectDelay = mRetryMgr.getRetryTimer();
|
||||
Log.d(LOG_TAG, "PDP activate failed. Scheduling next attempt for "
|
||||
+ (nextReconnectDelay / 1000) + "s");
|
||||
|
||||
@@ -1202,8 +1218,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
|
||||
SystemClock.elapsedRealtime() + nextReconnectDelay,
|
||||
mReconnectIntent);
|
||||
|
||||
// double it for next time
|
||||
nextReconnectDelay *= 2;
|
||||
mRetryMgr.increaseRetryCount();
|
||||
|
||||
if (!shouldPostNotification(lastFailCauseCode)) {
|
||||
Log.d(LOG_TAG,"NOT Posting GPRS Unavailable notification "
|
||||
@@ -1276,7 +1291,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
|
||||
protected void onRadioOffOrNotAvailable() {
|
||||
// Make sure our reconnect delay starts at the initial value
|
||||
// next time the radio comes on
|
||||
nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS;
|
||||
mRetryMgr.resetRetryCount();
|
||||
mReregisterOnReconnectFailure = false;
|
||||
|
||||
if (phone.getSimulatedRadioControl() != null) {
|
||||
@@ -1361,8 +1376,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
|
||||
setState(State.SCANNING);
|
||||
// Wait a bit before trying the next APN, so that
|
||||
// we're not tying up the RIL command channel
|
||||
sendMessageDelayed(obtainMessage(EVENT_TRY_SETUP_DATA, reason),
|
||||
RECONNECT_DELAY_INITIAL_MILLIS);
|
||||
sendMessageDelayed(obtainMessage(EVENT_TRY_SETUP_DATA, reason), APN_DELAY_MILLIS);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1407,7 +1421,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
|
||||
}
|
||||
} else {
|
||||
// reset reconnect timer
|
||||
nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS;
|
||||
mRetryMgr.resetRetryCount();
|
||||
mReregisterOnReconnectFailure = false;
|
||||
// in case data setup was attempted when we were on a voice call
|
||||
trySetupData(Phone.REASON_VOICE_CALL_ENDED);
|
||||
@@ -1684,7 +1698,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
|
||||
} else {
|
||||
if (state == State.FAILED) {
|
||||
cleanUpConnection(false, Phone.REASON_PS_RESTRICT_ENABLED);
|
||||
nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS;
|
||||
mRetryMgr.resetRetryCount();
|
||||
mReregisterOnReconnectFailure = false;
|
||||
}
|
||||
trySetupData(Phone.REASON_PS_RESTRICT_ENABLED);
|
||||
|
||||
@@ -47,6 +47,7 @@ public class TelephonyTests {
|
||||
suite.addTestSuite(SimUtilsTest.class);
|
||||
suite.addTestSuite(SimPhoneBookTest.class);
|
||||
suite.addTestSuite(SimSmsTest.class);
|
||||
suite.addTestSuite(TelephonyUtilsTest.class);
|
||||
|
||||
return suite;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
/**
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.internal.telephony;
|
||||
|
||||
import com.android.internal.telephony.RetryManager;
|
||||
import junit.framework.TestCase;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
public class TelephonyUtilsTest extends TestCase {
|
||||
|
||||
/**
|
||||
* After first creating the RetryManager
|
||||
* isRetryNeeded should be false and the time 0
|
||||
*/
|
||||
@SmallTest
|
||||
public void testRetryManagerEmpty() throws Exception {
|
||||
RetryManager rm = new RetryManager();
|
||||
|
||||
assertEquals(0, rm.getRetryCount());
|
||||
assertFalse(rm.isRetryForever());
|
||||
assertFalse(rm.isRetryNeeded());
|
||||
assertEquals(0, rm.getRetryCount());
|
||||
assertEquals(0, rm.getRetryTimer());
|
||||
|
||||
rm.increaseRetryCount();
|
||||
assertFalse(rm.isRetryForever());
|
||||
assertFalse(rm.isRetryNeeded());
|
||||
assertEquals(0, rm.getRetryCount());
|
||||
assertEquals(0, rm.getRetryTimer());
|
||||
|
||||
rm.setRetryCount(123);
|
||||
assertFalse(rm.isRetryForever());
|
||||
assertFalse(rm.isRetryNeeded());
|
||||
assertEquals(0, rm.getRetryCount());
|
||||
assertEquals(0, rm.getRetryTimer());
|
||||
|
||||
rm.retryForeverUsingLastTimeout();
|
||||
assertTrue(rm.isRetryForever());
|
||||
assertTrue(rm.isRetryNeeded());
|
||||
assertEquals(0, rm.getRetryCount());
|
||||
assertEquals(0, rm.getRetryTimer());
|
||||
|
||||
rm.setRetryCount(2);
|
||||
assertFalse(rm.isRetryForever());
|
||||
assertFalse(rm.isRetryNeeded());
|
||||
assertEquals(0, rm.getRetryCount());
|
||||
assertEquals(0, rm.getRetryTimer());
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple test and that randomization is doing something.
|
||||
*/
|
||||
@SmallTest
|
||||
public void testRetryManagerSimplest() throws Exception {
|
||||
RetryManager rm = new RetryManager();
|
||||
|
||||
assertTrue(rm.configure(1, 500, 10));
|
||||
int loops = 10;
|
||||
int count = 0;
|
||||
for (int i = 0; i < loops; i++) {
|
||||
assertTrue(rm.isRetryNeeded());
|
||||
int time = rm.getRetryTimer();
|
||||
assertTrue((time >= 500) && (time < 600));
|
||||
if (time == 500) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
assertFalse(count == loops);
|
||||
rm.increaseRetryCount();
|
||||
assertFalse(rm.isRetryNeeded());
|
||||
rm.setRetryCount(0);
|
||||
assertTrue(rm.isRetryNeeded());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test multiple values using simple configuration.
|
||||
*/
|
||||
@SmallTest
|
||||
public void testRetryManagerSimple() throws Exception {
|
||||
RetryManager rm = new RetryManager();
|
||||
|
||||
assertTrue(rm.configure(3, 1000, 0));
|
||||
assertTrue(rm.isRetryNeeded());
|
||||
assertEquals(1000, rm.getRetryTimer());
|
||||
assertEquals(rm.getRetryTimer(), 1000);
|
||||
rm.increaseRetryCount();
|
||||
assertTrue(rm.isRetryNeeded());
|
||||
assertEquals(1000, rm.getRetryTimer());
|
||||
rm.increaseRetryCount();
|
||||
assertTrue(rm.isRetryNeeded());
|
||||
assertEquals(1000, rm.getRetryTimer());
|
||||
rm.increaseRetryCount();
|
||||
assertFalse(rm.isRetryNeeded());
|
||||
assertEquals(1000, rm.getRetryTimer());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test string configuration, simplest
|
||||
*/
|
||||
@SmallTest
|
||||
public void testRetryManageSimpleString() throws Exception {
|
||||
RetryManager rm = new RetryManager();
|
||||
|
||||
assertTrue(rm.configure("101"));
|
||||
assertTrue(rm.isRetryNeeded());
|
||||
assertEquals(101, rm.getRetryTimer());
|
||||
rm.increaseRetryCount();
|
||||
assertFalse(rm.isRetryNeeded());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test string configuration using all options.
|
||||
*/
|
||||
@SmallTest
|
||||
public void testRetryManageString() throws Exception {
|
||||
RetryManager rm = new RetryManager();
|
||||
int time;
|
||||
|
||||
assertTrue(rm.configure("max_retries=4,"
|
||||
+ "default_randomization=100,1000, 2000 :200 , 3000"));
|
||||
assertTrue(rm.isRetryNeeded());
|
||||
time = rm.getRetryTimer();
|
||||
assertTrue((time >= 1000) && (time < 1100));
|
||||
|
||||
rm.increaseRetryCount();
|
||||
assertTrue(rm.isRetryNeeded());
|
||||
time = rm.getRetryTimer();
|
||||
assertTrue((time >= 2000) && (time < 2200));
|
||||
|
||||
rm.increaseRetryCount();
|
||||
assertTrue(rm.isRetryNeeded());
|
||||
time = rm.getRetryTimer();
|
||||
assertTrue((time >= 3000) && (time < 3100));
|
||||
|
||||
rm.increaseRetryCount();
|
||||
assertTrue(rm.isRetryNeeded());
|
||||
time = rm.getRetryTimer();
|
||||
assertTrue((time >= 3000) && (time < 3100));
|
||||
|
||||
rm.increaseRetryCount();
|
||||
assertFalse(rm.isRetryNeeded());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test string configuration using all options.
|
||||
*/
|
||||
@SmallTest
|
||||
public void testRetryManageForever() throws Exception {
|
||||
RetryManager rm = new RetryManager();
|
||||
int time;
|
||||
|
||||
assertTrue(rm.configure("1000, 2000, 3000"));
|
||||
assertTrue(rm.isRetryNeeded());
|
||||
assertFalse(rm.isRetryForever());
|
||||
assertEquals(0, rm.getRetryCount());
|
||||
assertEquals(1000, rm.getRetryTimer());
|
||||
|
||||
rm.retryForeverUsingLastTimeout();
|
||||
rm.increaseRetryCount();
|
||||
rm.increaseRetryCount();
|
||||
rm.increaseRetryCount();
|
||||
assertTrue(rm.isRetryNeeded());
|
||||
assertTrue(rm.isRetryForever());
|
||||
assertEquals(3, rm.getRetryCount());
|
||||
assertEquals(3000, rm.getRetryTimer());
|
||||
|
||||
rm.setRetryCount(1);
|
||||
assertTrue(rm.isRetryNeeded());
|
||||
assertFalse(rm.isRetryForever());
|
||||
assertEquals(1, rm.getRetryCount());
|
||||
assertEquals(2000, rm.getRetryTimer());
|
||||
|
||||
rm.retryForeverUsingLastTimeout();
|
||||
assertTrue(rm.isRetryNeeded());
|
||||
assertTrue(rm.isRetryForever());
|
||||
rm.resetRetryCount();
|
||||
assertTrue(rm.isRetryNeeded());
|
||||
assertFalse(rm.isRetryForever());
|
||||
assertEquals(0, rm.getRetryCount());
|
||||
assertEquals(1000, rm.getRetryTimer());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user