Bug 1147473 - Expose Firefox Account debug information from Settings activity. r=rnewman
☠☠ backed out by cb309087f978 ☠ ☠
authorNick Alexander <nalexander@mozilla.com>
Fri, 27 Mar 2015 12:16:38 -0700
changeset 266501 24bf86f4b440353e074c3e5c8323b2b218aa4b0e
parent 266471 f81a156c8a71af5dc7a095034a11759e2d2c4bd6
child 266502 6c3fd5d5aa407616b09fea84a0136fcd8692501b
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)
reviewersrnewman
bugs1147473, 1142596
milestone39.0a1
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
Bug 1147473 - Expose Firefox Account debug information from Settings activity. r=rnewman ======== https://github.com/mozilla-services/android-sync/commit/afcbbba32cdf290afc26e89a7db8c739f0216d5e Author: Nick Alexander <nalexander@mozilla.com> Date: Tue Mar 24 23:24:27 2015 -0700 Bug 1147473 - Part 2: Make it possible to toggle Firefox Accounts debug / PII logging at runtime. Tap the email address in the Firefox Account settings activity 5 times. Only available when: !defined(MOZILLA_OFFICIAL) || defined(NIGHTLY_BUILD) || defined(MOZ_DEBUG) ======== https://github.com/mozilla-services/android-sync/commit/261f912d1267d52ffb0268d2a83c7870caad8457 Author: Nick Alexander <nalexander@mozilla.com> Date: Tue Mar 24 22:10:05 2015 -0700 Bug 1147473 - Part 1: Add additional (dangerous!) debug commands. These make it easier to test oauth token authorization, especially using stage servers. ======== https://github.com/mozilla-services/android-sync/commit/c1509baa7736e95c4bda9ea972dd407ca5966536 Author: Nick Alexander <nalexander@mozilla.com> Date: Tue Mar 24 22:06:43 2015 -0700 Bug 1142596 - Pre: Move constants around. ======== https://github.com/mozilla-services/android-sync/commit/2c676500c32ae11e06fa65fd0aa9c05e13d3fd79 Author: Nick Alexander <nalexander@mozilla.com> Date: Thu Mar 26 11:28:22 2015 -0700 Bug 1142596 - Pre: Make it easier to construct intermediate states.
mobile/android/base/fxa/FxAccountConstants.java
mobile/android/base/fxa/activities/FxAccountStatusFragment.java
mobile/android/base/fxa/authenticator/AndroidFxAccount.java
mobile/android/base/fxa/login/Cohabiting.java
mobile/android/base/fxa/login/Married.java
mobile/android/base/reading/ReadingListConstants.java
mobile/android/base/reading/ReadingListSyncAdapter.java
mobile/android/base/resources/xml/fxaccount_status_prefscreen.xml
--- a/mobile/android/base/fxa/FxAccountConstants.java
+++ b/mobile/android/base/fxa/FxAccountConstants.java
@@ -5,21 +5,26 @@
 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://api-accounts.stage.mozaws.net/v1";
-  public static final String STAGE_TOKEN_SERVER_ENDPOINT = "https://token.stage.mozaws.net/1.0/sync/1.5";
+  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";
 
   // 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,77 +1,81 @@
 /* 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 java.util.Calendar;
-import java.util.Date;
-import java.util.GregorianCalendar;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
+import android.widget.Toast;
 
 
 /**
  * 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;
@@ -79,16 +83,25 @@ 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;
@@ -172,16 +185,18 @@ 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);
@@ -209,16 +224,27 @@ 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?
@@ -759,42 +785,71 @@ 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;
     }
   }
 
   /**
@@ -802,30 +857,22 @@ 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 Preference debugCategory = ensureFindPreference("debug_category");
+    final PreferenceCategory debugCategory = (PreferenceCategory) ensureFindPreference("debug_category");
     debugCategory.setTitle(debugCategory.getKey());
 
-    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!
+    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!
       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
@@ -615,9 +615,40 @@ 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);
+  }
 }
--- a/mobile/android/base/fxa/login/Cohabiting.java
+++ b/mobile/android/base/fxa/login/Cohabiting.java
@@ -13,16 +13,20 @@ 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 {
@@ -34,13 +38,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"), new Married(email, uid, sessionToken, kA, kB, keyPair, certificate));
+        delegate.handleTransition(new LogMessage("sign succeeded"), withCertificate(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 State makeCohabitingState() {
+  public Cohabiting makeCohabitingState() {
     return new Cohabiting(email, uid, sessionToken, kA, kB, keyPair);
   }
 }
--- a/mobile/android/base/reading/ReadingListConstants.java
+++ b/mobile/android/base/reading/ReadingListConstants.java
@@ -7,12 +7,13 @@ 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_ENDPOINT_PROD = "https://oauth.accounts.firefox.com/v1";
+  public static final String OAUTH_SCOPE_READINGLIST = "readinglist";
+  public static final String AUTH_TOKEN_TYPE = "oauth::" + OAUTH_SCOPE_READINGLIST;
 
   public static boolean DEBUG = false;
 }
--- a/mobile/android/base/reading/ReadingListSyncAdapter.java
+++ b/mobile/android/base/reading/ReadingListSyncAdapter.java
@@ -20,16 +20,17 @@ import org.mozilla.gecko.background.fxa.
 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.FxAccountConstants;
 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;
@@ -43,18 +44,16 @@ import android.content.ContentProviderCl
 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();
@@ -140,17 +139,17 @@ public class ReadingListSyncAdapter exte
       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 oauthServerUri = FxAccountConstants.STAGE_OAUTH_SERVER_ENDPOINT;
       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() {
@@ -190,18 +189,18 @@ public class ReadingListSyncAdapter exte
               syncDelegate.handleCannotSync(state);
               return;
             }
 
             final Married married = (Married) state;
             final String assertion = married.generateAssertion(audience, JSONWebTokenUtils.DEFAULT_ASSERTION_ISSUER);
             JSONWebTokenUtils.dumpAssertion(assertion);
 
-            final String clientID = OAUTH_CLIENT_ID_FENNEC;
-            final String scope = OAUTH_SCOPE_READINGLIST;
+            final String clientID = FxAccountConstants.OAUTH_CLIENT_ID_FENNEC;
+            final String scope = ReadingListConstants.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,
--- a/mobile/android/base/resources/xml/fxaccount_status_prefscreen.xml
+++ b/mobile/android/base/resources/xml/fxaccount_status_prefscreen.xml
@@ -121,15 +121,19 @@
                 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" />
-    </PreferenceCategory>
+        <Preference android:key="debug_make_account_stage" />
+        <Preference android:key="debug_make_account_default" />
+	</PreferenceCategory>
 
 </PreferenceScreen>