mobile/android/base/fxa/FirefoxAccounts.java

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 package org.mozilla.gecko.fxa;
michael@0 6
michael@0 7 import java.io.File;
michael@0 8 import java.util.EnumSet;
michael@0 9 import java.util.concurrent.CountDownLatch;
michael@0 10
michael@0 11 import org.mozilla.gecko.background.common.log.Logger;
michael@0 12 import org.mozilla.gecko.fxa.authenticator.AccountPickler;
michael@0 13 import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
michael@0 14 import org.mozilla.gecko.fxa.sync.FxAccountSyncAdapter;
michael@0 15 import org.mozilla.gecko.fxa.sync.FxAccountSyncStatusHelper;
michael@0 16 import org.mozilla.gecko.sync.ThreadPool;
michael@0 17 import org.mozilla.gecko.sync.Utils;
michael@0 18
michael@0 19 import android.accounts.Account;
michael@0 20 import android.accounts.AccountManager;
michael@0 21 import android.content.ContentResolver;
michael@0 22 import android.content.Context;
michael@0 23 import android.os.Bundle;
michael@0 24
michael@0 25 /**
michael@0 26 * Simple public accessors for Firefox account objects.
michael@0 27 */
michael@0 28 public class FirefoxAccounts {
michael@0 29 private static final String LOG_TAG = FirefoxAccounts.class.getSimpleName();
michael@0 30
michael@0 31 public enum SyncHint {
michael@0 32 /**
michael@0 33 * Hint that a requested sync is preferred immediately.
michael@0 34 * <p>
michael@0 35 * On many devices, not including <code>SCHEDULE_NOW</code> means a delay of
michael@0 36 * at least 30 seconds.
michael@0 37 */
michael@0 38 SCHEDULE_NOW,
michael@0 39
michael@0 40 /**
michael@0 41 * Hint that a requested sync may ignore local rate limiting.
michael@0 42 * <p>
michael@0 43 * This is just a hint; the actual requested sync may not obey the hint.
michael@0 44 */
michael@0 45 IGNORE_LOCAL_RATE_LIMIT,
michael@0 46
michael@0 47 /**
michael@0 48 * Hint that a requested sync may ignore remote server backoffs.
michael@0 49 * <p>
michael@0 50 * This is just a hint; the actual requested sync may not obey the hint.
michael@0 51 */
michael@0 52 IGNORE_REMOTE_SERVER_BACKOFF,
michael@0 53 }
michael@0 54
michael@0 55 public static final EnumSet<SyncHint> SOON = EnumSet.noneOf(SyncHint.class);
michael@0 56
michael@0 57 public static final EnumSet<SyncHint> NOW = EnumSet.of(
michael@0 58 SyncHint.SCHEDULE_NOW);
michael@0 59
michael@0 60 public static final EnumSet<SyncHint> FORCE = EnumSet.of(
michael@0 61 SyncHint.SCHEDULE_NOW,
michael@0 62 SyncHint.IGNORE_LOCAL_RATE_LIMIT,
michael@0 63 SyncHint.IGNORE_REMOTE_SERVER_BACKOFF);
michael@0 64
michael@0 65 public interface SyncStatusListener {
michael@0 66 public Context getContext();
michael@0 67 public Account getAccount();
michael@0 68 public void onSyncStarted();
michael@0 69 public void onSyncFinished();
michael@0 70 }
michael@0 71
michael@0 72 /**
michael@0 73 * Returns true if a FirefoxAccount exists, false otherwise.
michael@0 74 *
michael@0 75 * @param context Android context.
michael@0 76 * @return true if at least one Firefox account exists.
michael@0 77 */
michael@0 78 public static boolean firefoxAccountsExist(final Context context) {
michael@0 79 return getFirefoxAccounts(context).length > 0;
michael@0 80 }
michael@0 81
michael@0 82 /**
michael@0 83 * Return Firefox accounts.
michael@0 84 * <p>
michael@0 85 * If no accounts exist in the AccountManager, one may be created
michael@0 86 * via a pickled FirefoxAccount, if available, and that account
michael@0 87 * will be added to the AccountManager and returned.
michael@0 88 * <p>
michael@0 89 * Note that this can be called from any thread.
michael@0 90 *
michael@0 91 * @param context Android context.
michael@0 92 * @return Firefox account objects.
michael@0 93 */
michael@0 94 public static Account[] getFirefoxAccounts(final Context context) {
michael@0 95 final Account[] accounts =
michael@0 96 AccountManager.get(context).getAccountsByType(FxAccountConstants.ACCOUNT_TYPE);
michael@0 97 if (accounts.length > 0) {
michael@0 98 return accounts;
michael@0 99 }
michael@0 100
michael@0 101 final Account pickledAccount = getPickledAccount(context);
michael@0 102 return (pickledAccount != null) ? new Account[] {pickledAccount} : new Account[0];
michael@0 103 }
michael@0 104
michael@0 105 private static Account getPickledAccount(final Context context) {
michael@0 106 // To avoid a StrictMode violation for disk access, we call this from a background thread.
michael@0 107 // We do this every time, so the caller doesn't have to care.
michael@0 108 final CountDownLatch latch = new CountDownLatch(1);
michael@0 109 final Account[] accounts = new Account[1];
michael@0 110 ThreadPool.run(new Runnable() {
michael@0 111 @Override
michael@0 112 public void run() {
michael@0 113 try {
michael@0 114 final File file = context.getFileStreamPath(FxAccountConstants.ACCOUNT_PICKLE_FILENAME);
michael@0 115 if (!file.exists()) {
michael@0 116 accounts[0] = null;
michael@0 117 return;
michael@0 118 }
michael@0 119
michael@0 120 // There is a small race window here: if the user creates a new Firefox account
michael@0 121 // between our checks, this could erroneously report that no Firefox accounts
michael@0 122 // exist.
michael@0 123 final AndroidFxAccount fxAccount =
michael@0 124 AccountPickler.unpickle(context, FxAccountConstants.ACCOUNT_PICKLE_FILENAME);
michael@0 125 accounts[0] = fxAccount.getAndroidAccount();
michael@0 126 } finally {
michael@0 127 latch.countDown();
michael@0 128 }
michael@0 129 }
michael@0 130 });
michael@0 131
michael@0 132 try {
michael@0 133 latch.await(); // Wait for the background thread to return.
michael@0 134 } catch (InterruptedException e) {
michael@0 135 Logger.warn(LOG_TAG,
michael@0 136 "Foreground thread unexpectedly interrupted while getting pickled account", e);
michael@0 137 return null;
michael@0 138 }
michael@0 139
michael@0 140 return accounts[0];
michael@0 141 }
michael@0 142
michael@0 143 /**
michael@0 144 * @param context Android context.
michael@0 145 * @return the configured Firefox account if one exists, or null otherwise.
michael@0 146 */
michael@0 147 public static Account getFirefoxAccount(final Context context) {
michael@0 148 Account[] accounts = getFirefoxAccounts(context);
michael@0 149 if (accounts.length > 0) {
michael@0 150 return accounts[0];
michael@0 151 }
michael@0 152 return null;
michael@0 153 }
michael@0 154
michael@0 155 protected static void putHintsToSync(final Bundle extras, EnumSet<SyncHint> syncHints) {
michael@0 156 // stagesToSync and stagesToSkip are allowed to be null.
michael@0 157 if (syncHints == null) {
michael@0 158 throw new IllegalArgumentException("syncHints must not be null");
michael@0 159 }
michael@0 160
michael@0 161 final boolean scheduleNow = syncHints.contains(SyncHint.SCHEDULE_NOW);
michael@0 162 final boolean ignoreLocalRateLimit = syncHints.contains(SyncHint.IGNORE_LOCAL_RATE_LIMIT);
michael@0 163 final boolean ignoreRemoteServerBackoff = syncHints.contains(SyncHint.IGNORE_REMOTE_SERVER_BACKOFF);
michael@0 164
michael@0 165 extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, scheduleNow);
michael@0 166 // The default when manually syncing is to ignore the local rate limit and
michael@0 167 // any remote server backoff requests. Since we can't add flags to a manual
michael@0 168 // sync instigated by the user, we have to reverse the natural conditionals.
michael@0 169 // See also the FORCE EnumSet.
michael@0 170 extras.putBoolean(FxAccountSyncAdapter.SYNC_EXTRAS_RESPECT_LOCAL_RATE_LIMIT, !ignoreLocalRateLimit);
michael@0 171 extras.putBoolean(FxAccountSyncAdapter.SYNC_EXTRAS_RESPECT_REMOTE_SERVER_BACKOFF, !ignoreRemoteServerBackoff);
michael@0 172 }
michael@0 173
michael@0 174 public static EnumSet<SyncHint> getHintsToSyncFromBundle(final Bundle extras) {
michael@0 175 final EnumSet<SyncHint> syncHints = EnumSet.noneOf(SyncHint.class);
michael@0 176
michael@0 177 final boolean scheduleNow = extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false);
michael@0 178 final boolean ignoreLocalRateLimit = !extras.getBoolean(FxAccountSyncAdapter.SYNC_EXTRAS_RESPECT_LOCAL_RATE_LIMIT, false);
michael@0 179 final boolean ignoreRemoteServerBackoff = !extras.getBoolean(FxAccountSyncAdapter.SYNC_EXTRAS_RESPECT_REMOTE_SERVER_BACKOFF, false);
michael@0 180
michael@0 181 if (scheduleNow) {
michael@0 182 syncHints.add(SyncHint.SCHEDULE_NOW);
michael@0 183 }
michael@0 184 if (ignoreLocalRateLimit) {
michael@0 185 syncHints.add(SyncHint.IGNORE_LOCAL_RATE_LIMIT);
michael@0 186 }
michael@0 187 if (ignoreRemoteServerBackoff) {
michael@0 188 syncHints.add(SyncHint.IGNORE_REMOTE_SERVER_BACKOFF);
michael@0 189 }
michael@0 190
michael@0 191 return syncHints;
michael@0 192 }
michael@0 193
michael@0 194 public static void logSyncHints(EnumSet<SyncHint> syncHints) {
michael@0 195 final boolean scheduleNow = syncHints.contains(SyncHint.SCHEDULE_NOW);
michael@0 196 final boolean ignoreLocalRateLimit = syncHints.contains(SyncHint.IGNORE_LOCAL_RATE_LIMIT);
michael@0 197 final boolean ignoreRemoteServerBackoff = syncHints.contains(SyncHint.IGNORE_REMOTE_SERVER_BACKOFF);
michael@0 198
michael@0 199 Logger.info(LOG_TAG, "Sync hints" +
michael@0 200 "; scheduling now: " + scheduleNow +
michael@0 201 "; ignoring local rate limit: " + ignoreLocalRateLimit +
michael@0 202 "; ignoring remote server backoff: " + ignoreRemoteServerBackoff + ".");
michael@0 203 }
michael@0 204
michael@0 205 /**
michael@0 206 * Request a sync for the given Android Account.
michael@0 207 * <p>
michael@0 208 * Any hints are strictly optional: the actual requested sync is scheduled by
michael@0 209 * the Android sync scheduler, and the sync mechanism may ignore hints as it
michael@0 210 * sees fit.
michael@0 211 * <p>
michael@0 212 * It is safe to call this method from any thread.
michael@0 213 *
michael@0 214 * @param account to sync.
michael@0 215 * @param syncHints to pass to sync.
michael@0 216 * @param stagesToSync stage names to sync.
michael@0 217 * @param stagesToSkip stage names to skip.
michael@0 218 */
michael@0 219 public static void requestSync(final Account account, EnumSet<SyncHint> syncHints, String[] stagesToSync, String[] stagesToSkip) {
michael@0 220 if (account == null) {
michael@0 221 throw new IllegalArgumentException("account must not be null");
michael@0 222 }
michael@0 223 if (syncHints == null) {
michael@0 224 throw new IllegalArgumentException("syncHints must not be null");
michael@0 225 }
michael@0 226
michael@0 227 final Bundle extras = new Bundle();
michael@0 228 putHintsToSync(extras, syncHints);
michael@0 229 Utils.putStageNamesToSync(extras, stagesToSync, stagesToSkip);
michael@0 230
michael@0 231 Logger.info(LOG_TAG, "Requesting sync.");
michael@0 232 logSyncHints(syncHints);
michael@0 233
michael@0 234 // We get strict mode warnings on some devices, so make the request on a
michael@0 235 // background thread.
michael@0 236 ThreadPool.run(new Runnable() {
michael@0 237 @Override
michael@0 238 public void run() {
michael@0 239 for (String authority : AndroidFxAccount.getAndroidAuthorities()) {
michael@0 240 ContentResolver.requestSync(account, authority, extras);
michael@0 241 }
michael@0 242 }
michael@0 243 });
michael@0 244 }
michael@0 245
michael@0 246 /**
michael@0 247 * Start notifying <code>syncStatusListener</code> of sync status changes.
michael@0 248 * <p>
michael@0 249 * Only a weak reference to <code>syncStatusListener</code> is held.
michael@0 250 *
michael@0 251 * @param syncStatusListener to start notifying.
michael@0 252 */
michael@0 253 public static void addSyncStatusListener(SyncStatusListener syncStatusListener) {
michael@0 254 // startObserving null-checks its argument.
michael@0 255 FxAccountSyncStatusHelper.getInstance().startObserving(syncStatusListener);
michael@0 256 }
michael@0 257
michael@0 258 /**
michael@0 259 * Stop notifying <code>syncStatusListener</code> of sync status changes.
michael@0 260 *
michael@0 261 * @param syncStatusListener to stop notifying.
michael@0 262 */
michael@0 263 public static void removeSyncStatusListener(SyncStatusListener syncStatusListener) {
michael@0 264 // stopObserving null-checks its argument.
michael@0 265 FxAccountSyncStatusHelper.getInstance().stopObserving(syncStatusListener);
michael@0 266 }
michael@0 267 }

mercurial