summaryrefslogtreecommitdiff
path: root/java/com/android/dialer/common/backoff
diff options
context:
space:
mode:
authorTreehugger Robot <treehugger-gerrit@google.com>2017-11-18 09:16:18 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2017-11-18 09:16:18 +0000
commit3b450ceb4290364df58a541c60cc690cc36ba4c8 (patch)
treec9bb61716fc202d23c4966a1d2407cfa09744791 /java/com/android/dialer/common/backoff
parenta8b9f7de79559caef0aaab7dc05f585314024790 (diff)
parent7f1076d08b39467ac8c550f60a7fbbad604ab4fa (diff)
Merge changes Iee4e3db8,I6e74c7fe,Ibe722477,Ia22751f0,Ic28fb197, ...
* changes: DialpadView cleanup. Merge the following methods in InCallActivityCommon into InCallActivity: Add contact source options. Automated rollback of changelist 174944384 Refactoring and adding TOS check before transcribing Adding exponential backoff utilities Implement suggested SIM Fix dialer simulator for conference calling funcitonality. Updated the following contents: 1.Fix the order of spawning connections for GSM conference. 2.Make VOLTE conference call more realistic. 3.Fix minor bugs about simulator. 4.Add SimulatorConnectionsBank class to store connection tags created by simulator. 5.Fix tests influenced by SimulatorConnectionsBank.
Diffstat (limited to 'java/com/android/dialer/common/backoff')
-rw-r--r--java/com/android/dialer/common/backoff/ExponentialBackoff.java96
-rw-r--r--java/com/android/dialer/common/backoff/ExponentialBaseCalculator.java126
2 files changed, 222 insertions, 0 deletions
diff --git a/java/com/android/dialer/common/backoff/ExponentialBackoff.java b/java/com/android/dialer/common/backoff/ExponentialBackoff.java
new file mode 100644
index 000000000..67727d8c2
--- /dev/null
+++ b/java/com/android/dialer/common/backoff/ExponentialBackoff.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 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.dialer.common.backoff;
+
+import com.android.dialer.common.Assert;
+
+/**
+ * Given an initial backoff delay, D, a base multiplier, B, and a total number of backoffs, N, this
+ * class returns values in the exponential sequence, D, D*B, D*B^2, ... D*B^(N-1), ...
+ *
+ * <p>Example usage:
+ *
+ * <pre>
+ * long initialDelayMillis = 1000;
+ * double multiplier = 1.2;
+ * int backoffs = 10;
+ * ExponentialBackoff backoff = new ExponentialBackoff(initialDelayMillis, multiplier, backoffs);
+ * while (backoff.isInRange()) {
+ * ...
+ * sleep(backoff.getNextBackoff());
+ * }
+ * </pre>
+ *
+ * <p>Note: the base multiplier can be calculated using {@code ExponentialBaseCalculator}
+ */
+public final class ExponentialBackoff {
+ public final long initialDelayMillis;
+ public final double baseMultiplier;
+ public final int maximumBackoffs;
+ private double nextBackoff;
+ private int backoffCount;
+
+ /**
+ * Setup an exponential backoff with an initial delay, a base multiplier and a maximum number of
+ * backoff steps.
+ *
+ * @throws IllegalArgumentException for negative argument values
+ */
+ public ExponentialBackoff(long initialDelayMillis, double baseMultiplier, int maximumBackoffs) {
+ Assert.checkArgument(initialDelayMillis > 0);
+ Assert.checkArgument(baseMultiplier > 0);
+ Assert.checkArgument(maximumBackoffs > 0);
+ this.initialDelayMillis = initialDelayMillis;
+ this.baseMultiplier = baseMultiplier;
+ this.maximumBackoffs = maximumBackoffs;
+ reset();
+ }
+
+ /**
+ * @return the next backoff time in the exponential sequence. Specifically, if D is the initial
+ * delay, B is the base multiplier and N is the total number of backoffs, then the return
+ * values will be: D, D*B, D*B^2, ... D*B^(N-1), ...
+ */
+ public long getNextBackoff() {
+ long backoff = Math.round(nextBackoff);
+ backoffCount++;
+ nextBackoff *= baseMultiplier;
+ return backoff;
+ }
+
+ /** @return the number of times getNextBackoff() has been called */
+ public int getBackoffCount() {
+ return backoffCount;
+ }
+
+ /**
+ * @return {@code true} if getNextBackoff() has been called less than the maximumBackoffs value
+ * specified in the constructor.
+ */
+ public boolean isInRange() {
+ return backoffCount < maximumBackoffs;
+ }
+
+ /**
+ * Reset the sequence of backoff values so the next call to getNextBackoff() will return the
+ * initial delay and getBackoffCount() will return 0
+ */
+ public void reset() {
+ nextBackoff = initialDelayMillis;
+ backoffCount = 0;
+ }
+}
diff --git a/java/com/android/dialer/common/backoff/ExponentialBaseCalculator.java b/java/com/android/dialer/common/backoff/ExponentialBaseCalculator.java
new file mode 100644
index 000000000..a1ae8a053
--- /dev/null
+++ b/java/com/android/dialer/common/backoff/ExponentialBaseCalculator.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2017 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.dialer.common.backoff;
+
+import com.android.dialer.common.Assert;
+
+/**
+ * Given an initial delay, D, a maximum total backoff time, T, and a maximum number of backoffs, N,
+ * this class calculates a base multiplier, b, and a scaling factor, g, such that the initial
+ * backoff is g*b = D and the sum of the scaled backoffs is T = g*b + g*b^2 + g*b^3 + ... g*b^N
+ *
+ * <p>T/D = (1 - b^N)/(1 - b) but this cannot be written as a simple equation for b in terms of T, N
+ * and D so instead use Newton's method (https://en.wikipedia.org/wiki/Newton%27s_method) to find an
+ * approximate value for b.
+ *
+ * <p>Example usage using the {@code ExponentialBackoff} would be:
+ *
+ * <pre>
+ * // Retry with exponential backoff for up to 2 minutes, with an initial delay of 100 millis
+ * // and a maximum of 10 retries
+ * long initialDelayMillis = 100;
+ * int maxTries = 10;
+ * double base = ExponentialBaseCalculator.findBase(
+ * initialDelayMillis,
+ * TimeUnit.MINUTES.toMillis(2),
+ * maxTries);
+ * ExponentialBackoff backoff = new ExponentialBackoff(initialDelayMillis, base, maxTries);
+ * while (backoff.isInRange()) {
+ * ...
+ * long delay = backoff.getNextBackoff();
+ * // Wait for the indicated time...
+ * }
+ * </pre>
+ */
+public final class ExponentialBaseCalculator {
+ private static final int MAX_STEPS = 1000;
+ private static final double DEFAULT_TOLERANCE_MILLIS = 1;
+
+ /**
+ * Calculate an exponential backoff base multiplier such that the first backoff delay will be as
+ * specified and the sum of the delays after doing the indicated maximum number of backoffs will
+ * be as specified.
+ *
+ * @throws IllegalArgumentException if the initial delay is greater than the total backoff time
+ * @throws IllegalArgumentException if the maximum number of backoffs is not greater than 1
+ * @throws IllegalStateException if it fails to find an acceptable base multiplier
+ */
+ public static double findBase(
+ long initialDelayMillis, long totalBackoffTimeMillis, int maximumBackoffs) {
+ Assert.checkArgument(initialDelayMillis < totalBackoffTimeMillis);
+ Assert.checkArgument(maximumBackoffs > 1);
+ long scaledTotalTime = Math.round(((double) totalBackoffTimeMillis) / initialDelayMillis);
+ double scaledTolerance = DEFAULT_TOLERANCE_MILLIS / initialDelayMillis;
+ return getBaseImpl(scaledTotalTime, maximumBackoffs, scaledTolerance);
+ }
+
+ /**
+ * T/D = (1 - b^N)/(1 - b) but this cannot be written as a simple equation for b in terms of T, D
+ * and N so instead we use Newtons method to find an approximate value for b.
+ *
+ * <p>Let f(b) = (1 - b^N)/(1 - b) - T/D then we want to find b* such that f(b*) = 0, or more
+ * precisely |f(b*)| < tolerance
+ *
+ * <p>Using Newton's method we can interatively find b* as follows: b1 = b0 - f(b0)/f'(b0), where
+ * b0 is the current best guess for b* and b1 is the next best guess.
+ *
+ * <p>f'(b) = (f(b) + T/D - N*b^(N - 1))/(1 - b)
+ *
+ * <p>so
+ *
+ * <p>b1 = b0 - f(b0)(1 - b0)/(f(b0) + T/D - N*b0^(N - 1))
+ */
+ private static double getBaseImpl(long t, int n, double tolerance) {
+ double b0 = 2; // Initial guess for b*
+ double b0n = Math.pow(b0, n);
+ double fb0 = f(b0, t, b0n);
+ if (Math.abs(fb0) < tolerance) {
+ // Initial guess was pretty good
+ return b0;
+ }
+
+ for (int i = 0; i < MAX_STEPS; i++) {
+ double fpb0 = fp(b0, t, n, fb0, b0n);
+ double b1 = b0 - fb0 / fpb0;
+ double b1n = Math.pow(b1, n);
+ double fb1 = f(b1, t, b1n);
+
+ if (Math.abs(fb1) < tolerance) {
+ // Found an acceptable value
+ return b1;
+ }
+
+ b0 = b1;
+ b0n = b1n;
+ fb0 = fb1;
+ }
+
+ throw new IllegalStateException("Failed to find base. Too many iterations.");
+ }
+
+ // Evaluate f(b), the function we are trying to find the zero for.
+ // Note: passing b^N as a parameter so it only has to be calculated once
+ private static double f(double b, long t, double bn) {
+ return (1 - bn) / (1 - b) - t;
+ }
+
+ // Evaluate f'(b), the derivative of the function we are trying to find the zero for.
+ // Note: passing f(b) and b^N as parameters for efficiency
+ private static double fp(double b, long t, int n, double fb, double bn) {
+ return (fb + t - n * bn / b) / (1 - b);
+ }
+}