Merge "DO NOT MERGE: Merge Oreo MR1 into master"
diff --git a/common/java/com/android/common/OperationScheduler.java b/common/java/com/android/common/OperationScheduler.java
index 5a8dce8..be92d24 100644
--- a/common/java/com/android/common/OperationScheduler.java
+++ b/common/java/com/android/common/OperationScheduler.java
@@ -183,11 +183,8 @@
                 (options.backoffIncrementalMillis * errorCount) +
                 (((long)options.backoffExponentialMillis) << shift);
 
-            // Treat backoff like a moratorium: don't let the backoff
-            // time grow too large.
-            if (moratoriumTimeMillis > 0 && backoff > moratoriumTimeMillis) {
-                backoff = moratoriumTimeMillis;
-            }
+            // Treat backoff like a moratorium: don't let the backoff time grow too large.
+            backoff = Math.min(backoff, options.maxMoratoriumMillis);
 
             time = Math.max(time, lastErrorTimeMillis + backoff);
         }
diff --git a/common/tests/src/com/android/common/OperationSchedulerTest.java b/common/tests/src/com/android/common/OperationSchedulerTest.java
index 87e2cd8..a25544a 100644
--- a/common/tests/src/com/android/common/OperationSchedulerTest.java
+++ b/common/tests/src/com/android/common/OperationSchedulerTest.java
@@ -155,6 +155,29 @@
         assertEquals(beforeError + 84100, scheduler.getNextTimeMillis(options));
     }
 
+    @MediumTest
+    public void testExponentialBackoffBoundedByMoratorium() throws Exception {
+        TimeTravelScheduler scheduler = new TimeTravelScheduler();
+        scheduler.setTriggerTimeMillis(0);
+        scheduler.setEnabledState(true);
+        scheduler.timeMillis = System.currentTimeMillis();
+
+        OperationScheduler.Options options = new OperationScheduler.Options();
+        options.backoffFixedMillis = 100;
+        options.backoffIncrementalMillis = 1000;
+        options.backoffExponentialMillis = 10000;
+        options.maxMoratoriumMillis = 24 * 3600 * 1000;
+
+        for(int i = 1; i < 31; i++) {
+            // report an error - increments the errorCount
+            scheduler.onTransientError();
+            final long nextTime = scheduler.getNextTimeMillis(options);
+            final long timeAfterOperation = System.currentTimeMillis();
+            assertTrue("Backoff is not bounded by max moratorium for iteration " + i,
+                    nextTime < timeAfterOperation + options.maxMoratoriumMillis);
+        }
+    }
+
     @SmallTest
     public void testParseOptions() throws Exception {
          OperationScheduler.Options options = new OperationScheduler.Options();