Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(8)

Unified Diff: chrome/android/java_staging/src/org/chromium/chrome/browser/omaha/ExponentialBackoffScheduler.java

Issue 1141283003: Upstream oodles of Chrome for Android code into Chromium. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: final patch? Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/android/java_staging/src/org/chromium/chrome/browser/omaha/ExponentialBackoffScheduler.java
diff --git a/chrome/android/java_staging/src/org/chromium/chrome/browser/omaha/ExponentialBackoffScheduler.java b/chrome/android/java_staging/src/org/chromium/chrome/browser/omaha/ExponentialBackoffScheduler.java
new file mode 100644
index 0000000000000000000000000000000000000000..d36d8760dbfdd7c361efb02ab05de37a2ebaff9e
--- /dev/null
+++ b/chrome/android/java_staging/src/org/chromium/chrome/browser/omaha/ExponentialBackoffScheduler.java
@@ -0,0 +1,197 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.omaha;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.util.Log;
+
+import org.chromium.base.VisibleForTesting;
+
+import java.util.Date;
+import java.util.Random;
+
+import javax.annotation.concurrent.NotThreadSafe;
+
+/**
+ * Manages a timer that implements exponential backoff for failed attempts.
+ *
+ * The first timer will fire after BASE_MILLISECONDS. On a failure, the timer is changed to
+ * (randomInteger[0, 2^failures) + 1) * BASE_MILLISECONDS. MAX_MILLISECONDS is used to ensure that
+ * you aren't waiting years for a timer to fire.
+ *
+ * The state is stored in shared preferences to ensure that they are kept after the device sleeps.
+ * Because multiple ExponentialBackoffSchedulers can be used by different components,
+ * the owning class must set the preference name.
+ *
+ * Timestamps are recorded in RTC to avoid situations where the phone is rebooted, messing up
+ * any timestamps generated using elapsedRealtime().
+ *
+ * This class is not thread-safe because any two different classes could be accessing the same
+ * SharedPreferences.
+ *
+ * TODO(dfalcantara): Consider making this an AlarmManagerHelper class to manage general alarms.
+ */
+@NotThreadSafe
+public class ExponentialBackoffScheduler {
+ private static final String TAG = "ExponentialBackoffScheduler";
+
+ private static final String PREFERENCE_DELAY = "delay";
+ private static final String PREFERENCE_FAILED_ATTEMPTS = "backoffFailedAttempts";
+
+ private static Random sRandom = new Random();
+
+ private static final int MAX_EXPONENT = 10;
+
+ private final long mBaseMilliseconds;
+ private final long mMaxMilliseconds;
+ private final Context mContext;
+ private final String mPreferencePackage;
+
+ /**
+ * Creates a new scheduler.
+ * @param packageName The name under which to store its state in SharedPreferences.
+ * @param context The application's context.
+ * @param baseMilliseconds Used to calculate random backoff times.
+ * @param maxMilliseconds The absolute maximum delay allowed.
+ */
+ public ExponentialBackoffScheduler(String packageName, Context context, long baseMilliseconds,
+ long maxMilliseconds) {
+ mPreferencePackage = packageName;
+ mContext = context;
+ mBaseMilliseconds = baseMilliseconds;
+ mMaxMilliseconds = maxMilliseconds;
+ }
+
+ /**
+ * Creates an alarm to fire the specified intent after a random delay.
+ * @param intent The intent to fire.
+ * @return the timestamp of the scheduled intent
+ */
+ public long createAlarm(Intent intent) {
+ long delay = generateRandomDelay();
+ long timestamp = delay + getCurrentTime();
+ return createAlarm(intent, timestamp);
+ }
+
+ /**
+ * Creates an alarm to fire the specified intent at the specified time.
+ * @param intent The intent to fire.
+ * @return the timestamp of the scheduled intent
+ */
+ public long createAlarm(Intent intent, long timestamp) {
+ PendingIntent retryPIntent = PendingIntent.getService(mContext, 0, intent, 0);
+ AlarmManager am = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+ setAlarm(am, timestamp, retryPIntent);
+ return timestamp;
+ }
+
+ /**
+ * Attempts to cancel any alarms set using the given Intent.
+ * @param scheduledIntent Intent that may have been previously scheduled.
+ * @return whether or not an alarm was canceled.
+ */
+ public boolean cancelAlarm(Intent scheduledIntent) {
+ PendingIntent pendingIntent = PendingIntent.getService(mContext, 0, scheduledIntent,
+ PendingIntent.FLAG_NO_CREATE);
+ if (pendingIntent != null) {
+ AlarmManager am = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+ am.cancel(pendingIntent);
+ pendingIntent.cancel();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public int getNumFailedAttempts() {
+ SharedPreferences preferences = getSharedPreferences();
+ return preferences.getInt(PREFERENCE_FAILED_ATTEMPTS, 0);
+ }
+
+ public void increaseFailedAttempts() {
+ SharedPreferences preferences = getSharedPreferences();
+ int numFailedAttempts = getNumFailedAttempts() + 1;
+ preferences.edit()
+ .putInt(PREFERENCE_FAILED_ATTEMPTS, numFailedAttempts)
+ .apply();
+ }
+
+ public void resetFailedAttempts() {
+ SharedPreferences preferences = getSharedPreferences();
+ preferences.edit()
+ .putInt(PREFERENCE_FAILED_ATTEMPTS, 0)
+ .apply();
+ }
+
+ /**
+ * Returns a timestamp representing now, according to the backoff scheduler.
+ */
+ public long getCurrentTime() {
+ return System.currentTimeMillis();
+ }
+
+ /**
+ * Returns the delay used to generate the last alarm. If no previous alarm was generated,
+ * return the base delay.
+ */
+ public long getGeneratedDelay() {
+ SharedPreferences preferences = getSharedPreferences();
+ return preferences.getLong(PREFERENCE_DELAY, mBaseMilliseconds);
+ }
+
+ /**
+ * Sets an alarm in the alarm manager.
+ */
+ @VisibleForTesting
+ protected void setAlarm(AlarmManager am, long timestamp, PendingIntent retryPIntent) {
+ Log.v(TAG, "now(" + new Date(getCurrentTime()) + ") refiringAt("
+ + new Date(timestamp) + ")");
+ am.set(AlarmManager.RTC, timestamp, retryPIntent);
+ }
+
+ /**
+ * Determines the amount of time to wait for the current delay, then saves it.
+ * @return the number of milliseconds to wait.
+ */
+ private long generateRandomDelay() {
+ long delay;
+ int numFailedAttempts = getNumFailedAttempts();
+ if (numFailedAttempts == 0) {
+ delay = Math.min(mBaseMilliseconds, mMaxMilliseconds);
+ } else {
+ int backoffCoefficient = computeConstrainedBackoffCoefficient(numFailedAttempts);
+ delay = Math.min(backoffCoefficient * mBaseMilliseconds, mMaxMilliseconds);
+ }
+
+ // Save the delay for sanity checks.
+ SharedPreferences preferences = getSharedPreferences();
+ preferences.edit()
+ .putLong(PREFERENCE_DELAY, delay)
+ .apply();
+ return delay;
+ }
+
+ /**
+ * Calculates a random coefficient based on the number of cumulative failed attempts.
+ * @param numFailedAttempts Number of cumulative failed attempts
+ * @return A random number between 1 and 2^N, where N is the smallest value of MAX_EXPONENT and
+ * numFailedAttempts
+ */
+ private int computeConstrainedBackoffCoefficient(int numFailedAttempts) {
+ int n = Math.min(MAX_EXPONENT, numFailedAttempts);
+ int twoToThePowerOfN = 1 << n;
+ return sRandom.nextInt(twoToThePowerOfN) + 1;
+ }
+
+ private SharedPreferences getSharedPreferences() {
+ SharedPreferences preferences =
+ mContext.getSharedPreferences(mPreferencePackage, Context.MODE_PRIVATE);
+ return preferences;
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698