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

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/mobile/android/base/fxa/sync/FxAccountSchedulePolicy.java	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,176 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +package org.mozilla.gecko.fxa.sync;
     1.9 +
    1.10 +import org.mozilla.gecko.background.common.log.Logger;
    1.11 +import org.mozilla.gecko.db.BrowserContract;
    1.12 +import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
    1.13 +import org.mozilla.gecko.fxa.login.State.Action;
    1.14 +import org.mozilla.gecko.sync.BackoffHandler;
    1.15 +
    1.16 +import android.accounts.Account;
    1.17 +import android.content.ContentResolver;
    1.18 +import android.content.Context;
    1.19 +import android.os.Bundle;
    1.20 +
    1.21 +public class FxAccountSchedulePolicy implements SchedulePolicy {
    1.22 +  private static final String LOG_TAG = "FxAccountSchedulePolicy";
    1.23 +
    1.24 +  // Our poll intervals are used to trigger automatic background syncs
    1.25 +  // in the absence of user activity.
    1.26 +  //
    1.27 +  // We also receive sync requests as a result of network tickles, so
    1.28 +  // these intervals are long, with the exception of the rapid polling
    1.29 +  // while we wait for verification: if we're waiting for the user to
    1.30 +  // click on a verification link, we sync very often in order to detect
    1.31 +  // a change in state.
    1.32 +  //
    1.33 +  // In the case of unverified -> unverified (no transition), this should be
    1.34 +  // very close to a single HTTP request (with the SyncAdapter overhead, of
    1.35 +  // course, but that's not wildly different from alarm manager overhead).
    1.36 +  //
    1.37 +  // The /account/status endpoint is HAWK authed by sessionToken, so we still
    1.38 +  // have to do some crypto no matter what.
    1.39 +
    1.40 +  // TODO: only do this for a while...
    1.41 +  public static final long POLL_INTERVAL_PENDING_VERIFICATION = 60;         // 1 minute.
    1.42 +
    1.43 +  // If we're in some kind of error state, there's no point trying often.
    1.44 +  // This is not the same as a server-imposed backoff, which will be
    1.45 +  // reflected dynamically.
    1.46 +  public static final long POLL_INTERVAL_ERROR_STATE_SEC = 24 * 60 * 60;    // 24 hours.
    1.47 +
    1.48 +  // If we're the only device, just sync once or twice a day in case that
    1.49 +  // changes.
    1.50 +  public static final long POLL_INTERVAL_SINGLE_DEVICE_SEC = 18 * 60 * 60;  // 18 hours.
    1.51 +
    1.52 +  // And if we know there are other devices, let's sync often enough that
    1.53 +  // we'll be more likely to be caught up (even if not completely) by the
    1.54 +  // time you next use this device. This is also achieved via Android's
    1.55 +  // network tickles.
    1.56 +  public static final long POLL_INTERVAL_MULTI_DEVICE_SEC = 12 * 60 * 60;   // 12 hours.
    1.57 +
    1.58 +  // This is used solely as an optimization for backoff handling, so it's not
    1.59 +  // persisted.
    1.60 +  private static volatile long POLL_INTERVAL_CURRENT_SEC = POLL_INTERVAL_SINGLE_DEVICE_SEC;
    1.61 +
    1.62 +  // Never sync more frequently than this, unless forced.
    1.63 +  // This is to avoid overly-frequent syncs during active browsing.
    1.64 +  public static final long RATE_LIMIT_FUNDAMENTAL_SEC = 90;                 // 90 seconds.
    1.65 +
    1.66 +  /**
    1.67 +   * We are prompted to sync by several inputs:
    1.68 +   * * Periodic syncs that we schedule at long intervals. See the POLL constants.
    1.69 +   * * Network-tickle-based syncs that Android starts.
    1.70 +   * * Upload-only syncs that are caused by local database writes.
    1.71 +   *
    1.72 +   * We rate-limit periodic and network-sourced events with this constant.
    1.73 +   * We rate limit <b>both</b> with {@link FxAccountSchedulePolicy#RATE_LIMIT_FUNDAMENTAL_SEC}.
    1.74 +   */
    1.75 +  public static final long RATE_LIMIT_BACKGROUND_SEC = 60 * 60;             // 1 hour.
    1.76 +
    1.77 +  private final AndroidFxAccount account;
    1.78 +  private final Context context;
    1.79 +
    1.80 +  public FxAccountSchedulePolicy(Context context, AndroidFxAccount account) {
    1.81 +    this.account = account;
    1.82 +    this.context = context;
    1.83 +  }
    1.84 +
    1.85 +  /**
    1.86 +   * Return a millisecond timestamp in the future, offset from the current
    1.87 +   * time by the provided amount.
    1.88 +   * @param millis the duration by which to delay
    1.89 +   * @return a timestamp.
    1.90 +   */
    1.91 +  private static long delay(long millis) {
    1.92 +    return System.currentTimeMillis() + millis;
    1.93 +  }
    1.94 +
    1.95 +  /**
    1.96 +   * Updates the existing system periodic sync interval to the specified duration.
    1.97 +   *
    1.98 +   * @param intervalSeconds the requested period, which Android will vary by up to 4%.
    1.99 +   */
   1.100 +  protected void requestPeriodicSync(final long intervalSeconds) {
   1.101 +    final String authority = BrowserContract.AUTHORITY;
   1.102 +    final Account account = this.account.getAndroidAccount();
   1.103 +    this.context.getContentResolver();
   1.104 +    Logger.info(LOG_TAG, "Scheduling periodic sync for " + intervalSeconds + ".");
   1.105 +    ContentResolver.addPeriodicSync(account, authority, Bundle.EMPTY, intervalSeconds);
   1.106 +    POLL_INTERVAL_CURRENT_SEC = intervalSeconds;
   1.107 +  }
   1.108 +
   1.109 +  @Override
   1.110 +  public void onSuccessfulSync(int otherClientsCount) {
   1.111 +    // This undoes the change made in observeBackoffMillis -- once we hit backoff we'll
   1.112 +    // periodically sync at the backoff duration, but as soon as we succeed we'll switch
   1.113 +    // into the client-count-dependent interval.
   1.114 +    long interval = (otherClientsCount > 0) ? POLL_INTERVAL_MULTI_DEVICE_SEC : POLL_INTERVAL_SINGLE_DEVICE_SEC;
   1.115 +    requestPeriodicSync(interval);
   1.116 +  }
   1.117 +
   1.118 +  @Override
   1.119 +  public void onHandleFinal(Action needed) {
   1.120 +    switch (needed) {
   1.121 +    case NeedsPassword:
   1.122 +    case NeedsUpgrade:
   1.123 +      requestPeriodicSync(POLL_INTERVAL_ERROR_STATE_SEC);
   1.124 +      break;
   1.125 +    case NeedsVerification:
   1.126 +      requestPeriodicSync(POLL_INTERVAL_PENDING_VERIFICATION);
   1.127 +      break;
   1.128 +    case None:
   1.129 +      // No action needed: we'll set the periodic sync interval
   1.130 +      // when the sync finishes, via the SessionCallback.
   1.131 +      break;
   1.132 +    }
   1.133 +  }
   1.134 +
   1.135 +  @Override
   1.136 +  public void onUpgradeRequired() {
   1.137 +    // TODO: this shouldn't occur in FxA, but when we upgrade we
   1.138 +    // need to reduce the interval again.
   1.139 +    requestPeriodicSync(POLL_INTERVAL_ERROR_STATE_SEC);
   1.140 +  }
   1.141 +
   1.142 +  @Override
   1.143 +  public void onUnauthorized() {
   1.144 +    // TODO: this shouldn't occur in FxA, but when we fix our credentials
   1.145 +    // we need to reduce the interval again.
   1.146 +    requestPeriodicSync(POLL_INTERVAL_ERROR_STATE_SEC);
   1.147 +  }
   1.148 +
   1.149 +  @Override
   1.150 +  public void configureBackoffMillisOnBackoff(BackoffHandler backoffHandler, long backoffMillis, boolean onlyExtend) {
   1.151 +    if (onlyExtend) {
   1.152 +      backoffHandler.extendEarliestNextRequest(delay(backoffMillis));
   1.153 +    } else {
   1.154 +      backoffHandler.setEarliestNextRequest(delay(backoffMillis));
   1.155 +    }
   1.156 +
   1.157 +    // Yes, we might be part-way through the interval, in which case the backoff
   1.158 +    // code will do its job. But we certainly don't want to reduce the interval
   1.159 +    // if we're given a small backoff instruction.
   1.160 +    // We'll reset the poll interval next time we sync without a backoff instruction.
   1.161 +    if (backoffMillis > (POLL_INTERVAL_CURRENT_SEC * 1000)) {
   1.162 +      // Slightly inflate the backoff duration to ensure that a fuzzed
   1.163 +      // periodic sync doesn't occur before our backoff has passed. Android
   1.164 +      // 19+ default to a 4% fuzz factor.
   1.165 +      requestPeriodicSync((long) Math.ceil((1.05 * backoffMillis) / 1000));
   1.166 +    }
   1.167 +  }
   1.168 +
   1.169 +  /**
   1.170 +   * Accepts two {@link BackoffHandler} instances as input. These are used
   1.171 +   * respectively to track fundamental rate limiting, and to separately
   1.172 +   * rate-limit periodic and network-tickled syncs.
   1.173 +   */
   1.174 +  @Override
   1.175 +  public void configureBackoffMillisBeforeSyncing(BackoffHandler fundamentalRateHandler, BackoffHandler backgroundRateHandler) {
   1.176 +    fundamentalRateHandler.setEarliestNextRequest(delay(RATE_LIMIT_FUNDAMENTAL_SEC * 1000));
   1.177 +    backgroundRateHandler.setEarliestNextRequest(delay(RATE_LIMIT_BACKGROUND_SEC * 1000));
   1.178 +  }
   1.179 +}
   1.180 \ No newline at end of file

mercurial