394 lines
13 KiB
Java
394 lines
13 KiB
Java
/**
|
|
* 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
|
|
* retry 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. if value
|
|
* is infinite then isRetryNeeded will always return true.
|
|
*
|
|
* 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 retries with no randomization value which means its 0:
|
|
* <ul><li><code>"1000, 2000, 3000"</code></ul>
|
|
*
|
|
* <li>10 retries 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 retries with a 100 as the default randomization value for the first 2 values and
|
|
* the other two having specified values of 500:
|
|
* <ul><li><code>"default_randomization=100, 1000, 2000, 4000:500, 5000:500"</code></ul>
|
|
*
|
|
* <li>Infinite number of retries with the first one at 1000, the second at 2000 all
|
|
* others will be at 3000.
|
|
* <ul><li><code>"max_retries=infinite,1000,2000,3000</code></ul>
|
|
* </ul>
|
|
*
|
|
* {@hide}
|
|
*/
|
|
public class RetryManager {
|
|
static public final String LOG_TAG = "RetryManager";
|
|
static public final boolean DBG = false;
|
|
|
|
/**
|
|
* 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 retries */
|
|
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 successful
|
|
*/
|
|
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 arbitrary
|
|
* sequences of times. See class comments for the
|
|
* string format.
|
|
*
|
|
* @return true if successful
|
|
*/
|
|
public boolean configure(String configStr) {
|
|
// Strip quotes if present.
|
|
if ((configStr.startsWith("\"") && configStr.endsWith("\""))) {
|
|
configStr = configStr.substring(1, configStr.length()-1);
|
|
}
|
|
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")) {
|
|
if (TextUtils.equals("infinite",splitStr[1])) {
|
|
mRetryForever = true;
|
|
} else {
|
|
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 retries 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("increaseRetryCount: " + 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);
|
|
}
|
|
}
|