OperationScheduler.java revision 2991425379efd0bc29bce6cd718e5833cc6aa59b
1494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor/* 2494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * Copyright (C) 2009 The Android Open Source Project 3494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * 4494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * Licensed under the Apache License, Version 2.0 (the "License"); 5494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * you may not use this file except in compliance with the License. 6494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * You may obtain a copy of the License at 7494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * 8494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * http://www.apache.org/licenses/LICENSE-2.0 9494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * 10494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * Unless required by applicable law or agreed to in writing, software 11494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * distributed under the License is distributed on an "AS IS" BASIS, 12494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * See the License for the specific language governing permissions and 14494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * limitations under the License. 15494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor */ 16494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor 17494a1dc5e95e22839b06bf7de84426094a38691aDan Egnorpackage com.android.common; 18494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor 19494a1dc5e95e22839b06bf7de84426094a38691aDan Egnorimport android.content.SharedPreferences; 20494a1dc5e95e22839b06bf7de84426094a38691aDan Egnorimport android.text.format.Time; 21494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor 22494a1dc5e95e22839b06bf7de84426094a38691aDan Egnorimport java.util.Map; 23494a1dc5e95e22839b06bf7de84426094a38691aDan Egnorimport java.util.TreeSet; 24494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor 25494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor/** 26494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * Tracks the success/failure history of a particular network operation in 27494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * persistent storage and computes retry strategy accordingly. Handles 28494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * exponential backoff, periodic rescheduling, event-driven triggering, 29494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * retry-after moratorium intervals, etc. based on caller-specified parameters. 30494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * 31494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * <p>This class does not directly perform or invoke any operations, 32494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * it only keeps track of the schedule. Somebody else needs to call 33494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * {@link #getNextTimeMillis()} as appropriate and do the actual work. 34494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor */ 35494a1dc5e95e22839b06bf7de84426094a38691aDan Egnorpublic class OperationScheduler { 36494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor /** Tunable parameter options for {@link #getNextTimeMillis}. */ 37494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor public static class Options { 38494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor /** Wait this long after every error before retrying. */ 39494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor public long backoffFixedMillis = 0; 40494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor 41494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor /** Wait this long times the number of consecutive errors so far before retrying. */ 42494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor public long backoffIncrementalMillis = 5000; 43494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor 44494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor /** Maximum duration of moratorium to honor. Mostly an issue for clock rollbacks. */ 45494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor public long maxMoratoriumMillis = 24 * 3600 * 1000; 46494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor 47494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor /** Minimum duration after success to wait before allowing another trigger. */ 48494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor public long minTriggerMillis = 0; 49494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor 50494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor /** Automatically trigger this long after the last success. */ 51494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor public long periodicIntervalMillis = 0; 52494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor 53494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor @Override 54494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor public String toString() { 55494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor return String.format( 56494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor "OperationScheduler.Options[backoff=%.1f+%.1f max=%.1f min=%.1f period=%.1f]", 57494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor backoffFixedMillis / 1000.0, backoffIncrementalMillis / 1000.0, 58494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor maxMoratoriumMillis / 1000.0, minTriggerMillis / 1000.0, 59494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor periodicIntervalMillis / 1000.0); 60494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor } 61494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor } 62494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor 63494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor private static final String PREFIX = "OperationScheduler_"; 64494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor private final SharedPreferences mStorage; 65494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor 66494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor /** 67494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * Initialize the scheduler state. 68494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * @param storage to use for recording the state of operations across restarts/reboots 69494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor */ 70494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor public OperationScheduler(SharedPreferences storage) { 71494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor mStorage = storage; 72494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor } 73494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor 74494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor /** 75494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * Parse scheduler options supplied in this string form: 76494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * 77494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * <pre> 78494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * backoff=(fixed)+(incremental) max=(maxmoratorium) min=(mintrigger) [period=](interval) 79494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * </pre> 80494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * 81494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * All values are times in (possibly fractional) <em>seconds</em> (not milliseconds). 82494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * Omitted settings are left at whatever existing default value was passed in. 83494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * 84494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * <p> 85494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * The default options: <code>backoff=0+5 max=86400 min=0 period=0</code><br> 86494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * Fractions are OK: <code>backoff=+2.5 period=10.0</code><br> 87494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * The "period=" can be omitted: <code>3600</code><br> 88494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * 89494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * @param spec describing some or all scheduler options. 90494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * @param options to update with parsed values. 91494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * @return the options passed in (for convenience) 92494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * @throws IllegalArgumentException if the syntax is invalid 93494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor */ 94494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor public static Options parseOptions(String spec, Options options) 95494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor throws IllegalArgumentException { 96494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor for (String param : spec.split(" +")) { 97494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor if (param.length() == 0) continue; 98494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor if (param.startsWith("backoff=")) { 99494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor int plus = param.indexOf('+', 8); 100494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor if (plus < 0) { 101494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor options.backoffFixedMillis = parseSeconds(param.substring(8)); 102494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor } else { 103494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor if (plus > 8) { 104494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor options.backoffFixedMillis = parseSeconds(param.substring(8, plus)); 105494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor } 106494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor options.backoffIncrementalMillis = parseSeconds(param.substring(plus + 1)); 107494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor } 108494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor } else if (param.startsWith("max=")) { 109494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor options.maxMoratoriumMillis = parseSeconds(param.substring(4)); 110494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor } else if (param.startsWith("min=")) { 111494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor options.minTriggerMillis = parseSeconds(param.substring(4)); 112494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor } else if (param.startsWith("period=")) { 113494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor options.periodicIntervalMillis = parseSeconds(param.substring(7)); 114494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor } else { 115494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor options.periodicIntervalMillis = parseSeconds(param); 116494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor } 117494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor } 118494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor return options; 119494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor } 120494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor 121494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor private static long parseSeconds(String param) throws NumberFormatException { 122494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor return (long) (Float.parseFloat(param) * 1000); 123494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor } 124494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor 125494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor /** 126494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * Compute the time of the next operation. Does not modify any state. 127494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * 128494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * @param options to use for this computation. 129494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * @return the wall clock time ({@link System#currentTimeMillis()}) when the 130494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * next operation should be attempted -- immediately, if the return value is 131494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * before the current time. 132494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor */ 133494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor public long getNextTimeMillis(Options options) { 134494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor boolean enabledState = mStorage.getBoolean(PREFIX + "enabledState", true); 135494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor if (!enabledState) return Long.MAX_VALUE; 136494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor 137494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor boolean permanentError = mStorage.getBoolean(PREFIX + "permanentError", false); 138494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor if (permanentError) return Long.MAX_VALUE; 139494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor 140494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor // We do quite a bit of limiting to prevent a clock rollback from totally 141494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor // hosing the scheduler. Times which are supposed to be in the past are 142494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor // clipped to the current time so we don't languish forever. 143494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor 144494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor int errorCount = mStorage.getInt(PREFIX + "errorCount", 0); 145494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor long now = System.currentTimeMillis(); 146494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor long lastSuccessTimeMillis = getTimeBefore(PREFIX + "lastSuccessTimeMillis", now); 147494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor long lastErrorTimeMillis = getTimeBefore(PREFIX + "lastErrorTimeMillis", now); 148494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor long triggerTimeMillis = mStorage.getLong(PREFIX + "triggerTimeMillis", Long.MAX_VALUE); 149494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor long moratoriumSetMillis = mStorage.getLong(PREFIX + "moratoriumSetTimeMillis", 0); 150494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor long moratoriumTimeMillis = getTimeBefore(PREFIX + "moratoriumTimeMillis", 151494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor moratoriumSetMillis + options.maxMoratoriumMillis); 152494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor 153494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor long time = triggerTimeMillis; 154494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor if (options.periodicIntervalMillis > 0) { 155494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor time = Math.min(time, lastSuccessTimeMillis + options.periodicIntervalMillis); 156494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor } 157494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor if (time >= moratoriumTimeMillis - options.maxMoratoriumMillis) { 158494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor time = Math.max(time, moratoriumTimeMillis); 159494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor } 160494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor time = Math.max(time, lastSuccessTimeMillis + options.minTriggerMillis); 161494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor time = Math.max(time, lastErrorTimeMillis + options.backoffFixedMillis + 162494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor options.backoffIncrementalMillis * errorCount); 163494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor return time; 164494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor } 165494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor 166494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor /** 167494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * Fetch a {@link SharedPreferences} property, but force it to be before 168494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * a certain time, updating the value if necessary. This is to recover 169494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * gracefully from clock rollbacks which could otherwise strand our timers. 170494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * 171494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * @param name of SharedPreferences key 172494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * @param max time to allow in result 173494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * @return current value attached to key (default 0), limited by max 174494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor */ 175494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor private long getTimeBefore(String name, long max) { 176494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor long time = mStorage.getLong(name, 0); 177494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor if (time > max) mStorage.edit().putLong(name, (time = max)).commit(); 178494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor return time; 179494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor } 180494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor 181494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor /** 182494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * Request an operation to be performed at a certain time. The actual 183494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * scheduled time may be affected by error backoff logic and defined 184494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * minimum intervals. 185494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * 186494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * @param millis wall clock time ({@link System#currentTimeMillis()}) to 187494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * trigger another operation; 0 to trigger immediately 188494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor */ 189494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor public void setTriggerTimeMillis(long millis) { 190494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor mStorage.edit().putLong(PREFIX + "triggerTimeMillis", millis).commit(); 191494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor } 192494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor 193494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor /** 194494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * Forbid any operations until after a certain (absolute) time. 195494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * Limited by {@link #Options.maxMoratoriumMillis}. 196494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * 197494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * @param millis wall clock time ({@link System#currentTimeMillis()}) to 198494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * wait before attempting any more operations; 0 to remove moratorium 199494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor */ 200494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor public void setMoratoriumTimeMillis(long millis) { 201494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor mStorage.edit() 202494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor .putLong(PREFIX + "moratoriumTimeMillis", millis) 203494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor .putLong(PREFIX + "moratoriumSetTimeMillis", System.currentTimeMillis()) 204494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor .commit(); 205494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor } 206494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor 207494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor /** 2082991425379efd0bc29bce6cd718e5833cc6aa59bDan Egnor * Forbid any operations until after a certain time, as specified in 2092991425379efd0bc29bce6cd718e5833cc6aa59bDan Egnor * the format used by the HTTP "Retry-After" header. 2102991425379efd0bc29bce6cd718e5833cc6aa59bDan Egnor * Limited by {@link #Options.maxMoratoriumMillis}. 2112991425379efd0bc29bce6cd718e5833cc6aa59bDan Egnor * 2122991425379efd0bc29bce6cd718e5833cc6aa59bDan Egnor * @param retryAfter moratorium time in HTTP format 2132991425379efd0bc29bce6cd718e5833cc6aa59bDan Egnor * @return true if a time was successfully parsed 2142991425379efd0bc29bce6cd718e5833cc6aa59bDan Egnor */ 2152991425379efd0bc29bce6cd718e5833cc6aa59bDan Egnor public boolean setMoratoriumTimeHttp(String retryAfter) { 2162991425379efd0bc29bce6cd718e5833cc6aa59bDan Egnor try { 2172991425379efd0bc29bce6cd718e5833cc6aa59bDan Egnor long ms = Long.valueOf(retryAfter) * 1000; 2182991425379efd0bc29bce6cd718e5833cc6aa59bDan Egnor setMoratoriumTimeMillis(ms + System.currentTimeMillis()); 2192991425379efd0bc29bce6cd718e5833cc6aa59bDan Egnor return true; 2202991425379efd0bc29bce6cd718e5833cc6aa59bDan Egnor } catch (NumberFormatException nfe) { 2212991425379efd0bc29bce6cd718e5833cc6aa59bDan Egnor try { 2222991425379efd0bc29bce6cd718e5833cc6aa59bDan Egnor setMoratoriumTimeMillis(HttpDateTime.parse(retryAfter)); 2232991425379efd0bc29bce6cd718e5833cc6aa59bDan Egnor return true; 2242991425379efd0bc29bce6cd718e5833cc6aa59bDan Egnor } catch (IllegalArgumentException iae) { 2252991425379efd0bc29bce6cd718e5833cc6aa59bDan Egnor return false; 2262991425379efd0bc29bce6cd718e5833cc6aa59bDan Egnor } 2272991425379efd0bc29bce6cd718e5833cc6aa59bDan Egnor } 2282991425379efd0bc29bce6cd718e5833cc6aa59bDan Egnor } 2292991425379efd0bc29bce6cd718e5833cc6aa59bDan Egnor 2302991425379efd0bc29bce6cd718e5833cc6aa59bDan Egnor /** 231494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * Enable or disable all operations. When disabled, all calls to 232494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * {@link #getNextTimeMillis()} return {@link Long#MAX_VALUE}. 233494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * Commonly used when data network availability goes up and down. 234494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * 235494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * @param enabled if operations can be performed 236494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor */ 237494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor public void setEnabledState(boolean enabled) { 238494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor mStorage.edit().putBoolean(PREFIX + "enabledState", enabled).commit(); 239494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor } 240494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor 241494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor /** 242494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * Report successful completion of an operation. Resets all error 243494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * counters, clears any trigger directives, and records the success. 244494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor */ 245494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor public void onSuccess() { 246494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor resetTransientError(); 247494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor resetPermanentError(); 248494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor long now = System.currentTimeMillis(); 249494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor mStorage.edit() 250494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor .remove(PREFIX + "errorCount") 251494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor .remove(PREFIX + "lastErrorTimeMillis") 252494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor .remove(PREFIX + "permanentError") 253494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor .remove(PREFIX + "triggerTimeMillis") 254494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor .putLong(PREFIX + "lastSuccessTimeMillis", now).commit(); 255494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor } 256494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor 257494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor /** 258494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * Report a transient error (usually a network failure). Increments 259494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * the error count and records the time of the latest error for backoff 260494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * purposes. 261494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor */ 262494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor public void onTransientError() { 263494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor long now = System.currentTimeMillis(); 264494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor mStorage.edit().putLong(PREFIX + "lastErrorTimeMillis", now).commit(); 265494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor mStorage.edit().putInt(PREFIX + "errorCount", 266494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor mStorage.getInt(PREFIX + "errorCount", 0) + 1).commit(); 267494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor } 268494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor 269494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor /** 270494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * Reset all transient error counts, allowing the next operation to proceed 271494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * immediately without backoff. Commonly used on network state changes, when 272494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * partial progress occurs (some data received), and in other circumstances 273494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * where there is reason to hope things might start working better. 274494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor */ 275494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor public void resetTransientError() { 276494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor mStorage.edit() 277494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor .remove(PREFIX + "lastErrorTimeMillis") 278494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor .remove(PREFIX + "errorCount").commit(); 279494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor } 280494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor 281494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor /** 282494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * Report a permanent error that will not go away until further notice. 283494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * No operation will be scheduled until {@link #resetPermanentError()} 284494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * is called. Commonly used for authentication failures (which are reset 285494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * when the accounts database is updated). 286494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor */ 287494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor public void onPermanentError() { 288494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor mStorage.edit().putBoolean(PREFIX + "permanentError", true).commit(); 289494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor } 290494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor 291494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor /** 292494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * Reset any permanent error status set by {@link #onPermanentError}, 293494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * allowing operations to be scheduled as normal. 294494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor */ 295494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor public void resetPermanentError() { 296494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor mStorage.edit().remove(PREFIX + "permanentError").commit(); 297494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor } 298494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor 299494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor /** 300494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor * Return a string description of the scheduler state for debugging. 301494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor */ 302494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor public String toString() { 303494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor StringBuilder out = new StringBuilder("[OperationScheduler:"); 304494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor for (String key : new TreeSet<String>(mStorage.getAll().keySet())) { // Sort keys 305494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor if (key.startsWith(PREFIX)) { 306494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor if (key.endsWith("TimeMillis")) { 307494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor Time time = new Time(); 308494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor time.set(mStorage.getLong(key, 0)); 309494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor out.append(" ").append(key.substring(PREFIX.length(), key.length() - 10)); 310494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor out.append("=").append(time.format("%Y-%m-%d/%H:%M:%S")); 311494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor } else { 312494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor out.append(" ").append(key.substring(PREFIX.length())); 313494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor out.append("=").append(mStorage.getAll().get(key).toString()); 314494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor } 315494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor } 316494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor } 317494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor return out.append("]").toString(); 318494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor } 319494a1dc5e95e22839b06bf7de84426094a38691aDan Egnor} 320