michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: package org.mozilla.gecko.fxa; michael@0: michael@0: import java.io.File; michael@0: import java.util.EnumSet; michael@0: import java.util.concurrent.CountDownLatch; michael@0: michael@0: import org.mozilla.gecko.background.common.log.Logger; michael@0: import org.mozilla.gecko.fxa.authenticator.AccountPickler; michael@0: import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount; michael@0: import org.mozilla.gecko.fxa.sync.FxAccountSyncAdapter; michael@0: import org.mozilla.gecko.fxa.sync.FxAccountSyncStatusHelper; michael@0: import org.mozilla.gecko.sync.ThreadPool; michael@0: import org.mozilla.gecko.sync.Utils; michael@0: michael@0: import android.accounts.Account; michael@0: import android.accounts.AccountManager; michael@0: import android.content.ContentResolver; michael@0: import android.content.Context; michael@0: import android.os.Bundle; michael@0: michael@0: /** michael@0: * Simple public accessors for Firefox account objects. michael@0: */ michael@0: public class FirefoxAccounts { michael@0: private static final String LOG_TAG = FirefoxAccounts.class.getSimpleName(); michael@0: michael@0: public enum SyncHint { michael@0: /** michael@0: * Hint that a requested sync is preferred immediately. michael@0: *
michael@0: * On many devices, not including SCHEDULE_NOW
means a delay of
michael@0: * at least 30 seconds.
michael@0: */
michael@0: SCHEDULE_NOW,
michael@0:
michael@0: /**
michael@0: * Hint that a requested sync may ignore local rate limiting.
michael@0: *
michael@0: * This is just a hint; the actual requested sync may not obey the hint. michael@0: */ michael@0: IGNORE_LOCAL_RATE_LIMIT, michael@0: michael@0: /** michael@0: * Hint that a requested sync may ignore remote server backoffs. michael@0: *
michael@0: * This is just a hint; the actual requested sync may not obey the hint.
michael@0: */
michael@0: IGNORE_REMOTE_SERVER_BACKOFF,
michael@0: }
michael@0:
michael@0: public static final EnumSet
michael@0: * If no accounts exist in the AccountManager, one may be created
michael@0: * via a pickled FirefoxAccount, if available, and that account
michael@0: * will be added to the AccountManager and returned.
michael@0: *
michael@0: * Note that this can be called from any thread.
michael@0: *
michael@0: * @param context Android context.
michael@0: * @return Firefox account objects.
michael@0: */
michael@0: public static Account[] getFirefoxAccounts(final Context context) {
michael@0: final Account[] accounts =
michael@0: AccountManager.get(context).getAccountsByType(FxAccountConstants.ACCOUNT_TYPE);
michael@0: if (accounts.length > 0) {
michael@0: return accounts;
michael@0: }
michael@0:
michael@0: final Account pickledAccount = getPickledAccount(context);
michael@0: return (pickledAccount != null) ? new Account[] {pickledAccount} : new Account[0];
michael@0: }
michael@0:
michael@0: private static Account getPickledAccount(final Context context) {
michael@0: // To avoid a StrictMode violation for disk access, we call this from a background thread.
michael@0: // We do this every time, so the caller doesn't have to care.
michael@0: final CountDownLatch latch = new CountDownLatch(1);
michael@0: final Account[] accounts = new Account[1];
michael@0: ThreadPool.run(new Runnable() {
michael@0: @Override
michael@0: public void run() {
michael@0: try {
michael@0: final File file = context.getFileStreamPath(FxAccountConstants.ACCOUNT_PICKLE_FILENAME);
michael@0: if (!file.exists()) {
michael@0: accounts[0] = null;
michael@0: return;
michael@0: }
michael@0:
michael@0: // There is a small race window here: if the user creates a new Firefox account
michael@0: // between our checks, this could erroneously report that no Firefox accounts
michael@0: // exist.
michael@0: final AndroidFxAccount fxAccount =
michael@0: AccountPickler.unpickle(context, FxAccountConstants.ACCOUNT_PICKLE_FILENAME);
michael@0: accounts[0] = fxAccount.getAndroidAccount();
michael@0: } finally {
michael@0: latch.countDown();
michael@0: }
michael@0: }
michael@0: });
michael@0:
michael@0: try {
michael@0: latch.await(); // Wait for the background thread to return.
michael@0: } catch (InterruptedException e) {
michael@0: Logger.warn(LOG_TAG,
michael@0: "Foreground thread unexpectedly interrupted while getting pickled account", e);
michael@0: return null;
michael@0: }
michael@0:
michael@0: return accounts[0];
michael@0: }
michael@0:
michael@0: /**
michael@0: * @param context Android context.
michael@0: * @return the configured Firefox account if one exists, or null otherwise.
michael@0: */
michael@0: public static Account getFirefoxAccount(final Context context) {
michael@0: Account[] accounts = getFirefoxAccounts(context);
michael@0: if (accounts.length > 0) {
michael@0: return accounts[0];
michael@0: }
michael@0: return null;
michael@0: }
michael@0:
michael@0: protected static void putHintsToSync(final Bundle extras, EnumSet
michael@0: * Any hints are strictly optional: the actual requested sync is scheduled by
michael@0: * the Android sync scheduler, and the sync mechanism may ignore hints as it
michael@0: * sees fit.
michael@0: *
michael@0: * It is safe to call this method from any thread.
michael@0: *
michael@0: * @param account to sync.
michael@0: * @param syncHints to pass to sync.
michael@0: * @param stagesToSync stage names to sync.
michael@0: * @param stagesToSkip stage names to skip.
michael@0: */
michael@0: public static void requestSync(final Account account, EnumSet
michael@0: * Only a weak reference to syncStatusListener
of sync status changes.
michael@0: * syncStatusListener
is held.
michael@0: *
michael@0: * @param syncStatusListener to start notifying.
michael@0: */
michael@0: public static void addSyncStatusListener(SyncStatusListener syncStatusListener) {
michael@0: // startObserving null-checks its argument.
michael@0: FxAccountSyncStatusHelper.getInstance().startObserving(syncStatusListener);
michael@0: }
michael@0:
michael@0: /**
michael@0: * Stop notifying syncStatusListener
of sync status changes.
michael@0: *
michael@0: * @param syncStatusListener to stop notifying.
michael@0: */
michael@0: public static void removeSyncStatusListener(SyncStatusListener syncStatusListener) {
michael@0: // stopObserving null-checks its argument.
michael@0: FxAccountSyncStatusHelper.getInstance().stopObserving(syncStatusListener);
michael@0: }
michael@0: }