summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--telephony/java/com/android/internal/telephony/DataConnectionTracker.java7
-rw-r--r--telephony/java/com/android/internal/telephony/RetryManager.java383
-rw-r--r--telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java58
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java48
-rw-r--r--tests/CoreTests/com/android/internal/telephony/TelephonyTests.java1
-rw-r--r--tests/CoreTests/com/android/internal/telephony/TelephonyUtilsTest.java196
6 files changed, 642 insertions, 51 deletions
diff --git a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
index c074cb8..a3f1ad8 100644
--- a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
@@ -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;
diff --git a/telephony/java/com/android/internal/telephony/RetryManager.java b/telephony/java/com/android/internal/telephony/RetryManager.java
new file mode 100644
index 0000000..242b2cd
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/RetryManager.java
@@ -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);
+ }
+}
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
index c3818f5..38bc24d 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
@@ -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;
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index c33d4b6..8b3529f 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -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);
diff --git a/tests/CoreTests/com/android/internal/telephony/TelephonyTests.java b/tests/CoreTests/com/android/internal/telephony/TelephonyTests.java
index b4fb324..fdfafe1 100644
--- a/tests/CoreTests/com/android/internal/telephony/TelephonyTests.java
+++ b/tests/CoreTests/com/android/internal/telephony/TelephonyTests.java
@@ -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;
}
diff --git a/tests/CoreTests/com/android/internal/telephony/TelephonyUtilsTest.java b/tests/CoreTests/com/android/internal/telephony/TelephonyUtilsTest.java
new file mode 100644
index 0000000..e4cf1e8
--- /dev/null
+++ b/tests/CoreTests/com/android/internal/telephony/TelephonyUtilsTest.java
@@ -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());
+ }
+}