mobile/android/base/fxa/sync/FxAccountSchedulePolicy.java

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:35b4ab0a55c1
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5 package org.mozilla.gecko.fxa.sync;
6
7 import org.mozilla.gecko.background.common.log.Logger;
8 import org.mozilla.gecko.db.BrowserContract;
9 import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
10 import org.mozilla.gecko.fxa.login.State.Action;
11 import org.mozilla.gecko.sync.BackoffHandler;
12
13 import android.accounts.Account;
14 import android.content.ContentResolver;
15 import android.content.Context;
16 import android.os.Bundle;
17
18 public class FxAccountSchedulePolicy implements SchedulePolicy {
19 private static final String LOG_TAG = "FxAccountSchedulePolicy";
20
21 // Our poll intervals are used to trigger automatic background syncs
22 // in the absence of user activity.
23 //
24 // We also receive sync requests as a result of network tickles, so
25 // these intervals are long, with the exception of the rapid polling
26 // while we wait for verification: if we're waiting for the user to
27 // click on a verification link, we sync very often in order to detect
28 // a change in state.
29 //
30 // In the case of unverified -> unverified (no transition), this should be
31 // very close to a single HTTP request (with the SyncAdapter overhead, of
32 // course, but that's not wildly different from alarm manager overhead).
33 //
34 // The /account/status endpoint is HAWK authed by sessionToken, so we still
35 // have to do some crypto no matter what.
36
37 // TODO: only do this for a while...
38 public static final long POLL_INTERVAL_PENDING_VERIFICATION = 60; // 1 minute.
39
40 // If we're in some kind of error state, there's no point trying often.
41 // This is not the same as a server-imposed backoff, which will be
42 // reflected dynamically.
43 public static final long POLL_INTERVAL_ERROR_STATE_SEC = 24 * 60 * 60; // 24 hours.
44
45 // If we're the only device, just sync once or twice a day in case that
46 // changes.
47 public static final long POLL_INTERVAL_SINGLE_DEVICE_SEC = 18 * 60 * 60; // 18 hours.
48
49 // And if we know there are other devices, let's sync often enough that
50 // we'll be more likely to be caught up (even if not completely) by the
51 // time you next use this device. This is also achieved via Android's
52 // network tickles.
53 public static final long POLL_INTERVAL_MULTI_DEVICE_SEC = 12 * 60 * 60; // 12 hours.
54
55 // This is used solely as an optimization for backoff handling, so it's not
56 // persisted.
57 private static volatile long POLL_INTERVAL_CURRENT_SEC = POLL_INTERVAL_SINGLE_DEVICE_SEC;
58
59 // Never sync more frequently than this, unless forced.
60 // This is to avoid overly-frequent syncs during active browsing.
61 public static final long RATE_LIMIT_FUNDAMENTAL_SEC = 90; // 90 seconds.
62
63 /**
64 * We are prompted to sync by several inputs:
65 * * Periodic syncs that we schedule at long intervals. See the POLL constants.
66 * * Network-tickle-based syncs that Android starts.
67 * * Upload-only syncs that are caused by local database writes.
68 *
69 * We rate-limit periodic and network-sourced events with this constant.
70 * We rate limit <b>both</b> with {@link FxAccountSchedulePolicy#RATE_LIMIT_FUNDAMENTAL_SEC}.
71 */
72 public static final long RATE_LIMIT_BACKGROUND_SEC = 60 * 60; // 1 hour.
73
74 private final AndroidFxAccount account;
75 private final Context context;
76
77 public FxAccountSchedulePolicy(Context context, AndroidFxAccount account) {
78 this.account = account;
79 this.context = context;
80 }
81
82 /**
83 * Return a millisecond timestamp in the future, offset from the current
84 * time by the provided amount.
85 * @param millis the duration by which to delay
86 * @return a timestamp.
87 */
88 private static long delay(long millis) {
89 return System.currentTimeMillis() + millis;
90 }
91
92 /**
93 * Updates the existing system periodic sync interval to the specified duration.
94 *
95 * @param intervalSeconds the requested period, which Android will vary by up to 4%.
96 */
97 protected void requestPeriodicSync(final long intervalSeconds) {
98 final String authority = BrowserContract.AUTHORITY;
99 final Account account = this.account.getAndroidAccount();
100 this.context.getContentResolver();
101 Logger.info(LOG_TAG, "Scheduling periodic sync for " + intervalSeconds + ".");
102 ContentResolver.addPeriodicSync(account, authority, Bundle.EMPTY, intervalSeconds);
103 POLL_INTERVAL_CURRENT_SEC = intervalSeconds;
104 }
105
106 @Override
107 public void onSuccessfulSync(int otherClientsCount) {
108 // This undoes the change made in observeBackoffMillis -- once we hit backoff we'll
109 // periodically sync at the backoff duration, but as soon as we succeed we'll switch
110 // into the client-count-dependent interval.
111 long interval = (otherClientsCount > 0) ? POLL_INTERVAL_MULTI_DEVICE_SEC : POLL_INTERVAL_SINGLE_DEVICE_SEC;
112 requestPeriodicSync(interval);
113 }
114
115 @Override
116 public void onHandleFinal(Action needed) {
117 switch (needed) {
118 case NeedsPassword:
119 case NeedsUpgrade:
120 requestPeriodicSync(POLL_INTERVAL_ERROR_STATE_SEC);
121 break;
122 case NeedsVerification:
123 requestPeriodicSync(POLL_INTERVAL_PENDING_VERIFICATION);
124 break;
125 case None:
126 // No action needed: we'll set the periodic sync interval
127 // when the sync finishes, via the SessionCallback.
128 break;
129 }
130 }
131
132 @Override
133 public void onUpgradeRequired() {
134 // TODO: this shouldn't occur in FxA, but when we upgrade we
135 // need to reduce the interval again.
136 requestPeriodicSync(POLL_INTERVAL_ERROR_STATE_SEC);
137 }
138
139 @Override
140 public void onUnauthorized() {
141 // TODO: this shouldn't occur in FxA, but when we fix our credentials
142 // we need to reduce the interval again.
143 requestPeriodicSync(POLL_INTERVAL_ERROR_STATE_SEC);
144 }
145
146 @Override
147 public void configureBackoffMillisOnBackoff(BackoffHandler backoffHandler, long backoffMillis, boolean onlyExtend) {
148 if (onlyExtend) {
149 backoffHandler.extendEarliestNextRequest(delay(backoffMillis));
150 } else {
151 backoffHandler.setEarliestNextRequest(delay(backoffMillis));
152 }
153
154 // Yes, we might be part-way through the interval, in which case the backoff
155 // code will do its job. But we certainly don't want to reduce the interval
156 // if we're given a small backoff instruction.
157 // We'll reset the poll interval next time we sync without a backoff instruction.
158 if (backoffMillis > (POLL_INTERVAL_CURRENT_SEC * 1000)) {
159 // Slightly inflate the backoff duration to ensure that a fuzzed
160 // periodic sync doesn't occur before our backoff has passed. Android
161 // 19+ default to a 4% fuzz factor.
162 requestPeriodicSync((long) Math.ceil((1.05 * backoffMillis) / 1000));
163 }
164 }
165
166 /**
167 * Accepts two {@link BackoffHandler} instances as input. These are used
168 * respectively to track fundamental rate limiting, and to separately
169 * rate-limit periodic and network-tickled syncs.
170 */
171 @Override
172 public void configureBackoffMillisBeforeSyncing(BackoffHandler fundamentalRateHandler, BackoffHandler backgroundRateHandler) {
173 fundamentalRateHandler.setEarliestNextRequest(delay(RATE_LIMIT_FUNDAMENTAL_SEC * 1000));
174 backgroundRateHandler.setEarliestNextRequest(delay(RATE_LIMIT_BACKGROUND_SEC * 1000));
175 }
176 }

mercurial