Backed out 3 changesets (bug 1147473, bug 1148504, bug 1142596) for bustage.
authorRyan VanderMeulen <ryanvm@gmail.com>
Fri, 27 Mar 2015 15:41:53 -0400
changeset 266504 cb309087f978fc39dacf1e5ff565fd4c84d7dd95
parent 266503 c4639723e721ca127fac6c5c0fd9828b6a9debf0
child 266505 02623719b09419865790bcf5dad10f253509372f
push id830
push userraliiev@mozilla.com
push dateFri, 19 Jun 2015 19:24:37 +0000
treeherdermozilla-release@932614382a68 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1147473, 1148504, 1142596
milestone39.0a1
backs outc4639723e721ca127fac6c5c0fd9828b6a9debf0
6c3fd5d5aa407616b09fea84a0136fcd8692501b
24bf86f4b440353e074c3e5c8323b2b218aa4b0e
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out 3 changesets (bug 1147473, bug 1148504, bug 1142596) for bustage. Backed out changeset c4639723e721 (bug 1148504) Backed out changeset 6c3fd5d5aa40 (bug 1142596) Backed out changeset 24bf86f4b440 (bug 1147473) DONTBUILD CLOSED TREE
mobile/android/base/android-services.mozbuild
mobile/android/base/background/fxa/FxAccountUtils.java
mobile/android/base/background/fxa/oauth/FxAccountAbstractClientException.java
mobile/android/base/fxa/FxAccountConstants.java
mobile/android/base/fxa/activities/FxAccountStatusFragment.java
mobile/android/base/fxa/authenticator/AndroidFxAccount.java
mobile/android/base/fxa/authenticator/FxADefaultLoginStateMachineDelegate.java
mobile/android/base/fxa/authenticator/FxAccountAuthenticator.java
mobile/android/base/fxa/login/Cohabiting.java
mobile/android/base/fxa/login/Married.java
mobile/android/base/fxa/receivers/FxAccountDeletedService.java
mobile/android/base/fxa/sync/FxAccountNotificationManager.java
mobile/android/base/fxa/sync/FxAccountSyncAdapter.java
mobile/android/base/fxa/sync/FxAccountSyncDelegate.java
mobile/android/base/reading/ReadingListConstants.java
mobile/android/base/reading/ReadingListInvalidAuthenticationException.java
mobile/android/base/reading/ReadingListSyncAdapter.java
mobile/android/base/reading/ReadingListSynchronizer.java
mobile/android/base/resources/xml/fxaccount_status_prefscreen.xml
mobile/android/base/sync/net/MozResponse.java
--- a/mobile/android/base/android-services.mozbuild
+++ b/mobile/android/base/android-services.mozbuild
@@ -863,17 +863,16 @@ sync_java_files = [
     'fxa/activities/FxAccountUpdateCredentialsActivity.java',
     'fxa/activities/FxAccountVerifiedAccountActivity.java',
     'fxa/authenticator/AccountPickler.java',
     'fxa/authenticator/AndroidFxAccount.java',
     'fxa/authenticator/FxAccountAuthenticator.java',
     'fxa/authenticator/FxAccountAuthenticatorService.java',
     'fxa/authenticator/FxAccountLoginDelegate.java',
     'fxa/authenticator/FxAccountLoginException.java',
-    'fxa/authenticator/FxADefaultLoginStateMachineDelegate.java',
     'fxa/FirefoxAccounts.java',
     'fxa/FxAccountConstants.java',
     'fxa/login/BaseRequestDelegate.java',
     'fxa/login/Cohabiting.java',
     'fxa/login/Doghouse.java',
     'fxa/login/Engaged.java',
     'fxa/login/FxAccountLoginStateMachine.java',
     'fxa/login/FxAccountLoginTransition.java',
@@ -1167,17 +1166,16 @@ reading_list_service_java_files = [
     'reading/FetchSpec.java',
     'reading/LocalReadingListStorage.java',
     'reading/ReadingListChangeAccumulator.java',
     'reading/ReadingListClient.java',
     'reading/ReadingListClientContentValuesFactory.java',
     'reading/ReadingListClientRecordFactory.java',
     'reading/ReadingListConstants.java',
     'reading/ReadingListDeleteDelegate.java',
-    'reading/ReadingListInvalidAuthenticationException.java',
     'reading/ReadingListRecord.java',
     'reading/ReadingListRecordDelegate.java',
     'reading/ReadingListRecordResponse.java',
     'reading/ReadingListRecordUploadDelegate.java',
     'reading/ReadingListResponse.java',
     'reading/ReadingListStorage.java',
     'reading/ReadingListStorageResponse.java',
     'reading/ReadingListSyncAdapter.java',
--- a/mobile/android/base/background/fxa/FxAccountUtils.java
+++ b/mobile/android/base/background/fxa/FxAccountUtils.java
@@ -9,24 +9,26 @@ import java.math.BigInteger;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.security.GeneralSecurityException;
 import java.security.InvalidKeyException;
 import java.security.NoSuchAlgorithmException;
 
 import org.mozilla.gecko.AppConstants;
 import org.mozilla.gecko.R;
+import org.mozilla.gecko.R.string;
 import org.mozilla.gecko.background.common.log.Logger;
 import org.mozilla.gecko.background.nativecode.NativeCrypto;
 import org.mozilla.gecko.sync.Utils;
 import org.mozilla.gecko.sync.crypto.HKDF;
 import org.mozilla.gecko.sync.crypto.KeyBundle;
 import org.mozilla.gecko.sync.crypto.PBKDF2;
 
 import android.content.Context;
+import android.os.Build;
 
 public class FxAccountUtils {
   private static final String LOG_TAG = FxAccountUtils.class.getSimpleName();
 
   public static final int SALT_LENGTH_BYTES = 32;
   public static final int SALT_LENGTH_HEX = 2 * SALT_LENGTH_BYTES;
 
   public static final int HASH_LENGTH_BYTES = 16;
--- a/mobile/android/base/background/fxa/oauth/FxAccountAbstractClientException.java
+++ b/mobile/android/base/background/fxa/oauth/FxAccountAbstractClientException.java
@@ -4,17 +4,16 @@
 
 package org.mozilla.gecko.background.fxa.oauth;
 
 import org.mozilla.gecko.sync.ExtendedJSONObject;
 import org.mozilla.gecko.sync.HTTPFailureException;
 import org.mozilla.gecko.sync.net.SyncStorageResponse;
 
 import ch.boye.httpclientandroidlib.HttpResponse;
-import ch.boye.httpclientandroidlib.HttpStatus;
 
 /**
  * From <a href="https://github.com/mozilla/fxa-auth-server/blob/master/docs/api.md">https://github.com/mozilla/fxa-auth-server/blob/master/docs/api.md</a>.
  */
 public class FxAccountAbstractClientException extends Exception {
   private static final long serialVersionUID = 1953459541558266597L;
 
   public FxAccountAbstractClientException(String detailMessage) {
@@ -47,20 +46,16 @@ public class FxAccountAbstractClientExce
       this.message = message;
       this.body = body;
     }
 
     @Override
     public String toString() {
       return "<FxAccountAbstractClientRemoteException " + this.httpStatusCode + " [" + this.apiErrorNumber + "]: " + this.message + ">";
     }
-
-    public boolean isInvalidAuthentication() {
-      return this.httpStatusCode == HttpStatus.SC_UNAUTHORIZED;
-    }
   }
 
   public static class FxAccountAbstractClientMalformedResponseException extends FxAccountAbstractClientRemoteException {
     private static final long serialVersionUID = 1209313149952001098L;
 
     public FxAccountAbstractClientMalformedResponseException(HttpResponse response) {
       super(response, 0, FxAccountOAuthRemoteError.UNKNOWN_ERROR, "Response malformed", "Response malformed", new ExtendedJSONObject());
     }
--- a/mobile/android/base/fxa/FxAccountConstants.java
+++ b/mobile/android/base/fxa/FxAccountConstants.java
@@ -5,26 +5,21 @@
 package org.mozilla.gecko.fxa;
 
 import org.mozilla.gecko.AppConstants;
 
 public class FxAccountConstants {
   public static final String GLOBAL_LOG_TAG = "FxAccounts";
   public static final String ACCOUNT_TYPE = AppConstants.MOZ_ANDROID_SHARED_FXACCOUNT_TYPE;
 
-  // Must be a client ID allocated with "canGrant" privileges!
-  public static final String OAUTH_CLIENT_ID_FENNEC = "3332a18d142636cb";
-
   public static final String DEFAULT_AUTH_SERVER_ENDPOINT = "https://api.accounts.firefox.com/v1";
   public static final String DEFAULT_TOKEN_SERVER_ENDPOINT = "https://token.services.mozilla.com/1.0/sync/1.5";
-  public static final String DEFAULT_OAUTH_SERVER_ENDPOINT = "https://oauth.accounts.firefox.com/v1";
 
-  public static final String STAGE_AUTH_SERVER_ENDPOINT = "https://stable.dev.lcip.org/auth/v1";
-  public static final String STAGE_TOKEN_SERVER_ENDPOINT = "https://stable.dev.lcip.org/syncserver/token/1.0/sync/1.5";
-  public static final String STAGE_OAUTH_SERVER_ENDPOINT = "https://oauth-stable.dev.lcip.org/v1";
+  public static final String STAGE_AUTH_SERVER_ENDPOINT = "https://api-accounts.stage.mozaws.net/v1";
+  public static final String STAGE_TOKEN_SERVER_ENDPOINT = "https://token.stage.mozaws.net/1.0/sync/1.5";
 
   // You must be at least 13 years old, on the day of creation, to create a Firefox Account.
   public static final int MINIMUM_AGE_TO_CREATE_AN_ACCOUNT = 13;
 
   // You must wait 15 minutes after failing an age check before trying to create a different account.
   public static final long MINIMUM_TIME_TO_WAIT_AFTER_AGE_CHECK_FAILED_IN_MILLISECONDS = 15 * 60 * 1000;
 
   public static final String USER_AGENT = "Firefox-Android-FxAccounts/" + AppConstants.MOZ_APP_VERSION + " (" + AppConstants.MOZ_APP_DISPLAYNAME + ")";
--- a/mobile/android/base/fxa/activities/FxAccountStatusFragment.java
+++ b/mobile/android/base/fxa/activities/FxAccountStatusFragment.java
@@ -1,81 +1,77 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.fxa.activities;
 
-import java.util.Calendar;
-import java.util.Date;
-import java.util.GregorianCalendar;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-
 import org.mozilla.gecko.AppConstants;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.background.common.log.Logger;
 import org.mozilla.gecko.background.fxa.FxAccountUtils;
 import org.mozilla.gecko.background.preferences.PreferenceFragment;
 import org.mozilla.gecko.db.BrowserContract;
 import org.mozilla.gecko.fxa.FirefoxAccounts;
 import org.mozilla.gecko.fxa.FxAccountConstants;
 import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
 import org.mozilla.gecko.fxa.login.Married;
 import org.mozilla.gecko.fxa.login.State;
 import org.mozilla.gecko.fxa.sync.FxAccountSyncStatusHelper;
 import org.mozilla.gecko.fxa.tasks.FxAccountCodeResender;
-import org.mozilla.gecko.reading.ReadingListConstants;
 import org.mozilla.gecko.sync.ExtendedJSONObject;
 import org.mozilla.gecko.sync.SharedPreferencesClientsDataDelegate;
 import org.mozilla.gecko.sync.SyncConfiguration;
 import org.mozilla.gecko.util.HardwareUtils;
 
 import android.accounts.Account;
-import android.accounts.AccountManager;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.os.Bundle;
 import android.os.Handler;
 import android.preference.CheckBoxPreference;
 import android.preference.EditTextPreference;
 import android.preference.Preference;
 import android.preference.Preference.OnPreferenceChangeListener;
 import android.preference.Preference.OnPreferenceClickListener;
 import android.preference.PreferenceCategory;
 import android.preference.PreferenceScreen;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
-import android.widget.Toast;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
 
 
 /**
  * A fragment that displays the status of an AndroidFxAccount.
  * <p>
  * The owning activity is responsible for providing an AndroidFxAccount at
  * appropriate times.
  */
 public class FxAccountStatusFragment
     extends PreferenceFragment
     implements OnPreferenceClickListener, OnPreferenceChangeListener {
   private static final String LOG_TAG = FxAccountStatusFragment.class.getSimpleName();
 
-  /**
-   * If a device claims to have synced before this date, we will assume it has never synced.
-   */
-  private static final Date EARLIEST_VALID_SYNCED_DATE;
-  static {
-    final Calendar c = GregorianCalendar.getInstance();
-    c.set(2000, Calendar.JANUARY, 1, 0, 0, 0);
-    EARLIEST_VALID_SYNCED_DATE = c.getTime();
-  }
-
+    /**
+     * If a device claims to have synced before this date, we will assume it has never synced.
+     */
+    private static final Date EARLIEST_VALID_SYNCED_DATE;
+    static {
+        final Calendar c = GregorianCalendar.getInstance();
+        c.set(2000, Calendar.JANUARY, 1, 0, 0, 0);
+        EARLIEST_VALID_SYNCED_DATE = c.getTime();
+    }
   // When a checkbox is toggled, wait 5 seconds (for other checkbox actions)
   // before trying to sync. Should we kill off the fragment before the sync
   // request happens, that's okay: the runnable will run if the UI thread is
   // still around to service it, and since we're not updating any UI, we'll just
   // schedule the sync as usual. See also comment below about garbage
   // collection.
   private static final long DELAY_IN_MILLISECONDS_BEFORE_REQUESTING_SYNC = 5 * 1000;
   private static final long LAST_SYNCED_TIME_UPDATE_INTERVAL_IN_MILLISECONDS = 60 * 1000;
@@ -83,25 +79,16 @@ public class FxAccountStatusFragment
   // By default, the auth/account server preference is only shown when the
   // account is configured to use a custom server. In debug mode, this is set.
   private static boolean ALWAYS_SHOW_AUTH_SERVER = false;
 
   // By default, the Sync server preference is only shown when the account is
   // configured to use a custom Sync server. In debug mode, this is set.
   private static boolean ALWAYS_SHOW_SYNC_SERVER = false;
 
-  // If the user clicks the email field this many times, the debug / personal
-  // information logging setting will toggle. The setting is not permanent: it
-  // lasts until this process is killed. We don't want to dump PII to the log
-  // for a long time!
-  private final int NUMBER_OF_CLICKS_TO_TOGGLE_DEBUG =
-      // !defined(MOZILLA_OFFICIAL) || defined(NIGHTLY_BUILD) || defined(MOZ_DEBUG)
-      (!AppConstants.MOZILLA_OFFICIAL || AppConstants.NIGHTLY_BUILD || AppConstants.DEBUG_BUILD) ? 5 : -1 /* infinite */;
-  private int debugClickCount = 0;
-
   protected PreferenceCategory accountCategory;
   protected Preference emailPreference;
   protected Preference authServerPreference;
 
   protected Preference needsPasswordPreference;
   protected Preference needsUpgradePreference;
   protected Preference needsVerificationPreference;
   protected Preference needsMasterSyncAutomaticallyEnabledPreference;
@@ -185,18 +172,16 @@ public class FxAccountStatusFragment
     if (!FxAccountUtils.LOG_PERSONAL_INFORMATION) {
       removeDebugButtons();
     } else {
       connectDebugButtons();
       ALWAYS_SHOW_AUTH_SERVER = true;
       ALWAYS_SHOW_SYNC_SERVER = true;
     }
 
-    emailPreference.setOnPreferenceClickListener(this);
-
     needsPasswordPreference.setOnPreferenceClickListener(this);
     needsVerificationPreference.setOnPreferenceClickListener(this);
     needsFinishMigratingPreference.setOnPreferenceClickListener(this);
 
     bookmarksPreference.setOnPreferenceClickListener(this);
     historyPreference.setOnPreferenceClickListener(this);
     tabsPreference.setOnPreferenceClickListener(this);
     passwordsPreference.setOnPreferenceClickListener(this);
@@ -224,27 +209,16 @@ public class FxAccountStatusFragment
    */
   @Override
   public void onResume() {
     super.onResume();
   }
 
   @Override
   public boolean onPreferenceClick(Preference preference) {
-    if (preference == emailPreference) {
-      debugClickCount += 1;
-      if (NUMBER_OF_CLICKS_TO_TOGGLE_DEBUG > 0 && debugClickCount >= NUMBER_OF_CLICKS_TO_TOGGLE_DEBUG) {
-        debugClickCount = 0;
-        FxAccountUtils.LOG_PERSONAL_INFORMATION = !FxAccountUtils.LOG_PERSONAL_INFORMATION;
-        Toast.makeText(getActivity(), "Toggled logging Firefox Account personal information!", Toast.LENGTH_LONG).show();
-        hardRefresh(); // Display or hide debug options.
-      }
-      return true;
-    }
-
     if (preference == needsPasswordPreference) {
       Intent intent = new Intent(getActivity(), FxAccountUpdateCredentialsActivity.class);
       final Bundle extras = getExtrasForAccount();
       if (extras != null) {
         intent.putExtras(extras);
       }
       // Per http://stackoverflow.com/a/8992365, this triggers a known bug with
       // the soft keyboard not being shown for the started activity. Why, Android, why?
@@ -785,71 +759,42 @@ public class FxAccountStatusFragment
         Logger.info(LOG_TAG, "Refreshing.");
         refresh();
       } else if ("debug_dump".equals(key)) {
         fxAccount.dump();
       } else if ("debug_force_sync".equals(key)) {
         Logger.info(LOG_TAG, "Force syncing.");
         fxAccount.requestSync(FirefoxAccounts.FORCE);
         // No sense refreshing, since the sync will complete in the future.
-      } else if ("debug_forget_reading_list_oauth_token".equals(key)) {
-        final Account account = fxAccount.getAndroidAccount();
-        final AccountManager accountManager = AccountManager.get(getActivity());
-        final String authToken = accountManager.peekAuthToken(account, ReadingListConstants.AUTH_TOKEN_TYPE);
-        if (authToken != null) {
-          Logger.info(LOG_TAG, "Forgetting reading list oauth token: " + authToken);
-          accountManager.invalidateAuthToken(account.type, authToken);
-        } else {
-          Logger.warn(LOG_TAG, "No reading list oauth token to forget!");
-        }
       } else if ("debug_forget_certificate".equals(key)) {
         State state = fxAccount.getState();
         try {
           Married married = (Married) state;
           Logger.info(LOG_TAG, "Moving to Cohabiting state: Forgetting certificate.");
           fxAccount.setState(married.makeCohabitingState());
           refresh();
         } catch (ClassCastException e) {
           Logger.info(LOG_TAG, "Not in Married state; can't forget certificate.");
           // Ignore.
         }
-      } else if ("debug_invalidate_certificate".equals(key)) {
-        State state = fxAccount.getState();
-        try {
-          Married married = (Married) state;
-          Logger.info(LOG_TAG, "Invalidating certificate.");
-          fxAccount.setState(married.makeCohabitingState().withCertificate("INVALID CERTIFICATE"));
-          refresh();
-        } catch (ClassCastException e) {
-          Logger.info(LOG_TAG, "Not in Married state; can't invalidate certificate.");
-          // Ignore.
-        }
       } else if ("debug_require_password".equals(key)) {
         Logger.info(LOG_TAG, "Moving to Separated state: Forgetting password.");
         State state = fxAccount.getState();
         fxAccount.setState(state.makeSeparatedState());
         refresh();
       } else if ("debug_require_upgrade".equals(key)) {
         Logger.info(LOG_TAG, "Moving to Doghouse state: Requiring upgrade.");
         State state = fxAccount.getState();
         fxAccount.setState(state.makeDoghouseState());
         refresh();
       } else if ("debug_migrated_from_sync11".equals(key)) {
         Logger.info(LOG_TAG, "Moving to MigratedFromSync11 state: Requiring password.");
         State state = fxAccount.getState();
         fxAccount.setState(state.makeMigratedFromSync11State(null));
         refresh();
-      } else if ("debug_make_account_stage".equals(key)) {
-        Logger.info(LOG_TAG, "Moving Account endpoints, in place, to stage.  Deleting Sync and RL prefs and requiring password.");
-        fxAccount.unsafeTransitionToStageEndpoints();
-        refresh();
-      } else if ("debug_make_account_default".equals(key)) {
-        Logger.info(LOG_TAG, "Moving Account endpoints, in place, to default (production).  Deleting Sync and RL prefs and requiring password.");
-        fxAccount.unsafeTransitionToDefaultEndpoints();
-        refresh();
       } else {
         return false;
       }
       return true;
     }
   }
 
   /**
@@ -857,22 +802,30 @@ public class FxAccountStatusFragment
    * listener to each of them.
    */
   protected void connectDebugButtons() {
     // Separate listener to really separate debug logic from main code paths.
     final OnPreferenceClickListener listener = new DebugPreferenceClickListener();
 
     // We don't want to use Android resource strings for debug UI, so we just
     // use the keys throughout.
-    final PreferenceCategory debugCategory = (PreferenceCategory) ensureFindPreference("debug_category");
+    final Preference debugCategory = ensureFindPreference("debug_category");
     debugCategory.setTitle(debugCategory.getKey());
 
-    for (int i = 0; i < debugCategory.getPreferenceCount(); i++) {
-      final Preference button = debugCategory.getPreference(i);
-      button.setTitle(button.getKey()); // Not very friendly, but this is for debugging only!
+    String[] debugKeys = new String[] {
+        "debug_refresh",
+        "debug_dump",
+        "debug_force_sync",
+        "debug_forget_certificate",
+        "debug_require_password",
+        "debug_require_upgrade",
+        "debug_migrated_from_sync11" };
+    for (String debugKey : debugKeys) {
+      final Preference button = ensureFindPreference(debugKey);
+      button.setTitle(debugKey); // Not very friendly, but this is for debugging only!
       button.setOnPreferenceClickListener(listener);
     }
   }
 
   @Override
   public boolean onPreferenceChange(Preference preference, Object newValue) {
     if (preference == deviceNamePreference) {
       String newClientName = (String) newValue;
--- a/mobile/android/base/fxa/authenticator/AndroidFxAccount.java
+++ b/mobile/android/base/fxa/authenticator/AndroidFxAccount.java
@@ -8,17 +8,16 @@ import java.io.UnsupportedEncodingExcept
 import java.net.URISyntaxException;
 import java.security.GeneralSecurityException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.Semaphore;
 
 import org.mozilla.gecko.AppConstants;
 import org.mozilla.gecko.background.common.GlobalConstants;
 import org.mozilla.gecko.background.common.log.Logger;
 import org.mozilla.gecko.background.fxa.FxAccountUtils;
 import org.mozilla.gecko.db.BrowserContract;
 import org.mozilla.gecko.fxa.FirefoxAccounts;
 import org.mozilla.gecko.fxa.FxAccountConstants;
@@ -31,17 +30,16 @@ import org.mozilla.gecko.sync.setup.Cons
 
 import android.accounts.Account;
 import android.accounts.AccountManager;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.os.Bundle;
-import android.util.Log;
 
 /**
  * A Firefox Account that stores its details and state as user data attached to
  * an Android Account instance.
  * <p>
  * Account user data is accessible only to the Android App(s) that own the
  * Account type. Account user data is not removed when the App's private data is
  * cleared.
@@ -617,92 +615,9 @@ public class AndroidFxAccount {
     final long neverSynced = -1L;
     try {
       return getSyncPrefs().getLong(PREF_KEY_LAST_SYNCED_TIMESTAMP, neverSynced);
     } catch (Exception e) {
       Logger.warn(LOG_TAG, "Got exception getting last synced time; ignoring.", e);
       return neverSynced;
     }
   }
-
-  // Debug only!  This is dangerous!
-  public void unsafeTransitionToDefaultEndpoints() {
-    unsafeTransitionToStageEndpoints(
-        FxAccountConstants.DEFAULT_AUTH_SERVER_ENDPOINT,
-        FxAccountConstants.DEFAULT_TOKEN_SERVER_ENDPOINT);
-    }
-
-  // Debug only!  This is dangerous!
-  public void unsafeTransitionToStageEndpoints() {
-    unsafeTransitionToStageEndpoints(
-        FxAccountConstants.STAGE_AUTH_SERVER_ENDPOINT,
-        FxAccountConstants.STAGE_TOKEN_SERVER_ENDPOINT);
-  }
-
-  protected void unsafeTransitionToStageEndpoints(String authServerEndpoint, String tokenServerEndpoint) {
-    try {
-      getReadingListPrefs().edit().clear().commit();
-    } catch (UnsupportedEncodingException | GeneralSecurityException e) {
-      // Ignore.
-    }
-    try {
-      getSyncPrefs().edit().clear().commit();
-    } catch (UnsupportedEncodingException | GeneralSecurityException e) {
-      // Ignore.
-    }
-    State state = getState();
-    setState(state.makeSeparatedState());
-    accountManager.setUserData(account, ACCOUNT_KEY_IDP_SERVER, authServerEndpoint);
-    accountManager.setUserData(account, ACCOUNT_KEY_TOKEN_SERVER, tokenServerEndpoint);
-  }
-
-  /**
-   * Take the lock to own updating any Firefox Account's internal state.
-   *
-   * We use a <code>Semaphore</code> rather than a <code>ReentrantLock</code>
-   * because the callback that needs to release the lock may not be invoked on
-   * the thread that initially acquired the lock. Be aware!
-   */
-  protected static final Semaphore sLock = new Semaphore(1, true /* fair */);
-
-  // Which consumer took the lock?
-  // Synchronized by this.
-  protected String lockTag = null;
-
-  // Are we locked?  (It's not easy to determine who took the lock dynamically,
-  // so we maintain this flag internally.)
-  // Synchronized by this.
-  protected boolean locked = false;
-
-  // Block until we can take the shared state lock.
-  public synchronized void acquireSharedAccountStateLock(final String tag) throws InterruptedException {
-    final long id = Thread.currentThread().getId();
-    this.lockTag = tag;
-    Log.d(Logger.DEFAULT_LOG_TAG, "Thread with tag and thread id acquiring lock: " + lockTag + ", " + id + " ...");
-    sLock.acquire();
-    locked = true;
-    Log.d(Logger.DEFAULT_LOG_TAG, "Thread with tag and thread id acquiring lock: " + lockTag + ", " + id + " ... ACQUIRED");
-  }
-
-  // If we hold the shared state lock, release it.  Otherwise, ignore the request.
-  public synchronized void releaseSharedAccountStateLock() {
-    final long id = Thread.currentThread().getId();
-    Log.d(Logger.DEFAULT_LOG_TAG, "Thread with tag and thread id releasing lock: " + lockTag + ", " + id + " ...");
-    if (locked) {
-      sLock.release();
-      locked = false;
-      Log.d(Logger.DEFAULT_LOG_TAG, "Thread with tag and thread id releasing lock: " + lockTag + ", " + id + " ... RELEASED");
-    } else {
-      Log.d(Logger.DEFAULT_LOG_TAG, "Thread with tag and thread id releasing lock: " + lockTag + ", " + id + " ... NOT LOCKED");
-    }
-  }
-
-  @Override
-  protected synchronized void finalize() {
-    if (locked) {
-      // Should never happen, but...
-      sLock.release();
-      locked = false;
-      final long id = Thread.currentThread().getId();
-      Log.e(Logger.DEFAULT_LOG_TAG, "Thread with tag and thread id releasing lock: " + lockTag + ", " + id + " ... RELEASED DURING FINALIZE");
-    }
-  }
 }
deleted file mode 100644
--- a/mobile/android/base/fxa/authenticator/FxADefaultLoginStateMachineDelegate.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.gecko.fxa.authenticator;
-
-import java.security.NoSuchAlgorithmException;
-import java.util.concurrent.Executor;
-import java.util.concurrent.Executors;
-
-import org.mozilla.gecko.background.common.log.Logger;
-import org.mozilla.gecko.background.fxa.FxAccountClient;
-import org.mozilla.gecko.background.fxa.FxAccountClient20;
-import org.mozilla.gecko.browserid.BrowserIDKeyPair;
-import org.mozilla.gecko.fxa.login.FxAccountLoginStateMachine.LoginStateMachineDelegate;
-import org.mozilla.gecko.fxa.login.FxAccountLoginTransition.Transition;
-import org.mozilla.gecko.fxa.login.Married;
-import org.mozilla.gecko.fxa.login.State;
-import org.mozilla.gecko.fxa.login.State.StateLabel;
-import org.mozilla.gecko.fxa.login.StateFactory;
-import org.mozilla.gecko.fxa.sync.FxAccountNotificationManager;
-import org.mozilla.gecko.fxa.sync.FxAccountSyncAdapter;
-
-import android.content.Context;
-
-public abstract class FxADefaultLoginStateMachineDelegate implements LoginStateMachineDelegate {
-  protected final static String LOG_TAG = LoginStateMachineDelegate.class.getSimpleName();
-
-  protected final Context context;
-  protected final AndroidFxAccount fxAccount;
-  protected final Executor executor;
-  protected final FxAccountClient client;
-
-  public FxADefaultLoginStateMachineDelegate(Context context, AndroidFxAccount fxAccount) {
-    this.context = context;
-    this.fxAccount = fxAccount;
-    this.executor = Executors.newSingleThreadExecutor();
-    this.client = new FxAccountClient20(fxAccount.getAccountServerURI(), executor);
-  }
-
-  abstract public void handleNotMarried(State notMarried);
-  abstract public void handleMarried(Married married);
-
-  @Override
-  public FxAccountClient getClient() {
-    return client;
-  }
-
-  @Override
-  public long getCertificateDurationInMilliseconds() {
-    return 12 * 60 * 60 * 1000;
-  }
-
-  @Override
-  public long getAssertionDurationInMilliseconds() {
-    return 15 * 60 * 1000;
-  }
-
-  @Override
-  public BrowserIDKeyPair generateKeyPair() throws NoSuchAlgorithmException {
-    return StateFactory.generateKeyPair();
-  }
-
-  @Override
-  public void handleTransition(Transition transition, State state) {
-    Logger.info(LOG_TAG, "handleTransition: " + transition + " to " + state.getStateLabel());
-  }
-
-  @Override
-  public void handleFinal(State state) {
-    Logger.info(LOG_TAG, "handleFinal: in " + state.getStateLabel());
-    fxAccount.setState(state);
-    // Update any notifications displayed.
-    final FxAccountNotificationManager notificationManager = new FxAccountNotificationManager(FxAccountSyncAdapter.NOTIFICATION_ID);
-    notificationManager.update(context, fxAccount);
-
-    if (state.getStateLabel() != StateLabel.Married) {
-      handleNotMarried(state);
-      return;
-    } else {
-      handleMarried((Married) state);
-    }
-  }
-}
--- a/mobile/android/base/fxa/authenticator/FxAccountAuthenticator.java
+++ b/mobile/android/base/fxa/authenticator/FxAccountAuthenticator.java
@@ -1,52 +1,29 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.fxa.authenticator;
 
-import java.security.NoSuchAlgorithmException;
-import java.util.concurrent.Executor;
-import java.util.concurrent.Executors;
-
 import org.mozilla.gecko.background.common.log.Logger;
-import org.mozilla.gecko.background.fxa.FxAccountClient;
-import org.mozilla.gecko.background.fxa.FxAccountClient20;
-import org.mozilla.gecko.background.fxa.FxAccountUtils;
-import org.mozilla.gecko.background.fxa.oauth.FxAccountAbstractClient.RequestDelegate;
-import org.mozilla.gecko.background.fxa.oauth.FxAccountAbstractClientException.FxAccountAbstractClientRemoteException;
-import org.mozilla.gecko.background.fxa.oauth.FxAccountOAuthClient10;
-import org.mozilla.gecko.background.fxa.oauth.FxAccountOAuthClient10.AuthorizationResponse;
-import org.mozilla.gecko.browserid.BrowserIDKeyPair;
-import org.mozilla.gecko.browserid.JSONWebTokenUtils;
 import org.mozilla.gecko.fxa.FxAccountConstants;
 import org.mozilla.gecko.fxa.activities.FxAccountGetStartedActivity;
-import org.mozilla.gecko.fxa.login.FxAccountLoginStateMachine;
-import org.mozilla.gecko.fxa.login.FxAccountLoginStateMachine.LoginStateMachineDelegate;
-import org.mozilla.gecko.fxa.login.FxAccountLoginTransition.Transition;
-import org.mozilla.gecko.fxa.login.Married;
-import org.mozilla.gecko.fxa.login.State;
-import org.mozilla.gecko.fxa.login.State.StateLabel;
-import org.mozilla.gecko.fxa.login.StateFactory;
-import org.mozilla.gecko.fxa.sync.FxAccountNotificationManager;
-import org.mozilla.gecko.fxa.sync.FxAccountSyncAdapter;
 
 import android.accounts.AbstractAccountAuthenticator;
 import android.accounts.Account;
 import android.accounts.AccountAuthenticatorResponse;
 import android.accounts.AccountManager;
 import android.accounts.NetworkErrorException;
 import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
 
 public class FxAccountAuthenticator extends AbstractAccountAuthenticator {
   public static final String LOG_TAG = FxAccountAuthenticator.class.getSimpleName();
-  public static final int UNKNOWN_ERROR_CODE = 999;
 
   protected final Context context;
   protected final AccountManager accountManager;
 
   public FxAccountAuthenticator(Context context) {
     super(context);
     this.context = context;
     this.accountManager = AccountManager.get(context);
@@ -86,216 +63,25 @@ public class FxAccountAuthenticator exte
 
   @Override
   public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
     Logger.debug(LOG_TAG, "editProperties");
 
     return null;
   }
 
-  protected static class Responder {
-    final AccountAuthenticatorResponse response;
-    final AndroidFxAccount fxAccount;
-
-    public Responder(AccountAuthenticatorResponse response, AndroidFxAccount fxAccount) {
-      this.response = response;
-      this.fxAccount = fxAccount;
-    }
-
-    public void fail(Exception e) {
-      Logger.warn(LOG_TAG, "Responding with error!", e);
-      fxAccount.releaseSharedAccountStateLock();
-      final Bundle result = new Bundle();
-      result.putInt(AccountManager.KEY_ERROR_CODE, UNKNOWN_ERROR_CODE);
-      result.putString(AccountManager.KEY_ERROR_MESSAGE, e.toString());
-      response.onResult(result);
-    }
-
-    public void succeed(String authToken) {
-      Logger.info(LOG_TAG, "Responding with success!");
-      fxAccount.releaseSharedAccountStateLock();
-      final Bundle result = new Bundle();
-      result.putString(AccountManager.KEY_ACCOUNT_NAME, fxAccount.account.name);
-      result.putString(AccountManager.KEY_ACCOUNT_TYPE, fxAccount.account.type);
-      result.putString(AccountManager.KEY_AUTHTOKEN, authToken);
-      response.onResult(result);
-    }
-  }
-
-  public abstract static class FxADefaultLoginStateMachineDelegate implements LoginStateMachineDelegate {
-    protected final Context context;
-    protected final AndroidFxAccount fxAccount;
-    protected final Executor executor;
-    protected final FxAccountClient client;
-
-    public FxADefaultLoginStateMachineDelegate(Context context, AndroidFxAccount fxAccount) {
-      this.context = context;
-      this.fxAccount = fxAccount;
-      this.executor = Executors.newSingleThreadExecutor();
-      this.client = new FxAccountClient20(fxAccount.getAccountServerURI(), executor);
-    }
-
-    @Override
-    public FxAccountClient getClient() {
-      return client;
-    }
-
-    @Override
-    public long getCertificateDurationInMilliseconds() {
-      return 12 * 60 * 60 * 1000;
-    }
-
-    @Override
-    public long getAssertionDurationInMilliseconds() {
-      return 15 * 60 * 1000;
-    }
-
-    @Override
-    public BrowserIDKeyPair generateKeyPair() throws NoSuchAlgorithmException {
-      return StateFactory.generateKeyPair();
-    }
-
-    @Override
-    public void handleTransition(Transition transition, State state) {
-      Logger.info(LOG_TAG, "handleTransition: " + transition + " to " + state.getStateLabel());
-    }
-
-    abstract public void handleNotMarried(State notMarried);
-    abstract public void handleMarried(Married married);
-
-    @Override
-    public void handleFinal(State state) {
-      Logger.info(LOG_TAG, "handleFinal: in " + state.getStateLabel());
-      fxAccount.setState(state);
-      // Update any notifications displayed.
-      final FxAccountNotificationManager notificationManager = new FxAccountNotificationManager(FxAccountSyncAdapter.NOTIFICATION_ID);
-      notificationManager.update(context, fxAccount);
-
-      if (state.getStateLabel() != StateLabel.Married) {
-        handleNotMarried(state);
-        return;
-      } else {
-        handleMarried((Married) state);
-      }
-    }
-  }
-
-  protected void getOAuthToken(final AccountAuthenticatorResponse response, final AndroidFxAccount fxAccount, final String scope) throws NetworkErrorException {
-    Logger.info(LOG_TAG, "Fetching oauth token with scope: " + scope);
-
-    final Responder responder = new Responder(response, fxAccount);
-
-    final String oauthServerUri = FxAccountConstants.DEFAULT_OAUTH_SERVER_ENDPOINT;
-    final String audience;
-    try {
-      audience = FxAccountUtils.getAudienceForURL(oauthServerUri); // The assertion gets traded in for an oauth bearer token.
-    } catch (Exception e) {
-      Logger.warn(LOG_TAG, "Got exception fetching oauth token.", e);
-      responder.fail(e);
-      return;
-    }
-
-    final FxAccountLoginStateMachine stateMachine = new FxAccountLoginStateMachine();
-
-    stateMachine.advance(fxAccount.getState(), StateLabel.Married, new FxADefaultLoginStateMachineDelegate(context, fxAccount) {
-      @Override
-      public void handleNotMarried(State state) {
-        final String message = "Cannot fetch oauth token from state: " + state.getStateLabel();
-        Logger.warn(LOG_TAG, message);
-        responder.fail(new RuntimeException(message));
-      }
-
-      @Override
-      public void handleMarried(final Married married) {
-        final String assertion;
-        try {
-          assertion = married.generateAssertion(audience, JSONWebTokenUtils.DEFAULT_ASSERTION_ISSUER);
-          if (FxAccountUtils.LOG_PERSONAL_INFORMATION) {
-            JSONWebTokenUtils.dumpAssertion(assertion);
-          }
-        } catch (Exception e) {
-          Logger.warn(LOG_TAG, "Got exception fetching oauth token.", e);
-          responder.fail(e);
-          return;
-        }
-
-        final FxAccountOAuthClient10 oauthClient = new FxAccountOAuthClient10(oauthServerUri, executor);
-        Logger.debug(LOG_TAG, "OAuth fetch for scope: " + scope);
-        oauthClient.authorization(FxAccountConstants.OAUTH_CLIENT_ID_FENNEC, assertion, null, scope, new RequestDelegate<FxAccountOAuthClient10.AuthorizationResponse>() {
-          @Override
-          public void handleSuccess(AuthorizationResponse result) {
-            Logger.debug(LOG_TAG, "OAuth success.");
-            FxAccountUtils.pii(LOG_TAG, "Fetched oauth token: " + result.access_token);
-            responder.succeed(result.access_token);
-          }
-
-          @Override
-          public void handleFailure(FxAccountAbstractClientRemoteException e) {
-            Logger.error(LOG_TAG, "OAuth failure.", e);
-            if (e.isInvalidAuthentication()) {
-              // We were married, generated an assertion, and our assertion was rejected by the
-              // oauth client. If it's a 401, we probably have a stale certificate.  If instead of
-              // a stale certificate we have bad credentials, the state machine will fail to sign
-              // our public key and drive us back to Separated.
-              fxAccount.setState(married.makeCohabitingState());
-            }
-            responder.fail(e);
-          }
-
-          @Override
-          public void handleError(Exception e) {
-            Logger.error(LOG_TAG, "OAuth error.", e);
-            responder.fail(e);
-          }
-        });
-      }
-    });
-  }
-
   @Override
   public Bundle getAuthToken(final AccountAuthenticatorResponse response,
       final Account account, final String authTokenType, final Bundle options)
           throws NetworkErrorException {
-    Logger.debug(LOG_TAG, "getAuthToken: " + authTokenType);
-
-    // If we have a cached authToken, hand it over.
-    final String cachedAuthToken = AccountManager.get(context).peekAuthToken(account, authTokenType);
-    if (cachedAuthToken != null && !cachedAuthToken.isEmpty()) {
-      Logger.info(LOG_TAG, "Return cached token.");
-      final Bundle result = new Bundle();
-      result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
-      result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
-      result.putString(AccountManager.KEY_AUTHTOKEN, cachedAuthToken);
-      return result;
-    }
+    Logger.debug(LOG_TAG, "getAuthToken");
 
-    // If we're asked for an oauth::scope token, try to generate one.
-    final String oauthPrefix = "oauth::";
-    if (authTokenType != null && authTokenType.startsWith(oauthPrefix)) {
-      final String scope = authTokenType.substring(oauthPrefix.length());
-      final AndroidFxAccount fxAccount = new AndroidFxAccount(context, account);
-      try {
-        fxAccount.acquireSharedAccountStateLock(LOG_TAG);
-      } catch (InterruptedException e) {
-        Logger.warn(LOG_TAG, "Could not acquire account state lock; return error bundle.");
-        final Bundle bundle = new Bundle();
-        bundle.putInt(AccountManager.KEY_ERROR_CODE, 1);
-        bundle.putString(AccountManager.KEY_ERROR_MESSAGE, "Could not acquire account state lock.");
-        return bundle;
-      }
-      getOAuthToken(response, fxAccount, scope);
-      return null;
-    }
+    Logger.warn(LOG_TAG, "Returning null bundle for getAuthToken.");
 
-    // Otherwise, fail.
-    Logger.warn(LOG_TAG, "Returning error bundle for getAuthToken with unknown token type.");
-    final Bundle bundle = new Bundle();
-    bundle.putInt(AccountManager.KEY_ERROR_CODE, 2);
-    bundle.putString(AccountManager.KEY_ERROR_MESSAGE, "Unknown token type: " + authTokenType);
-    return bundle;
+    return null;
   }
 
   @Override
   public String getAuthTokenLabel(String authTokenType) {
     Logger.debug(LOG_TAG, "getAuthTokenLabel");
 
     return null;
   }
--- a/mobile/android/base/fxa/login/Cohabiting.java
+++ b/mobile/android/base/fxa/login/Cohabiting.java
@@ -13,20 +13,16 @@ import org.mozilla.gecko.sync.ExtendedJS
 
 public class Cohabiting extends TokensAndKeysState {
   private static final String LOG_TAG = Cohabiting.class.getSimpleName();
 
   public Cohabiting(String email, String uid, byte[] sessionToken, byte[] kA, byte[] kB, BrowserIDKeyPair keyPair) {
     super(StateLabel.Cohabiting, email, uid, sessionToken, kA, kB, keyPair);
   }
 
-  public Married withCertificate(String certificate) {
-    return new Married(email, uid, sessionToken, kA, kB, keyPair, certificate);
-  }
-
   @Override
   public void execute(final ExecuteDelegate delegate) {
     delegate.getClient().sign(sessionToken, keyPair.getPublic().toJSONObject(), delegate.getCertificateDurationInMilliseconds(),
         new BaseRequestDelegate<String>(this, delegate) {
       @Override
       public void handleSuccess(String certificate) {
         if (FxAccountUtils.LOG_PERSONAL_INFORMATION) {
           try {
@@ -38,13 +34,13 @@ public class Cohabiting extends TokensAn
               FxAccountUtils.pii(LOG_TAG, "Signature: " + c.getString("signature"));
             } else {
               FxAccountUtils.pii(LOG_TAG, "Could not parse certificate!");
             }
           } catch (Exception e) {
             FxAccountUtils.pii(LOG_TAG, "Could not parse certificate!");
           }
         }
-        delegate.handleTransition(new LogMessage("sign succeeded"), withCertificate(certificate));
+        delegate.handleTransition(new LogMessage("sign succeeded"), new Married(email, uid, sessionToken, kA, kB, keyPair, certificate));
       }
     });
   }
 }
--- a/mobile/android/base/fxa/login/Married.java
+++ b/mobile/android/base/fxa/login/Married.java
@@ -107,12 +107,12 @@ public class Married extends TokensAndKe
 
   public String getClientState() {
     if (FxAccountUtils.LOG_PERSONAL_INFORMATION) {
       FxAccountUtils.pii(LOG_TAG, "Client state: " + this.clientState);
     }
     return this.clientState;
   }
 
-  public Cohabiting makeCohabitingState() {
+  public State makeCohabitingState() {
     return new Cohabiting(email, uid, sessionToken, kA, kB, keyPair);
   }
 }
--- a/mobile/android/base/fxa/receivers/FxAccountDeletedService.java
+++ b/mobile/android/base/fxa/receivers/FxAccountDeletedService.java
@@ -63,20 +63,16 @@ public class FxAccountDeletedService ext
     deletePickle(context);
 
     // Delete client database and non-local tabs.
     Logger.info(LOG_TAG, "Deleting the entire clients database and non-local tabs");
     FennecTabsRepository.deleteNonLocalClientsAndTabs(context);
 
     // Remove any displayed notifications.
     new FxAccountNotificationManager(FxAccountSyncAdapter.NOTIFICATION_ID).clear(context);
-
-    // Bug 1147275: Delete cached oauth tokens. There's no way to query all
-    // oauth tokens from Android, so this is tricky to do comprehensively. We
-    // can query, individually, for specific oauth tokens to delete, however.
   }
 
   public static void deletePickle(final Context context) {
     try {
       AccountPickler.deletePickle(context, FxAccountConstants.ACCOUNT_PICKLE_FILENAME);
     } catch (Exception e) {
       // This should never happen, but we really don't want to die in a background thread.
       Logger.warn(LOG_TAG, "Got exception deleting saved pickle file; ignoring.", e);
--- a/mobile/android/base/fxa/sync/FxAccountNotificationManager.java
+++ b/mobile/android/base/fxa/sync/FxAccountNotificationManager.java
@@ -60,17 +60,17 @@ public class FxAccountNotificationManage
    * Reflect new Firefox Account state to the notification manager: show or hide
    * notifications reflecting the state of a Firefox Account.
    *
    * @param context
    *          Android context.
    * @param fxAccount
    *          Firefox Account to reflect to the notification manager.
    */
-  public void update(Context context, AndroidFxAccount fxAccount) {
+  protected void update(Context context, AndroidFxAccount fxAccount) {
     final NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
 
     final State state = fxAccount.getState();
     final Action action = state.getNeededAction();
     if (action == Action.None) {
       Logger.info(LOG_TAG, "State " + state.getStateLabel() + " needs no action; cancelling any existing notification.");
       notificationManager.cancel(notificationId);
       return;
--- a/mobile/android/base/fxa/sync/FxAccountSyncAdapter.java
+++ b/mobile/android/base/fxa/sync/FxAccountSyncAdapter.java
@@ -1,37 +1,43 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.fxa.sync;
 
 import java.net.URI;
 import java.net.URISyntaxException;
+import java.security.NoSuchAlgorithmException;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.EnumSet;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 
 import org.mozilla.gecko.background.common.log.Logger;
+import org.mozilla.gecko.background.fxa.FxAccountClient;
+import org.mozilla.gecko.background.fxa.FxAccountClient20;
 import org.mozilla.gecko.background.fxa.FxAccountUtils;
 import org.mozilla.gecko.background.fxa.SkewHandler;
+import org.mozilla.gecko.browserid.BrowserIDKeyPair;
 import org.mozilla.gecko.browserid.JSONWebTokenUtils;
 import org.mozilla.gecko.fxa.FirefoxAccounts;
 import org.mozilla.gecko.fxa.FxAccountConstants;
 import org.mozilla.gecko.fxa.authenticator.AccountPickler;
 import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
-import org.mozilla.gecko.fxa.authenticator.FxADefaultLoginStateMachineDelegate;
 import org.mozilla.gecko.fxa.authenticator.FxAccountAuthenticator;
 import org.mozilla.gecko.fxa.login.FxAccountLoginStateMachine;
+import org.mozilla.gecko.fxa.login.FxAccountLoginStateMachine.LoginStateMachineDelegate;
+import org.mozilla.gecko.fxa.login.FxAccountLoginTransition.Transition;
 import org.mozilla.gecko.fxa.login.Married;
 import org.mozilla.gecko.fxa.login.State;
 import org.mozilla.gecko.fxa.login.State.StateLabel;
+import org.mozilla.gecko.fxa.login.StateFactory;
 import org.mozilla.gecko.sync.BackoffHandler;
 import org.mozilla.gecko.sync.GlobalSession;
 import org.mozilla.gecko.sync.PrefsBackoffHandler;
 import org.mozilla.gecko.sync.SharedPreferencesClientsDataDelegate;
 import org.mozilla.gecko.sync.SyncConfiguration;
 import org.mozilla.gecko.sync.ThreadPool;
 import org.mozilla.gecko.sync.Utils;
 import org.mozilla.gecko.sync.crypto.KeyBundle;
@@ -110,17 +116,17 @@ public class FxAccountSyncAdapter extend
     @Override
     public void rejectSync() {
       super.rejectSync();
     }
 
     protected final Collection<String> stageNamesToSync;
 
     public SyncDelegate(CountDownLatch latch, SyncResult syncResult, AndroidFxAccount fxAccount, Collection<String> stageNamesToSync) {
-      super(latch, syncResult);
+      super(latch, syncResult, fxAccount);
       this.stageNamesToSync = Collections.unmodifiableCollection(stageNamesToSync);
     }
 
     public Collection<String> getStageNamesToSync() {
       return this.stageNamesToSync;
     }
   }
 
@@ -229,30 +235,28 @@ public class FxAccountSyncAdapter extend
   protected void syncWithAssertion(final String audience,
                                    final String assertion,
                                    final URI tokenServerEndpointURI,
                                    final BackoffHandler tokenBackoffHandler,
                                    final SharedPreferences sharedPrefs,
                                    final KeyBundle syncKeyBundle,
                                    final String clientState,
                                    final SessionCallback callback,
-                                   final Bundle extras,
-                                   final AndroidFxAccount fxAccount) {
+                                   final Bundle extras) {
     final TokenServerClientDelegate delegate = new TokenServerClientDelegate() {
       private boolean didReceiveBackoff = false;
 
       @Override
       public String getUserAgent() {
         return FxAccountConstants.USER_AGENT;
       }
 
       @Override
       public void handleSuccess(final TokenServerToken token) {
         FxAccountUtils.pii(LOG_TAG, "Got token! uid is " + token.uid + " and endpoint is " + token.endpoint + ".");
-        fxAccount.releaseSharedAccountStateLock();
 
         if (!didReceiveBackoff) {
           // We must be OK to touch this token server.
           tokenBackoffHandler.setEarliestNextRequest(0L);
         }
 
         final URI storageServerURI;
         try {
@@ -320,34 +324,22 @@ public class FxAccountSyncAdapter extend
         } catch (Exception e) {
           callback.handleError(globalSession, e);
           return;
         }
       }
 
       @Override
       public void handleFailure(TokenServerException e) {
-        Logger.error(LOG_TAG, "Failed to get token.", e);
-        try {
-          // We should only get here *after* we're locked into the married state.
-          State state = fxAccount.getState();
-          if (state.getStateLabel() == StateLabel.Married) {
-            Married married = (Married) state;
-            fxAccount.setState(married.makeCohabitingState());
-          }
-        } finally {
-          fxAccount.releaseSharedAccountStateLock();
-        }
-        callback.handleError(null, e);
+        handleError(e);
       }
 
       @Override
       public void handleError(Exception e) {
         Logger.error(LOG_TAG, "Failed to get token.", e);
-        fxAccount.releaseSharedAccountStateLock();
         callback.handleError(null, e);
       }
 
       @Override
       public void handleBackoff(int backoffSeconds) {
         // This is the token server telling us to back off.
         Logger.info(LOG_TAG, "Token server requesting backoff of " + backoffSeconds + "s. Backoff handler: " + tokenBackoffHandler);
         didReceiveBackoff = true;
@@ -419,16 +411,24 @@ public class FxAccountSyncAdapter extend
     final CountDownLatch latch = new CountDownLatch(1);
 
     Collection<String> knownStageNames = SyncConfiguration.validEngineNames();
     Collection<String> stageNamesToSync = Utils.getStagesToSyncFromBundle(knownStageNames, extras);
 
     final SyncDelegate syncDelegate = new SyncDelegate(latch, syncResult, fxAccount, stageNamesToSync);
 
     try {
+      final State state;
+      try {
+        state = fxAccount.getState();
+      } catch (Exception e) {
+        syncDelegate.handleError(e);
+        return;
+      }
+
       // This will be the same chunk of SharedPreferences that we pass through to GlobalSession/SyncConfiguration.
       final SharedPreferences sharedPrefs = fxAccount.getSyncPrefs();
 
       final BackoffHandler backgroundBackoffHandler = new PrefsBackoffHandler(sharedPrefs, "background");
       final BackoffHandler rateLimitBackoffHandler = new PrefsBackoffHandler(sharedPrefs, "rate");
 
       // If this sync was triggered by user action, this will be true.
       final boolean isImmediate = (extras != null) &&
@@ -451,57 +451,67 @@ public class FxAccountSyncAdapter extend
       }
 
       final SchedulePolicy schedulePolicy = new FxAccountSchedulePolicy(context, fxAccount);
 
       // Set a small scheduled 'backoff' to rate-limit the next sync,
       // and extend the background delay even further into the future.
       schedulePolicy.configureBackoffMillisBeforeSyncing(rateLimitBackoffHandler, backgroundBackoffHandler);
 
+      final String authServerEndpoint = fxAccount.getAccountServerURI();
       final String tokenServerEndpoint = fxAccount.getTokenServerURI();
       final URI tokenServerEndpointURI = new URI(tokenServerEndpoint);
       final String audience = FxAccountUtils.getAudienceForURL(tokenServerEndpoint);
 
-      try {
-        // The clock starts... now!
-        fxAccount.acquireSharedAccountStateLock(FxAccountSyncAdapter.LOG_TAG);
-      } catch (InterruptedException e) {
-        // OK, skip this sync.
-        syncDelegate.handleError(e);
-        return;
-      }
+      // TODO: why doesn't the loginPolicy extract the audience from the account?
+      final FxAccountClient client = new FxAccountClient20(authServerEndpoint, executor);
+      final FxAccountLoginStateMachine stateMachine = new FxAccountLoginStateMachine();
+      stateMachine.advance(state, StateLabel.Married, new LoginStateMachineDelegate() {
+        @Override
+        public FxAccountClient getClient() {
+          return client;
+        }
+
+        @Override
+        public long getCertificateDurationInMilliseconds() {
+          return 12 * 60 * 60 * 1000;
+        }
 
-      final State state;
-      try {
-        state = fxAccount.getState();
-      } catch (Exception e) {
-        fxAccount.releaseSharedAccountStateLock();
-        syncDelegate.handleError(e);
-        return;
-      }
+        @Override
+        public long getAssertionDurationInMilliseconds() {
+          return 15 * 60 * 1000;
+        }
 
-      final FxAccountLoginStateMachine stateMachine = new FxAccountLoginStateMachine();
-      stateMachine.advance(state, StateLabel.Married, new FxADefaultLoginStateMachineDelegate(context, fxAccount) {
         @Override
-        public void handleNotMarried(State notMarried) {
-          Logger.info(LOG_TAG, "handleNotMarried: in " + notMarried.getStateLabel());
-          schedulePolicy.onHandleFinal(notMarried.getNeededAction());
-          syncDelegate.handleCannotSync(notMarried);
+        public BrowserIDKeyPair generateKeyPair() throws NoSuchAlgorithmException {
+          return StateFactory.generateKeyPair();
+        }
+
+        @Override
+        public void handleTransition(Transition transition, State state) {
+          Logger.info(LOG_TAG, "handleTransition: " + transition + " to " + state.getStateLabel());
         }
 
         private boolean shouldRequestToken(final BackoffHandler tokenBackoffHandler, final Bundle extras) {
           return shouldPerformSync(tokenBackoffHandler, "token", extras);
         }
 
         @Override
-        public void handleMarried(Married married) {
-          schedulePolicy.onHandleFinal(married.getNeededAction());
-          Logger.info(LOG_TAG, "handleMarried: in " + married.getStateLabel());
+        public void handleFinal(State state) {
+          Logger.info(LOG_TAG, "handleFinal: in " + state.getStateLabel());
+          fxAccount.setState(state);
+          schedulePolicy.onHandleFinal(state.getNeededAction());
+          notificationManager.update(context, fxAccount);
+          try {
+            if (state.getStateLabel() != StateLabel.Married) {
+              syncDelegate.handleCannotSync(state);
+              return;
+            }
 
-          try {
+            final Married married = (Married) state;
             final String assertion = married.generateAssertion(audience, JSONWebTokenUtils.DEFAULT_ASSERTION_ISSUER);
 
             /*
              * At this point we're in the correct state to sync, and we're ready to fetch
              * a token and do some work.
              *
              * But first we need to do two things:
              * 1. Check to see whether we're in a backoff situation for the token server.
@@ -524,28 +534,26 @@ public class FxAccountSyncAdapter extend
               Logger.info(LOG_TAG, "Not syncing (token server).");
               syncDelegate.postponeSync(tokenBackoffHandler.delayMilliseconds());
               return;
             }
 
             final SessionCallback sessionCallback = new SessionCallback(syncDelegate, schedulePolicy);
             final KeyBundle syncKeyBundle = married.getSyncKeyBundle();
             final String clientState = married.getClientState();
-            syncWithAssertion(audience, assertion, tokenServerEndpointURI, tokenBackoffHandler, sharedPrefs, syncKeyBundle, clientState, sessionCallback, extras, fxAccount);
+            syncWithAssertion(audience, assertion, tokenServerEndpointURI, tokenBackoffHandler, sharedPrefs, syncKeyBundle, clientState, sessionCallback, extras);
           } catch (Exception e) {
             syncDelegate.handleError(e);
             return;
           }
         }
       });
 
       latch.await();
     } catch (Exception e) {
       Logger.error(LOG_TAG, "Got error syncing.", e);
       syncDelegate.handleError(e);
-    } finally {
-      fxAccount.releaseSharedAccountStateLock();
     }
 
     Logger.info(LOG_TAG, "Syncing done.");
     lastSyncRealtimeMillis = SystemClock.elapsedRealtime();
   }
 }
--- a/mobile/android/base/fxa/sync/FxAccountSyncDelegate.java
+++ b/mobile/android/base/fxa/sync/FxAccountSyncDelegate.java
@@ -1,33 +1,42 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.fxa.sync;
 
 import java.util.concurrent.CountDownLatch;
 
+import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
+import org.mozilla.gecko.fxa.login.Married;
 import org.mozilla.gecko.fxa.login.State;
+import org.mozilla.gecko.fxa.login.State.StateLabel;
+import org.mozilla.gecko.tokenserver.TokenServerException;
 
 import android.content.SyncResult;
 
 public class FxAccountSyncDelegate {
   protected final CountDownLatch latch;
   protected final SyncResult syncResult;
+  protected final AndroidFxAccount fxAccount;
 
-  public FxAccountSyncDelegate(CountDownLatch latch, SyncResult syncResult) {
+  public FxAccountSyncDelegate(CountDownLatch latch, SyncResult syncResult, AndroidFxAccount fxAccount) {
     if (latch == null) {
       throw new IllegalArgumentException("latch must not be null");
     }
     if (syncResult == null) {
       throw new IllegalArgumentException("syncResult must not be null");
     }
+    if (fxAccount == null) {
+      throw new IllegalArgumentException("fxAccount must not be null");
+    }
     this.latch = latch;
     this.syncResult = syncResult;
+    this.fxAccount = fxAccount;
   }
 
   /**
    * No error!  Say that we made progress.
    */
   protected void setSyncResultSuccess() {
     syncResult.stats.numUpdates += 1;
   }
@@ -51,16 +60,26 @@ public class FxAccountSyncDelegate {
 
   public void handleSuccess() {
     setSyncResultSuccess();
     latch.countDown();
   }
 
   public void handleError(Exception e) {
     setSyncResultSoftError();
+    // This is awful, but we need to propagate bad assertions back up the
+    // chain somehow, and this will do for now.
+    if (e instanceof TokenServerException) {
+      // We should only get here *after* we're locked into the married state.
+      State state = fxAccount.getState();
+      if (state.getStateLabel() == StateLabel.Married) {
+        Married married = (Married) state;
+        fxAccount.setState(married.makeCohabitingState());
+      }
+    }
     latch.countDown();
   }
 
   /**
    * When the login machine terminates, we might not be in the
    * <code>Married</code> state, and therefore we can't sync. This method
    * messages as much to the user.
    * <p>
--- a/mobile/android/base/reading/ReadingListConstants.java
+++ b/mobile/android/base/reading/ReadingListConstants.java
@@ -7,13 +7,12 @@ package org.mozilla.gecko.reading;
 import org.mozilla.gecko.AppConstants;
 
 public class ReadingListConstants {
   public static final String GLOBAL_LOG_TAG = "FxReadingList";
   public static final String USER_AGENT = "Firefox-Android-FxReader/" + AppConstants.MOZ_APP_VERSION + " (" + AppConstants.MOZ_APP_DISPLAYNAME + ")";
   public static final String DEFAULT_DEV_ENDPOINT = "https://readinglist.dev.mozaws.net/v1/";
   public static final String DEFAULT_PROD_ENDPOINT = "https://readinglist.services.mozilla.com/v1/";
 
-  public static final String OAUTH_SCOPE_READINGLIST = "readinglist";
-  public static final String AUTH_TOKEN_TYPE = "oauth::" + OAUTH_SCOPE_READINGLIST;
+  public static final String OAUTH_ENDPOINT_PROD = "https://oauth.accounts.firefox.com/v1";
 
   public static boolean DEBUG = false;
 }
deleted file mode 100644
--- a/mobile/android/base/reading/ReadingListInvalidAuthenticationException.java
+++ /dev/null
@@ -1,18 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.gecko.reading;
-
-import org.mozilla.gecko.sync.net.MozResponse;
-
-public class ReadingListInvalidAuthenticationException extends Exception {
-  private static final long serialVersionUID = 7112459541558266597L;
-
-  public final MozResponse response;
-
-  public ReadingListInvalidAuthenticationException(MozResponse response) {
-    super();
-    this.response = response;
-  }
-}
--- a/mobile/android/base/reading/ReadingListSyncAdapter.java
+++ b/mobile/android/base/reading/ReadingListSyncAdapter.java
@@ -1,74 +1,87 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.reading;
 
 import java.net.URI;
 import java.net.URISyntaxException;
+import java.security.NoSuchAlgorithmException;
 import java.util.Collection;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.TimeUnit;
 
 import org.mozilla.gecko.background.common.PrefsBranch;
 import org.mozilla.gecko.background.common.log.Logger;
+import org.mozilla.gecko.background.fxa.FxAccountClient;
+import org.mozilla.gecko.background.fxa.FxAccountClient20;
 import org.mozilla.gecko.background.fxa.FxAccountUtils;
+import org.mozilla.gecko.background.fxa.oauth.FxAccountAbstractClient.RequestDelegate;
+import org.mozilla.gecko.background.fxa.oauth.FxAccountAbstractClientException.FxAccountAbstractClientRemoteException;
+import org.mozilla.gecko.background.fxa.oauth.FxAccountOAuthClient10;
+import org.mozilla.gecko.background.fxa.oauth.FxAccountOAuthClient10.AuthorizationResponse;
+import org.mozilla.gecko.browserid.BrowserIDKeyPair;
+import org.mozilla.gecko.browserid.JSONWebTokenUtils;
 import org.mozilla.gecko.db.BrowserContract.ReadingListItems;
 import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
+import org.mozilla.gecko.fxa.login.FxAccountLoginStateMachine;
+import org.mozilla.gecko.fxa.login.FxAccountLoginStateMachine.LoginStateMachineDelegate;
+import org.mozilla.gecko.fxa.login.FxAccountLoginTransition.Transition;
+import org.mozilla.gecko.fxa.login.Married;
+import org.mozilla.gecko.fxa.login.State;
+import org.mozilla.gecko.fxa.login.State.StateLabel;
+import org.mozilla.gecko.fxa.login.StateFactory;
 import org.mozilla.gecko.fxa.sync.FxAccountSyncDelegate;
 import org.mozilla.gecko.sync.net.AuthHeaderProvider;
 import org.mozilla.gecko.sync.net.BearerAuthHeaderProvider;
 
 import android.accounts.Account;
-import android.accounts.AccountManager;
 import android.content.AbstractThreadedSyncAdapter;
 import android.content.ContentProviderClient;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.content.SyncResult;
 import android.os.Bundle;
 
 public class ReadingListSyncAdapter extends AbstractThreadedSyncAdapter {
   public static final String PREF_LOCAL_NAME = "device.localname";
+  public static final String OAUTH_CLIENT_ID_FENNEC = "3332a18d142636cb";
+  public static final String OAUTH_SCOPE_READINGLIST = "readinglist";
 
   private static final String LOG_TAG = ReadingListSyncAdapter.class.getSimpleName();
   private static final long TIMEOUT_SECONDS = 60;
   protected final ExecutorService executor;
 
   public ReadingListSyncAdapter(Context context, boolean autoInitialize) {
     super(context, autoInitialize);
     this.executor = Executors.newSingleThreadExecutor();
   }
 
-  protected static abstract class SyncAdapterSynchronizerDelegate implements ReadingListSynchronizerDelegate {
+
+  static final class SyncAdapterSynchronizerDelegate implements ReadingListSynchronizerDelegate {
     private final FxAccountSyncDelegate syncDelegate;
     private final ContentProviderClient cpc;
     private final SyncResult result;
 
     SyncAdapterSynchronizerDelegate(FxAccountSyncDelegate syncDelegate,
                                     ContentProviderClient cpc,
                                     SyncResult result) {
       this.syncDelegate = syncDelegate;
       this.cpc = cpc;
       this.result = result;
     }
 
-    abstract public void onInvalidAuthentication();
-
     @Override
     public void onUnableToSync(Exception e) {
       Logger.warn(LOG_TAG, "Unable to sync.", e);
-      if (e instanceof ReadingListInvalidAuthenticationException) {
-        onInvalidAuthentication();
-      }
       cpc.release();
       syncDelegate.handleError(e);
     }
 
     @Override
     public void onDeletionsUploadComplete() {
       Logger.debug(LOG_TAG, "Step: onDeletionsUploadComplete");
       this.result.stats.numEntries += 1;   // TODO: Bug 1140809.
@@ -103,98 +116,168 @@ public class ReadingListSyncAdapter exte
     @Override
     public void onComplete() {
       Logger.info(LOG_TAG, "Reading list synchronization complete.");
       cpc.release();
       syncDelegate.handleSuccess();
     }
   }
 
-  private void syncWithAuthorization(final Context context,
-                                     final Account account,
-                                     final SyncResult syncResult,
-                                     final FxAccountSyncDelegate syncDelegate,
-                                     final String authToken,
-                                     final SharedPreferences sharedPrefs,
-                                     final Bundle extras) {
-    final AuthHeaderProvider auth = new BearerAuthHeaderProvider(authToken);
-
-    final String endpointString = ReadingListConstants.DEFAULT_PROD_ENDPOINT;
-    final URI endpoint;
-    Logger.info(LOG_TAG, "Syncing reading list against " + endpointString);
-    try {
-      endpoint = new URI(endpointString);
-    } catch (URISyntaxException e) {
-      // Should never happen.
-      Logger.error(LOG_TAG, "Unexpected malformed URI for reading list service: " + endpointString);
-      syncDelegate.handleError(e);
-      return;
-    }
-
-    final PrefsBranch branch = new PrefsBranch(sharedPrefs, "readinglist.");
-    final ReadingListClient remote = new ReadingListClient(endpoint, auth);
-    final ContentProviderClient cpc = getContentProviderClient(context); // Released by the inner SyncAdapterSynchronizerDelegate.
-
-    final LocalReadingListStorage local = new LocalReadingListStorage(cpc);
-    String localName = branch.getString(PREF_LOCAL_NAME, null);
-    if (localName == null) {
-      localName = FxAccountUtils.defaultClientName(context);
-    }
-
-    // Make sure DB rows don't refer to placeholder values.
-    local.updateLocalNames(localName);
-
-    final ReadingListSynchronizer synchronizer = new ReadingListSynchronizer(branch, remote, local);
-
-    synchronizer.syncAll(new SyncAdapterSynchronizerDelegate(syncDelegate, cpc, syncResult) {
-      @Override
-      public void onInvalidAuthentication() {
-        // The reading list server rejected our oauth token! Invalidate it. Next
-        // time through, we'll request a new one, which will drive the login
-        // state machine, produce a new assertion, and eventually a fresh token.
-        Logger.info(LOG_TAG, "Invalidating oauth token after 401!");
-        AccountManager.get(context).invalidateAuthToken(account.type, authToken);
-      }
-    });
-    // TODO: backoffs, and everything else handled by a SessionCallback.
-  }
-
   @Override
   public void onPerformSync(final Account account, final Bundle extras, final String authority, final ContentProviderClient provider, final SyncResult syncResult) {
     Logger.setThreadLogTag(ReadingListConstants.GLOBAL_LOG_TAG);
     Logger.resetLogging();
 
     final Context context = getContext();
     final AndroidFxAccount fxAccount = new AndroidFxAccount(context, account);
 
+    // If this sync was triggered by user action, this will be true.
+    final boolean isImmediate = (extras != null) &&
+        (extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, false) ||
+            extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false));
+
     final CountDownLatch latch = new CountDownLatch(1);
-    final FxAccountSyncDelegate syncDelegate = new FxAccountSyncDelegate(latch, syncResult);
+    final FxAccountSyncDelegate syncDelegate = new FxAccountSyncDelegate(latch, syncResult, fxAccount);
+    try {
+      final State state;
+      try {
+        state = fxAccount.getState();
+      } catch (Exception e) {
+        Logger.error(LOG_TAG, "Unable to sync.", e);
+        return;
+      }
+
+      final String oauthServerUri = ReadingListConstants.OAUTH_ENDPOINT_PROD;
+      final String authServerEndpoint = fxAccount.getAccountServerURI();
+      final String audience = FxAccountUtils.getAudienceForURL(oauthServerUri); // The assertion gets traded in for an oauth bearer token.
+
+      final SharedPreferences sharedPrefs = fxAccount.getReadingListPrefs();
+      final FxAccountClient client = new FxAccountClient20(authServerEndpoint, executor);
+      final FxAccountLoginStateMachine stateMachine = new FxAccountLoginStateMachine();
+
+      stateMachine.advance(state, StateLabel.Married, new LoginStateMachineDelegate() {
+        @Override
+        public FxAccountClient getClient() {
+          return client;
+        }
+
+        @Override
+        public long getCertificateDurationInMilliseconds() {
+          return 12 * 60 * 60 * 1000;
+        }
+
+        @Override
+        public long getAssertionDurationInMilliseconds() {
+          return 15 * 60 * 1000;
+        }
+
+        @Override
+        public BrowserIDKeyPair generateKeyPair() throws NoSuchAlgorithmException {
+          return StateFactory.generateKeyPair();
+        }
+
+        @Override
+        public void handleTransition(Transition transition, State state) {
+          Logger.info(LOG_TAG, "handleTransition: " + transition + " to " + state.getStateLabel());
+        }
+
+        @Override
+        public void handleFinal(State state) {
+          Logger.info(LOG_TAG, "handleFinal: in " + state.getStateLabel());
+          fxAccount.setState(state);
+
+          // TODO: scheduling, notifications.
+          try {
+            if (state.getStateLabel() != StateLabel.Married) {
+              syncDelegate.handleCannotSync(state);
+              return;
+            }
+
+            final Married married = (Married) state;
+            final String assertion = married.generateAssertion(audience, JSONWebTokenUtils.DEFAULT_ASSERTION_ISSUER);
+            JSONWebTokenUtils.dumpAssertion(assertion);
 
-    final AccountManager accountManager = AccountManager.get(context);
-    // If we have an auth failure that requires user intervention, FxA will show system
-    // notifications prompting the user to re-connect as it advances the internal account state.
-    // true causes the auth token fetch to return null on failure immediately, rather than doing
-    // Mysterious Internal Work to try to get the token.
-    final boolean notifyAuthFailure = true;
-    try {
-      final String authToken = accountManager.blockingGetAuthToken(account, ReadingListConstants.AUTH_TOKEN_TYPE, notifyAuthFailure);
-      if (authToken == null) {
-        throw new RuntimeException("Couldn't get oauth token!  Aborting sync.");
-      }
-      final SharedPreferences sharedPrefs = fxAccount.getReadingListPrefs();
-      syncWithAuthorization(context, account, syncResult, syncDelegate, authToken, sharedPrefs, extras);
+            final String clientID = OAUTH_CLIENT_ID_FENNEC;
+            final String scope = OAUTH_SCOPE_READINGLIST;
+            syncWithAssertion(clientID, scope, assertion, sharedPrefs, extras);
+          } catch (Exception e) {
+            syncDelegate.handleError(e);
+            return;
+          }
+        }
+
+        private void syncWithAssertion(final String client_id, final String scope, final String assertion,
+                                       final SharedPreferences sharedPrefs, final Bundle extras) {
+          final FxAccountOAuthClient10 oauthClient = new FxAccountOAuthClient10(oauthServerUri, executor);
+          Logger.debug(LOG_TAG, "OAuth fetch.");
+          oauthClient.authorization(client_id, assertion, null, scope, new RequestDelegate<FxAccountOAuthClient10.AuthorizationResponse>() {
+            @Override
+            public void handleSuccess(AuthorizationResponse result) {
+              Logger.debug(LOG_TAG, "OAuth success.");
+              syncWithAuthorization(result, sharedPrefs, extras);
+            }
+
+            @Override
+            public void handleFailure(FxAccountAbstractClientRemoteException e) {
+              Logger.error(LOG_TAG, "OAuth failure.", e);
+              syncDelegate.handleError(e);
+            }
+
+            @Override
+            public void handleError(Exception e) {
+              Logger.error(LOG_TAG, "OAuth error.", e);
+              syncDelegate.handleError(e);
+            }
+          });
+        }
+
+        private void syncWithAuthorization(AuthorizationResponse authResponse,
+                                           SharedPreferences sharedPrefs,
+                                           Bundle extras) {
+          final AuthHeaderProvider auth = new BearerAuthHeaderProvider(authResponse.access_token);
+
+          final String endpointString = ReadingListConstants.DEFAULT_DEV_ENDPOINT;
+          final URI endpoint;
+          Logger.info(LOG_TAG, "XXX Syncing to " + endpointString);
+          try {
+            endpoint = new URI(endpointString);
+          } catch (URISyntaxException e) {
+            // Should never happen.
+            Logger.error(LOG_TAG, "Unexpected malformed URI for reading list service: " + endpointString);
+            syncDelegate.handleError(e);
+            return;
+          }
+
+          final PrefsBranch branch = new PrefsBranch(sharedPrefs, "readinglist.");
+          final ReadingListClient remote = new ReadingListClient(endpoint, auth);
+          final ContentProviderClient cpc = getContentProviderClient(context);     // TODO: make sure I'm always released!
+
+          final LocalReadingListStorage local = new LocalReadingListStorage(cpc);
+          String localName = branch.getString(PREF_LOCAL_NAME, null);
+          if (localName == null) {
+            localName = FxAccountUtils.defaultClientName(context);
+          }
+
+          // Make sure DB rows don't refer to placeholder values.
+          local.updateLocalNames(localName);
+
+          final ReadingListSynchronizer synchronizer = new ReadingListSynchronizer(branch, remote, local);
+
+          synchronizer.syncAll(new SyncAdapterSynchronizerDelegate(syncDelegate, cpc, syncResult));
+          // TODO: backoffs, and everything else handled by a SessionCallback.
+        }
+      });
 
       latch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS);
       Logger.info(LOG_TAG, "Reading list sync done.");
+
     } catch (Exception e) {
-      // We can get lots of exceptions here; handle them uniformly.
       Logger.error(LOG_TAG, "Got error syncing.", e);
       syncDelegate.handleError(e);
     }
-
     /*
      * TODO:
      * * Account error notifications. How do we avoid these overlapping with Sync?
      * * Pickling. How do we avoid pickling twice if you use both Sync and RL?
      */
 
     /*
      * TODO:
@@ -203,14 +286,15 @@ public class ReadingListSyncAdapter exte
      * * Syncing.
      * * Error handling.
      * * Backoff and retry-after.
      * * Sync scheduling.
      * * Forcing syncs/interactive use.
      */
   }
 
+
   private ContentProviderClient getContentProviderClient(Context context) {
     final ContentResolver contentResolver = context.getContentResolver();
     final ContentProviderClient client = contentResolver.acquireContentProviderClient(ReadingListItems.CONTENT_URI);
     return client;
   }
 }
--- a/mobile/android/base/reading/ReadingListSynchronizer.java
+++ b/mobile/android/base/reading/ReadingListSynchronizer.java
@@ -769,21 +769,17 @@ public class ReadingListSynchronizer {
         delegate.fail(error);
       }
 
       @Override
       public void onFailure(MozResponse response) {
         final int statusCode = response.getStatusCode();
         Logger.error(LOG_TAG, "Download failed. since = " + since + ". Response: " + statusCode);
         response.logResponseBody(LOG_TAG);
-        if (response.isInvalidAuthentication()) {
-          delegate.fail(new ReadingListInvalidAuthenticationException(response));
-        } else {
-          delegate.fail();
-        }
+        delegate.fail();
       }
 
       @Override
       public void onComplete(ReadingListResponse response) {
         long lastModified = response.getLastModified();
         Logger.info(LOG_TAG, "Server last modified: " + lastModified);
         try {
           postDownload.finish();
--- a/mobile/android/base/resources/xml/fxaccount_status_prefscreen.xml
+++ b/mobile/android/base/resources/xml/fxaccount_status_prefscreen.xml
@@ -121,19 +121,15 @@
                 android:targetPackage="@string/browser_intent_package" />
         </Preference>
     </PreferenceCategory>
     <PreferenceCategory
         android:key="debug_category" >
         <Preference android:key="debug_refresh" />
         <Preference android:key="debug_dump" />
         <Preference android:key="debug_force_sync" />
-        <Preference android:key="debug_invalidate_certificate" />
-        <Preference android:key="debug_forget_reading_list_oauth_token" />
         <Preference android:key="debug_forget_certificate" />
         <Preference android:key="debug_require_password" />
         <Preference android:key="debug_require_upgrade" />
         <Preference android:key="debug_migrated_from_sync11" />
-        <Preference android:key="debug_make_account_stage" />
-        <Preference android:key="debug_make_account_default" />
-	</PreferenceCategory>
+    </PreferenceCategory>
 
 </PreferenceScreen>
--- a/mobile/android/base/sync/net/MozResponse.java
+++ b/mobile/android/base/sync/net/MozResponse.java
@@ -14,17 +14,16 @@ import java.util.Scanner;
 import org.json.simple.parser.ParseException;
 import org.mozilla.gecko.background.common.log.Logger;
 import org.mozilla.gecko.sync.ExtendedJSONObject;
 import org.mozilla.gecko.sync.NonObjectJSONException;
 
 import ch.boye.httpclientandroidlib.Header;
 import ch.boye.httpclientandroidlib.HttpEntity;
 import ch.boye.httpclientandroidlib.HttpResponse;
-import ch.boye.httpclientandroidlib.HttpStatus;
 import ch.boye.httpclientandroidlib.impl.cookie.DateParseException;
 import ch.boye.httpclientandroidlib.impl.cookie.DateUtils;
 
 public class MozResponse {
   private static final String LOG_TAG = "MozResponse";
 
   private static final String HEADER_RETRY_AFTER = "retry-after";
 
@@ -38,20 +37,16 @@ public class MozResponse {
   public int getStatusCode() {
     return this.response.getStatusLine().getStatusCode();
   }
 
   public boolean wasSuccessful() {
     return this.getStatusCode() == 200;
   }
 
-  public boolean isInvalidAuthentication() {
-    return this.getStatusCode() == HttpStatus.SC_UNAUTHORIZED;
-  }
-
   /**
    * Fetch the content type of the HTTP response body.
    *
    * @return a <code>Header</code> instance, or <code>null</code> if there was
    *         no body or no valid Content-Type.
    */
   public Header getContentType() {
     HttpEntity entity = this.response.getEntity();