summaryrefslogtreecommitdiff
path: root/data/patches/disable/0003-upgrade-mobile-sync-to-v32.patch
diff options
context:
space:
mode:
Diffstat (limited to 'data/patches/disable/0003-upgrade-mobile-sync-to-v32.patch')
-rw-r--r--data/patches/disable/0003-upgrade-mobile-sync-to-v32.patch992
1 files changed, 992 insertions, 0 deletions
diff --git a/data/patches/disable/0003-upgrade-mobile-sync-to-v32.patch b/data/patches/disable/0003-upgrade-mobile-sync-to-v32.patch
new file mode 100644
index 0000000..e420727
--- /dev/null
+++ b/data/patches/disable/0003-upgrade-mobile-sync-to-v32.patch
@@ -0,0 +1,992 @@
+diff -ruN mozilla-esr31/mobile/android/base/fxa/FirefoxAccounts.java mozilla-release/mobile/android/base/fxa/FirefoxAccounts.java
+--- mozilla-esr31/mobile/android/base/fxa/FirefoxAccounts.java 2015-02-25 19:21:06.000000000 +0100
++++ mozilla-release/mobile/android/base/fxa/FirefoxAccounts.java 2014-09-24 03:05:32.000000000 +0200
+@@ -6,13 +6,18 @@
+
+ import java.io.File;
+ import java.util.EnumSet;
++import java.util.Locale;
+ import java.util.concurrent.CountDownLatch;
+
++import org.mozilla.gecko.AppConstants;
++import org.mozilla.gecko.R;
+ import org.mozilla.gecko.background.common.log.Logger;
+ import org.mozilla.gecko.fxa.authenticator.AccountPickler;
+ import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
++import org.mozilla.gecko.fxa.login.State;
+ import org.mozilla.gecko.fxa.sync.FxAccountSyncAdapter;
+ import org.mozilla.gecko.fxa.sync.FxAccountSyncStatusHelper;
++import org.mozilla.gecko.fxa.tasks.FxAccountCodeResender;
+ import org.mozilla.gecko.sync.ThreadPool;
+ import org.mozilla.gecko.sync.Utils;
+
+@@ -20,6 +25,7 @@
+ import android.accounts.AccountManager;
+ import android.content.ContentResolver;
+ import android.content.Context;
++import android.content.res.Resources;
+ import android.os.Bundle;
+
+ /**
+@@ -152,6 +158,38 @@
+ return null;
+ }
+
++ /**
++ * @return
++ * the {@link State} instance associated with the current account, or <code>null</code> if
++ * no accounts exist.
++ */
++ public static State getFirefoxAccountState(final Context context) {
++ final Account account = getFirefoxAccount(context);
++ if (account == null) {
++ return null;
++ }
++
++ final AndroidFxAccount fxAccount = new AndroidFxAccount(context, account);
++ try {
++ return fxAccount.getState();
++ } catch (final Exception ex) {
++ Logger.warn(LOG_TAG, "Could not get FX account state.", ex);
++ return null;
++ }
++ }
++
++ /*
++ * @param context Android context
++ * @return the email address associated with the configured Firefox account if one exists; null otherwise.
++ */
++ public static String getFirefoxAccountEmail(final Context context) {
++ final Account account = getFirefoxAccount(context);
++ if (account == null) {
++ return null;
++ }
++ return account.name;
++ }
++
+ protected static void putHintsToSync(final Bundle extras, EnumSet<SyncHint> syncHints) {
+ // stagesToSync and stagesToSkip are allowed to be null.
+ if (syncHints == null) {
+@@ -264,4 +302,33 @@
+ // stopObserving null-checks its argument.
+ FxAccountSyncStatusHelper.getInstance().stopObserving(syncStatusListener);
+ }
++
++ public static String getOldSyncUpgradeURL(final Resources res, final Locale locale) {
++ final String VERSION = AppConstants.MOZ_APP_VERSION;
++ final String OS = AppConstants.OS_TARGET;
++ final String LOCALE = Utils.getLanguageTag(locale);
++ return res.getString(R.string.fxaccount_link_old_firefox, VERSION, OS, LOCALE);
++ }
++
++ /**
++ * Resends the account verification email, and displays an appropriate
++ * toast on both send success and failure. Note that because the underlying implementation
++ * uses {@link AsyncTask}, the provided context must be UI-capable, and this
++ * method called from the UI thread (see
++ * {@link org.mozilla.gecko.fxa.tasks.FxAccountCodeResender#resendCode(Context, AndroidFxAccount)}
++ * for more).
++ *
++ * @param context a UI-capable Android context.
++ * @return true if an account exists, false otherwise.
++ */
++ public static boolean resendVerificationEmail(final Context context) {
++ final Account account = getFirefoxAccount(context);
++ if (account == null) {
++ return false;
++ }
++
++ final AndroidFxAccount fxAccount = new AndroidFxAccount(context, account);
++ FxAccountCodeResender.resendCode(context, fxAccount);
++ return true;
++ }
+ }
+diff -ruN mozilla-esr31/mobile/android/base/fxa/activities/FxAccountAbstractSetupActivity.java mozilla-release/mobile/android/base/fxa/activities/FxAccountAbstractSetupActivity.java
+--- mozilla-esr31/mobile/android/base/fxa/activities/FxAccountAbstractSetupActivity.java 2015-02-25 19:21:06.000000000 +0100
++++ mozilla-release/mobile/android/base/fxa/activities/FxAccountAbstractSetupActivity.java 2014-09-24 03:05:32.000000000 +0200
+@@ -19,10 +19,10 @@
+ import org.mozilla.gecko.background.fxa.PasswordStretcher;
+ import org.mozilla.gecko.background.fxa.QuickPasswordStretcher;
+ import org.mozilla.gecko.fxa.FxAccountConstants;
+-import org.mozilla.gecko.fxa.activities.FxAccountSetupTask.ProgressDisplay;
+ import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
+ import org.mozilla.gecko.fxa.login.Engaged;
+ import org.mozilla.gecko.fxa.login.State;
++import org.mozilla.gecko.fxa.tasks.FxAccountSetupTask.ProgressDisplay;
+ import org.mozilla.gecko.sync.SyncConfiguration;
+ import org.mozilla.gecko.sync.setup.Constants;
+ import org.mozilla.gecko.sync.setup.activities.ActivityUtils;
+diff -ruN mozilla-esr31/mobile/android/base/fxa/activities/FxAccountConfirmAccountActivity.java mozilla-release/mobile/android/base/fxa/activities/FxAccountConfirmAccountActivity.java
+--- mozilla-esr31/mobile/android/base/fxa/activities/FxAccountConfirmAccountActivity.java 2015-02-25 19:21:06.000000000 +0100
++++ mozilla-release/mobile/android/base/fxa/activities/FxAccountConfirmAccountActivity.java 2014-09-24 03:05:32.000000000 +0200
+@@ -4,21 +4,15 @@
+
+ package org.mozilla.gecko.fxa.activities;
+
+-import java.util.concurrent.Executor;
+-import java.util.concurrent.Executors;
+-
+ import org.mozilla.gecko.R;
+ import org.mozilla.gecko.background.common.log.Logger;
+-import org.mozilla.gecko.background.fxa.FxAccountClient;
+-import org.mozilla.gecko.background.fxa.FxAccountClient10.RequestDelegate;
+-import org.mozilla.gecko.background.fxa.FxAccountClient20;
+-import org.mozilla.gecko.background.fxa.FxAccountClientException.FxAccountClientRemoteException;
+ import org.mozilla.gecko.fxa.FirefoxAccounts;
+ import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
+ import org.mozilla.gecko.fxa.login.Engaged;
+ import org.mozilla.gecko.fxa.login.State;
+ import org.mozilla.gecko.fxa.login.State.Action;
+ import org.mozilla.gecko.fxa.sync.FxAccountSyncStatusHelper;
++import org.mozilla.gecko.fxa.tasks.FxAccountCodeResender;
+ import org.mozilla.gecko.sync.setup.activities.ActivityUtils;
+
+ import android.accounts.Account;
+@@ -28,7 +22,6 @@
+ import android.view.View;
+ import android.view.View.OnClickListener;
+ import android.widget.TextView;
+-import android.widget.Toast;
+
+ /**
+ * Activity which displays account created successfully screen to the user, and
+@@ -164,76 +157,8 @@
+ resendLink.setClickable(resendLinkShouldBeEnabled);
+ }
+
+- public static class FxAccountResendCodeTask extends FxAccountSetupTask<Void> {
+- protected static final String LOG_TAG = FxAccountResendCodeTask.class.getSimpleName();
+-
+- protected final byte[] sessionToken;
+-
+- public FxAccountResendCodeTask(Context context, byte[] sessionToken, FxAccountClient client, RequestDelegate<Void> delegate) {
+- super(context, null, client, delegate);
+- this.sessionToken = sessionToken;
+- }
+-
+- @Override
+- protected InnerRequestDelegate<Void> doInBackground(Void... arg0) {
+- try {
+- client.resendCode(sessionToken, innerDelegate);
+- latch.await();
+- return innerDelegate;
+- } catch (Exception e) {
+- Logger.error(LOG_TAG, "Got exception signing in.", e);
+- delegate.handleError(e);
+- }
+- return null;
+- }
+- }
+-
+- protected static class ResendCodeDelegate implements RequestDelegate<Void> {
+- public final Context context;
+-
+- public ResendCodeDelegate(Context context) {
+- this.context = context;
+- }
+-
+- @Override
+- public void handleError(Exception e) {
+- Logger.warn(LOG_TAG, "Got exception requesting fresh confirmation link; ignoring.", e);
+- Toast.makeText(context, R.string.fxaccount_confirm_account_verification_link_not_sent, Toast.LENGTH_LONG).show();
+- }
+-
+- @Override
+- public void handleFailure(FxAccountClientRemoteException e) {
+- handleError(e);
+- }
+-
+- @Override
+- public void handleSuccess(Void result) {
+- Toast.makeText(context, R.string.fxaccount_confirm_account_verification_link_sent, Toast.LENGTH_SHORT).show();
+- }
+- }
+-
+- public static void resendCode(Context context, AndroidFxAccount fxAccount) {
+- RequestDelegate<Void> delegate = new ResendCodeDelegate(context);
+-
+- byte[] sessionToken;
+- try {
+- sessionToken = ((Engaged) fxAccount.getState()).getSessionToken();
+- } catch (Exception e) {
+- delegate.handleError(e);
+- return;
+- }
+- if (sessionToken == null) {
+- delegate.handleError(new IllegalStateException("sessionToken should not be null"));
+- return;
+- }
+-
+- Executor executor = Executors.newSingleThreadExecutor();
+- FxAccountClient client = new FxAccountClient20(fxAccount.getAccountServerURI(), executor);
+- new FxAccountResendCodeTask(context, sessionToken, client, delegate).execute();
+- }
+-
+ @Override
+ public void onClick(View v) {
+- resendCode(this, fxAccount);
++ FxAccountCodeResender.resendCode(this, fxAccount);
+ }
+ }
+diff -ruN mozilla-esr31/mobile/android/base/fxa/activities/FxAccountCreateAccountActivity.java mozilla-release/mobile/android/base/fxa/activities/FxAccountCreateAccountActivity.java
+--- mozilla-esr31/mobile/android/base/fxa/activities/FxAccountCreateAccountActivity.java 2015-02-25 19:21:06.000000000 +0100
++++ mozilla-release/mobile/android/base/fxa/activities/FxAccountCreateAccountActivity.java 2014-09-24 03:05:32.000000000 +0200
+@@ -21,7 +21,7 @@
+ import org.mozilla.gecko.background.fxa.FxAccountClientException.FxAccountClientRemoteException;
+ import org.mozilla.gecko.background.fxa.PasswordStretcher;
+ import org.mozilla.gecko.fxa.FxAccountConstants;
+-import org.mozilla.gecko.fxa.activities.FxAccountSetupTask.FxAccountCreateAccountTask;
++import org.mozilla.gecko.fxa.tasks.FxAccountCreateAccountTask;
+
+ import android.app.AlertDialog;
+ import android.app.Dialog;
+diff -ruN mozilla-esr31/mobile/android/base/fxa/activities/FxAccountGetStartedActivity.java mozilla-release/mobile/android/base/fxa/activities/FxAccountGetStartedActivity.java
+--- mozilla-esr31/mobile/android/base/fxa/activities/FxAccountGetStartedActivity.java 2015-02-25 19:21:06.000000000 +0100
++++ mozilla-release/mobile/android/base/fxa/activities/FxAccountGetStartedActivity.java 2014-09-24 03:05:32.000000000 +0200
+@@ -6,13 +6,11 @@
+
+ import java.util.Locale;
+
+-import org.mozilla.gecko.AppConstants;
+ import org.mozilla.gecko.R;
+ import org.mozilla.gecko.background.common.log.Logger;
+ import org.mozilla.gecko.background.fxa.FxAccountAgeLockoutHelper;
+ import org.mozilla.gecko.fxa.FirefoxAccounts;
+ import org.mozilla.gecko.fxa.FxAccountConstants;
+-import org.mozilla.gecko.sync.Utils;
+ import org.mozilla.gecko.sync.setup.activities.ActivityUtils;
+ import org.mozilla.gecko.sync.setup.activities.LocaleAware;
+
+@@ -109,11 +107,7 @@
+ protected void linkifyOldFirefoxLink() {
+ TextView oldFirefox = (TextView) findViewById(R.id.old_firefox);
+ String text = getResources().getString(R.string.fxaccount_getting_started_old_firefox);
+- String VERSION = AppConstants.MOZ_APP_VERSION;
+- String OS = AppConstants.OS_TARGET;
+-
+- String LOCALE = Utils.getLanguageTag(Locale.getDefault());
+- String url = getResources().getString(R.string.fxaccount_link_old_firefox, VERSION, OS, LOCALE);
++ final String url = FirefoxAccounts.getOldSyncUpgradeURL(getResources(), Locale.getDefault());
+ FxAccountConstants.pii(LOG_TAG, "Old Firefox url is: " + url); // Don't want to leak locale in particular.
+ ActivityUtils.linkTextView(oldFirefox, text, url);
+ }
+diff -ruN mozilla-esr31/mobile/android/base/fxa/activities/FxAccountSetupTask.java mozilla-release/mobile/android/base/fxa/activities/FxAccountSetupTask.java
+--- mozilla-esr31/mobile/android/base/fxa/activities/FxAccountSetupTask.java 2015-02-25 19:21:06.000000000 +0100
++++ mozilla-release/mobile/android/base/fxa/activities/FxAccountSetupTask.java 1970-01-01 01:00:00.000000000 +0100
+@@ -1,172 +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.activities;
+-
+-import java.io.UnsupportedEncodingException;
+-import java.util.concurrent.CountDownLatch;
+-
+-import org.mozilla.gecko.background.common.log.Logger;
+-import org.mozilla.gecko.background.fxa.FxAccountClient;
+-import org.mozilla.gecko.background.fxa.FxAccountClient10.RequestDelegate;
+-import org.mozilla.gecko.background.fxa.FxAccountClient20.LoginResponse;
+-import org.mozilla.gecko.background.fxa.FxAccountClientException.FxAccountClientRemoteException;
+-import org.mozilla.gecko.background.fxa.PasswordStretcher;
+-import org.mozilla.gecko.fxa.activities.FxAccountSetupTask.InnerRequestDelegate;
+-
+-import android.content.Context;
+-import android.os.AsyncTask;
+-
+-/**
+- * An <code>AsyncTask</code> wrapper around signing up for, and signing in to, a
+- * Firefox Account.
+- * <p>
+- * It's strange to add explicit blocking to callback-threading code, but we do
+- * it here to take advantage of Android's built in support for background work.
+- * We really want to avoid making a threading mistake that brings down the whole
+- * process.
+- */
+-abstract class FxAccountSetupTask<T> extends AsyncTask<Void, Void, InnerRequestDelegate<T>> {
+- private static final String LOG_TAG = FxAccountSetupTask.class.getSimpleName();
+-
+- public interface ProgressDisplay {
+- public void showProgress();
+- public void dismissProgress();
+- }
+-
+- protected final Context context;
+- protected final FxAccountClient client;
+- protected final ProgressDisplay progressDisplay;
+-
+- // Initialized lazily.
+- protected byte[] quickStretchedPW;
+-
+- // AsyncTask's are one-time-use, so final members are fine.
+- protected final CountDownLatch latch = new CountDownLatch(1);
+- protected final InnerRequestDelegate<T> innerDelegate = new InnerRequestDelegate<T>(latch);
+-
+- protected final RequestDelegate<T> delegate;
+-
+- public FxAccountSetupTask(Context context, ProgressDisplay progressDisplay, FxAccountClient client, RequestDelegate<T> delegate) {
+- this.context = context;
+- this.client = client;
+- this.delegate = delegate;
+- this.progressDisplay = progressDisplay;
+- }
+-
+- @Override
+- protected void onPreExecute() {
+- if (progressDisplay != null) {
+- progressDisplay.showProgress();
+- }
+- }
+-
+- @Override
+- protected void onPostExecute(InnerRequestDelegate<T> result) {
+- if (progressDisplay != null) {
+- progressDisplay.dismissProgress();
+- }
+-
+- // We are on the UI thread, and need to invoke these callbacks here to allow UI updating.
+- if (innerDelegate.failure != null) {
+- delegate.handleFailure(innerDelegate.failure);
+- } else if (innerDelegate.exception != null) {
+- delegate.handleError(innerDelegate.exception);
+- } else {
+- delegate.handleSuccess(result.response);
+- }
+- }
+-
+- @Override
+- protected void onCancelled(InnerRequestDelegate<T> result) {
+- if (progressDisplay != null) {
+- progressDisplay.dismissProgress();
+- }
+- delegate.handleError(new IllegalStateException("Task was cancelled."));
+- }
+-
+- protected static class InnerRequestDelegate<T> implements RequestDelegate<T> {
+- protected final CountDownLatch latch;
+- public T response = null;
+- public Exception exception = null;
+- public FxAccountClientRemoteException failure = null;
+-
+- protected InnerRequestDelegate(CountDownLatch latch) {
+- this.latch = latch;
+- }
+-
+- @Override
+- public void handleError(Exception e) {
+- Logger.error(LOG_TAG, "Got exception.");
+- this.exception = e;
+- latch.countDown();
+- }
+-
+- @Override
+- public void handleFailure(FxAccountClientRemoteException e) {
+- Logger.warn(LOG_TAG, "Got failure.");
+- this.failure = e;
+- latch.countDown();
+- }
+-
+- @Override
+- public void handleSuccess(T result) {
+- Logger.info(LOG_TAG, "Got success.");
+- this.response = result;
+- latch.countDown();
+- }
+- }
+-
+- public static class FxAccountCreateAccountTask extends FxAccountSetupTask<LoginResponse> {
+- private static final String LOG_TAG = FxAccountCreateAccountTask.class.getSimpleName();
+-
+- protected final byte[] emailUTF8;
+- protected final PasswordStretcher passwordStretcher;
+-
+- public FxAccountCreateAccountTask(Context context, ProgressDisplay progressDisplay, String email, PasswordStretcher passwordStretcher, FxAccountClient client, RequestDelegate<LoginResponse> delegate) throws UnsupportedEncodingException {
+- super(context, progressDisplay, client, delegate);
+- this.emailUTF8 = email.getBytes("UTF-8");
+- this.passwordStretcher = passwordStretcher;
+- }
+-
+- @Override
+- protected InnerRequestDelegate<LoginResponse> doInBackground(Void... arg0) {
+- try {
+- client.createAccountAndGetKeys(emailUTF8, passwordStretcher, innerDelegate);
+- latch.await();
+- return innerDelegate;
+- } catch (Exception e) {
+- Logger.error(LOG_TAG, "Got exception logging in.", e);
+- delegate.handleError(e);
+- }
+- return null;
+- }
+- }
+-
+- public static class FxAccountSignInTask extends FxAccountSetupTask<LoginResponse> {
+- protected static final String LOG_TAG = FxAccountSignInTask.class.getSimpleName();
+-
+- protected final byte[] emailUTF8;
+- protected final PasswordStretcher passwordStretcher;
+-
+- public FxAccountSignInTask(Context context, ProgressDisplay progressDisplay, String email, PasswordStretcher passwordStretcher, FxAccountClient client, RequestDelegate<LoginResponse> delegate) throws UnsupportedEncodingException {
+- super(context, progressDisplay, client, delegate);
+- this.emailUTF8 = email.getBytes("UTF-8");
+- this.passwordStretcher = passwordStretcher;
+- }
+-
+- @Override
+- protected InnerRequestDelegate<LoginResponse> doInBackground(Void... arg0) {
+- try {
+- client.loginAndGetKeys(emailUTF8, passwordStretcher, innerDelegate);
+- latch.await();
+- return innerDelegate;
+- } catch (Exception e) {
+- Logger.error(LOG_TAG, "Got exception signing in.", e);
+- delegate.handleError(e);
+- }
+- return null;
+- }
+- }
+-}
+diff -ruN mozilla-esr31/mobile/android/base/fxa/activities/FxAccountSignInActivity.java mozilla-release/mobile/android/base/fxa/activities/FxAccountSignInActivity.java
+--- mozilla-esr31/mobile/android/base/fxa/activities/FxAccountSignInActivity.java 2015-02-25 19:21:06.000000000 +0100
++++ mozilla-release/mobile/android/base/fxa/activities/FxAccountSignInActivity.java 2014-09-24 03:05:32.000000000 +0200
+@@ -16,7 +16,7 @@
+ import org.mozilla.gecko.background.fxa.FxAccountClientException.FxAccountClientRemoteException;
+ import org.mozilla.gecko.background.fxa.PasswordStretcher;
+ import org.mozilla.gecko.fxa.FxAccountConstants;
+-import org.mozilla.gecko.fxa.activities.FxAccountSetupTask.FxAccountSignInTask;
++import org.mozilla.gecko.fxa.tasks.FxAccountSignInTask;
+ import org.mozilla.gecko.sync.setup.activities.ActivityUtils;
+
+ import android.content.Intent;
+diff -ruN mozilla-esr31/mobile/android/base/fxa/activities/FxAccountStatusFragment.java mozilla-release/mobile/android/base/fxa/activities/FxAccountStatusFragment.java
+--- mozilla-esr31/mobile/android/base/fxa/activities/FxAccountStatusFragment.java 2015-02-25 19:21:06.000000000 +0100
++++ mozilla-release/mobile/android/base/fxa/activities/FxAccountStatusFragment.java 2014-09-24 03:05:32.000000000 +0200
+@@ -17,6 +17,8 @@
+ 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.sync.SharedPreferencesClientsDataDelegate;
+ import org.mozilla.gecko.sync.SyncConfiguration;
+
+ import android.accounts.Account;
+@@ -27,10 +29,13 @@
+ 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;
+
+ /**
+ * A fragment that displays the status of an AndroidFxAccount.
+@@ -38,7 +43,9 @@
+ * The owning activity is responsible for providing an AndroidFxAccount at
+ * appropriate times.
+ */
+-public class FxAccountStatusFragment extends PreferenceFragment implements OnPreferenceClickListener {
++public class FxAccountStatusFragment
++ extends PreferenceFragment
++ implements OnPreferenceClickListener, OnPreferenceChangeListener {
+ private static final String LOG_TAG = FxAccountStatusFragment.class.getSimpleName();
+
+ // When a checkbox is toggled, wait 5 seconds (for other checkbox actions)
+@@ -64,7 +71,12 @@
+ protected CheckBoxPreference tabsPreference;
+ protected CheckBoxPreference passwordsPreference;
+
++ protected EditTextPreference deviceNamePreference;
++
+ protected volatile AndroidFxAccount fxAccount;
++ // The contract is: when fxAccount is non-null, then clientsDataDelegate is
++ // non-null. If violated then an IllegalStateException is thrown.
++ protected volatile SharedPreferencesClientsDataDelegate clientsDataDelegate;
+
+ // Used to post delayed sync requests.
+ protected Handler handler;
+@@ -87,6 +99,10 @@
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
++ addPreferences();
++ }
++
++ protected void addPreferences() {
+ addPreferencesFromResource(R.xml.fxaccount_status_prefscreen);
+
+ emailPreference = ensureFindPreference("email");
+@@ -118,6 +134,9 @@
+ historyPreference.setOnPreferenceClickListener(this);
+ tabsPreference.setOnPreferenceClickListener(this);
+ passwordsPreference.setOnPreferenceClickListener(this);
++
++ deviceNamePreference = (EditTextPreference) ensureFindPreference("device_name");
++ deviceNamePreference.setOnPreferenceChangeListener(this);
+ }
+
+ /**
+@@ -142,7 +161,7 @@
+ }
+
+ if (preference == needsVerificationPreference) {
+- FxAccountConfirmAccountActivity.resendCode(getActivity().getApplicationContext(), fxAccount);
++ FxAccountCodeResender.resendCode(getActivity().getApplicationContext(), fxAccount);
+
+ Intent intent = new Intent(getActivity(), FxAccountConfirmAccountActivity.class);
+ // Per http://stackoverflow.com/a/8992365, this triggers a known bug with
+@@ -176,6 +195,8 @@
+ historyPreference.setEnabled(enabled);
+ tabsPreference.setEnabled(enabled);
+ passwordsPreference.setEnabled(enabled);
++ // Since we can't sync, we can't update our remote client record.
++ deviceNamePreference.setEnabled(enabled);
+ }
+
+ /**
+@@ -293,6 +314,14 @@
+ throw new IllegalArgumentException("fxAccount must not be null");
+ }
+ this.fxAccount = fxAccount;
++ try {
++ this.clientsDataDelegate = new SharedPreferencesClientsDataDelegate(fxAccount.getSyncPrefs());
++ } catch (Exception e) {
++ Logger.error(LOG_TAG, "Got exception fetching Sync prefs associated to Firefox Account; aborting.", e);
++ // Something is terribly wrong; best to get a stack trace rather than
++ // continue with a null clients delegate.
++ throw new IllegalStateException(e);
++ }
+
+ handler = new Handler(); // Attached to current (assumed to be UI) thread.
+
+@@ -318,6 +347,17 @@
+ FxAccountSyncStatusHelper.getInstance().stopObserving(syncStatusDelegate);
+ }
+
++ protected void hardRefresh() {
++ // This is the only way to guarantee that the EditText dialogs created by
++ // EditTextPreferences are re-created. This works around the issue described
++ // at http://androiddev.orkitra.com/?p=112079.
++ final PreferenceScreen statusScreen = (PreferenceScreen) ensureFindPreference("status_screen");
++ statusScreen.removeAll();
++ addPreferences();
++
++ refresh();
++ }
++
+ protected void refresh() {
+ // refresh is called from our onResume, which can happen before the owning
+ // Activity tells us about an account (via our public
+@@ -371,6 +411,10 @@
+ // No matter our state, we should update the checkboxes.
+ updateSelectedEngines();
+ }
++
++ final String clientName = clientsDataDelegate.getClientName();
++ deviceNamePreference.setSummary(clientName);
++ deviceNamePreference.setText(clientName);
+ }
+
+ /**
+@@ -570,4 +614,22 @@
+ button.setOnPreferenceClickListener(listener);
+ }
+ }
++
++ @Override
++ public boolean onPreferenceChange(Preference preference, Object newValue) {
++ if (preference == deviceNamePreference) {
++ String newClientName = (String) newValue;
++ if (TextUtils.isEmpty(newClientName)) {
++ newClientName = clientsDataDelegate.getDefaultClientName();
++ }
++ final long now = System.currentTimeMillis();
++ clientsDataDelegate.setClientName(newClientName, now);
++ requestDelayedSync(); // Try to update our remote client record.
++ hardRefresh(); // Updates the value displayed to the user, among other things.
++ return true;
++ }
++
++ // For everything else, accept the change.
++ return true;
++ }
+ }
+diff -ruN mozilla-esr31/mobile/android/base/fxa/activities/FxAccountUpdateCredentialsActivity.java mozilla-release/mobile/android/base/fxa/activities/FxAccountUpdateCredentialsActivity.java
+--- mozilla-esr31/mobile/android/base/fxa/activities/FxAccountUpdateCredentialsActivity.java 2015-02-25 19:21:06.000000000 +0100
++++ mozilla-release/mobile/android/base/fxa/activities/FxAccountUpdateCredentialsActivity.java 2014-09-24 03:05:32.000000000 +0200
+@@ -18,11 +18,11 @@
+ import org.mozilla.gecko.background.fxa.PasswordStretcher;
+ import org.mozilla.gecko.fxa.FirefoxAccounts;
+ import org.mozilla.gecko.fxa.FxAccountConstants;
+-import org.mozilla.gecko.fxa.activities.FxAccountSetupTask.FxAccountSignInTask;
+ import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
+ import org.mozilla.gecko.fxa.login.Engaged;
+ import org.mozilla.gecko.fxa.login.State;
+ import org.mozilla.gecko.fxa.login.State.StateLabel;
++import org.mozilla.gecko.fxa.tasks.FxAccountSignInTask;
+ import org.mozilla.gecko.sync.setup.activities.ActivityUtils;
+
+ import android.os.Bundle;
+diff -ruN mozilla-esr31/mobile/android/base/fxa/sync/FxAccountSyncAdapter.java mozilla-release/mobile/android/base/fxa/sync/FxAccountSyncAdapter.java
+--- mozilla-esr31/mobile/android/base/fxa/sync/FxAccountSyncAdapter.java 2015-02-25 19:21:06.000000000 +0100
++++ mozilla-release/mobile/android/base/fxa/sync/FxAccountSyncAdapter.java 2014-09-24 03:05:33.000000000 +0200
+@@ -357,7 +357,11 @@
+
+ FxAccountGlobalSession globalSession = null;
+ try {
+- ClientsDataDelegate clientsDataDelegate = new SharedPreferencesClientsDataDelegate(sharedPrefs);
++ final ClientsDataDelegate clientsDataDelegate = new SharedPreferencesClientsDataDelegate(sharedPrefs);
++ if (FxAccountConstants.LOG_PERSONAL_INFORMATION) {
++ FxAccountConstants.pii(LOG_TAG, "Client device name is: '" + clientsDataDelegate.getClientName() + "'.");
++ FxAccountConstants.pii(LOG_TAG, "Client device data last modified: " + clientsDataDelegate.getLastModifiedTimestamp());
++ }
+
+ // We compute skew over time using SkewHandler. This yields an unchanging
+ // skew adjustment that the HawkAuthHeaderProvider uses to adjust its
+diff -ruN mozilla-esr31/mobile/android/base/fxa/tasks/FxAccountCodeResender.java mozilla-release/mobile/android/base/fxa/tasks/FxAccountCodeResender.java
+--- mozilla-esr31/mobile/android/base/fxa/tasks/FxAccountCodeResender.java 1970-01-01 01:00:00.000000000 +0100
++++ mozilla-release/mobile/android/base/fxa/tasks/FxAccountCodeResender.java 2014-09-24 03:05:33.000000000 +0200
+@@ -0,0 +1,108 @@
++/* 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.tasks;
++
++import java.util.concurrent.Executor;
++import java.util.concurrent.Executors;
++
++import org.mozilla.gecko.R;
++import org.mozilla.gecko.background.common.log.Logger;
++import org.mozilla.gecko.background.fxa.FxAccountClient;
++import org.mozilla.gecko.background.fxa.FxAccountClient10.RequestDelegate;
++import org.mozilla.gecko.background.fxa.FxAccountClient20;
++import org.mozilla.gecko.background.fxa.FxAccountClientException.FxAccountClientRemoteException;
++import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
++import org.mozilla.gecko.fxa.login.Engaged;
++
++import android.content.Context;
++import android.widget.Toast;
++
++/**
++ * A helper class that provides a simple interface for requesting
++ * a Firefox Account verification email to be resent.
++ */
++public class FxAccountCodeResender {
++ private static final String LOG_TAG = FxAccountCodeResender.class.getSimpleName();
++
++ private static class FxAccountResendCodeTask extends FxAccountSetupTask<Void> {
++ protected static final String LOG_TAG = FxAccountResendCodeTask.class.getSimpleName();
++
++ protected final byte[] sessionToken;
++
++ public FxAccountResendCodeTask(Context context, byte[] sessionToken, FxAccountClient client, RequestDelegate<Void> delegate) {
++ super(context, null, client, delegate);
++ this.sessionToken = sessionToken;
++ }
++
++ @Override
++ protected InnerRequestDelegate<Void> doInBackground(Void... arg0) {
++ try {
++ client.resendCode(sessionToken, innerDelegate);
++ latch.await();
++ return innerDelegate;
++ } catch (Exception e) {
++ Logger.error(LOG_TAG, "Got exception signing in.", e);
++ delegate.handleError(e);
++ }
++ return null;
++ }
++ }
++
++ private static class ResendCodeDelegate implements RequestDelegate<Void> {
++ public final Context context;
++
++ public ResendCodeDelegate(Context context) {
++ this.context = context;
++ }
++
++ @Override
++ public void handleError(Exception e) {
++ Logger.warn(LOG_TAG, "Got exception requesting fresh confirmation link; ignoring.", e);
++ Toast.makeText(context, R.string.fxaccount_confirm_account_verification_link_not_sent, Toast.LENGTH_LONG).show();
++ }
++
++ @Override
++ public void handleFailure(FxAccountClientRemoteException e) {
++ handleError(e);
++ }
++
++ @Override
++ public void handleSuccess(Void result) {
++ Toast.makeText(context, R.string.fxaccount_confirm_account_verification_link_sent, Toast.LENGTH_SHORT).show();
++ }
++ }
++
++ /**
++ * Resends the account verification email, and displays an appropriate
++ * toast on both send success and failure. Note that because the underlying implementation
++ * uses {@link AsyncTask}, the provided context must be UI-capable and
++ * this method called from the UI thread.
++ *
++ * Note that it may actually be possible to run this (and the {@link AsyncTask}) method
++ * from a background thread - but this hasn't been tested.
++ *
++ * @param context A UI-capable Android context.
++ * @param fxAccount The Firefox Account to resend the code to.
++ */
++ public static void resendCode(Context context, AndroidFxAccount fxAccount) {
++ RequestDelegate<Void> delegate = new ResendCodeDelegate(context);
++
++ byte[] sessionToken;
++ try {
++ sessionToken = ((Engaged) fxAccount.getState()).getSessionToken();
++ } catch (Exception e) {
++ delegate.handleError(e);
++ return;
++ }
++ if (sessionToken == null) {
++ delegate.handleError(new IllegalStateException("sessionToken should not be null"));
++ return;
++ }
++
++ Executor executor = Executors.newSingleThreadExecutor();
++ FxAccountClient client = new FxAccountClient20(fxAccount.getAccountServerURI(), executor);
++ new FxAccountResendCodeTask(context, sessionToken, client, delegate).execute();
++ }
++}
+diff -ruN mozilla-esr31/mobile/android/base/fxa/tasks/FxAccountCreateAccountTask.java mozilla-release/mobile/android/base/fxa/tasks/FxAccountCreateAccountTask.java
+--- mozilla-esr31/mobile/android/base/fxa/tasks/FxAccountCreateAccountTask.java 1970-01-01 01:00:00.000000000 +0100
++++ mozilla-release/mobile/android/base/fxa/tasks/FxAccountCreateAccountTask.java 2014-09-24 03:05:33.000000000 +0200
+@@ -0,0 +1,41 @@
++/* 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.tasks;
++
++import java.io.UnsupportedEncodingException;
++
++import org.mozilla.gecko.background.common.log.Logger;
++import org.mozilla.gecko.background.fxa.FxAccountClient;
++import org.mozilla.gecko.background.fxa.FxAccountClient10.RequestDelegate;
++import org.mozilla.gecko.background.fxa.FxAccountClient20.LoginResponse;
++import org.mozilla.gecko.background.fxa.PasswordStretcher;
++
++import android.content.Context;
++
++public class FxAccountCreateAccountTask extends FxAccountSetupTask<LoginResponse> {
++ private static final String LOG_TAG = FxAccountCreateAccountTask.class.getSimpleName();
++
++ protected final byte[] emailUTF8;
++ protected final PasswordStretcher passwordStretcher;
++
++ public FxAccountCreateAccountTask(Context context, ProgressDisplay progressDisplay, String email, PasswordStretcher passwordStretcher, FxAccountClient client, RequestDelegate<LoginResponse> delegate) throws UnsupportedEncodingException {
++ super(context, progressDisplay, client, delegate);
++ this.emailUTF8 = email.getBytes("UTF-8");
++ this.passwordStretcher = passwordStretcher;
++ }
++
++ @Override
++ protected InnerRequestDelegate<LoginResponse> doInBackground(Void... arg0) {
++ try {
++ client.createAccountAndGetKeys(emailUTF8, passwordStretcher, innerDelegate);
++ latch.await();
++ return innerDelegate;
++ } catch (Exception e) {
++ Logger.error(LOG_TAG, "Got exception logging in.", e);
++ delegate.handleError(e);
++ }
++ return null;
++ }
++}
+diff -ruN mozilla-esr31/mobile/android/base/fxa/tasks/FxAccountSetupTask.java mozilla-release/mobile/android/base/fxa/tasks/FxAccountSetupTask.java
+--- mozilla-esr31/mobile/android/base/fxa/tasks/FxAccountSetupTask.java 1970-01-01 01:00:00.000000000 +0100
++++ mozilla-release/mobile/android/base/fxa/tasks/FxAccountSetupTask.java 2014-09-24 03:05:33.000000000 +0200
+@@ -0,0 +1,117 @@
++/* 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.tasks;
++
++import java.util.concurrent.CountDownLatch;
++
++import org.mozilla.gecko.background.common.log.Logger;
++import org.mozilla.gecko.background.fxa.FxAccountClient;
++import org.mozilla.gecko.background.fxa.FxAccountClient10.RequestDelegate;
++import org.mozilla.gecko.background.fxa.FxAccountClientException.FxAccountClientRemoteException;
++import org.mozilla.gecko.fxa.tasks.FxAccountSetupTask.InnerRequestDelegate;
++
++import android.content.Context;
++import android.os.AsyncTask;
++
++/**
++ * An <code>AsyncTask</code> wrapper around signing up for, and signing in to, a
++ * Firefox Account.
++ * <p>
++ * It's strange to add explicit blocking to callback-threading code, but we do
++ * it here to take advantage of Android's built in support for background work.
++ * We really want to avoid making a threading mistake that brings down the whole
++ * process.
++ */
++public abstract class FxAccountSetupTask<T> extends AsyncTask<Void, Void, InnerRequestDelegate<T>> {
++ private static final String LOG_TAG = FxAccountSetupTask.class.getSimpleName();
++
++ public interface ProgressDisplay {
++ public void showProgress();
++ public void dismissProgress();
++ }
++
++ protected final Context context;
++ protected final FxAccountClient client;
++ protected final ProgressDisplay progressDisplay;
++
++ // Initialized lazily.
++ protected byte[] quickStretchedPW;
++
++ // AsyncTask's are one-time-use, so final members are fine.
++ protected final CountDownLatch latch = new CountDownLatch(1);
++ protected final InnerRequestDelegate<T> innerDelegate = new InnerRequestDelegate<T>(latch);
++
++ protected final RequestDelegate<T> delegate;
++
++ public FxAccountSetupTask(Context context, ProgressDisplay progressDisplay, FxAccountClient client, RequestDelegate<T> delegate) {
++ this.context = context;
++ this.client = client;
++ this.delegate = delegate;
++ this.progressDisplay = progressDisplay;
++ }
++
++ @Override
++ protected void onPreExecute() {
++ if (progressDisplay != null) {
++ progressDisplay.showProgress();
++ }
++ }
++
++ @Override
++ protected void onPostExecute(InnerRequestDelegate<T> result) {
++ if (progressDisplay != null) {
++ progressDisplay.dismissProgress();
++ }
++
++ // We are on the UI thread, and need to invoke these callbacks here to allow UI updating.
++ if (innerDelegate.failure != null) {
++ delegate.handleFailure(innerDelegate.failure);
++ } else if (innerDelegate.exception != null) {
++ delegate.handleError(innerDelegate.exception);
++ } else {
++ delegate.handleSuccess(result.response);
++ }
++ }
++
++ @Override
++ protected void onCancelled(InnerRequestDelegate<T> result) {
++ if (progressDisplay != null) {
++ progressDisplay.dismissProgress();
++ }
++ delegate.handleError(new IllegalStateException("Task was cancelled."));
++ }
++
++ protected static class InnerRequestDelegate<T> implements RequestDelegate<T> {
++ protected final CountDownLatch latch;
++ public T response = null;
++ public Exception exception = null;
++ public FxAccountClientRemoteException failure = null;
++
++ protected InnerRequestDelegate(CountDownLatch latch) {
++ this.latch = latch;
++ }
++
++ @Override
++ public void handleError(Exception e) {
++ Logger.error(LOG_TAG, "Got exception.");
++ this.exception = e;
++ latch.countDown();
++ }
++
++ @Override
++ public void handleFailure(FxAccountClientRemoteException e) {
++ Logger.warn(LOG_TAG, "Got failure.");
++ this.failure = e;
++ latch.countDown();
++ }
++
++ @Override
++ public void handleSuccess(T result) {
++ Logger.info(LOG_TAG, "Got success.");
++ this.response = result;
++ latch.countDown();
++ }
++ }
++}
+diff -ruN mozilla-esr31/mobile/android/base/fxa/tasks/FxAccountSignInTask.java mozilla-release/mobile/android/base/fxa/tasks/FxAccountSignInTask.java
+--- mozilla-esr31/mobile/android/base/fxa/tasks/FxAccountSignInTask.java 1970-01-01 01:00:00.000000000 +0100
++++ mozilla-release/mobile/android/base/fxa/tasks/FxAccountSignInTask.java 2014-09-24 03:05:33.000000000 +0200
+@@ -0,0 +1,41 @@
++/* 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.tasks;
++
++import java.io.UnsupportedEncodingException;
++
++import org.mozilla.gecko.background.common.log.Logger;
++import org.mozilla.gecko.background.fxa.FxAccountClient;
++import org.mozilla.gecko.background.fxa.FxAccountClient10.RequestDelegate;
++import org.mozilla.gecko.background.fxa.FxAccountClient20.LoginResponse;
++import org.mozilla.gecko.background.fxa.PasswordStretcher;
++
++import android.content.Context;
++
++public class FxAccountSignInTask extends FxAccountSetupTask<LoginResponse> {
++ protected static final String LOG_TAG = FxAccountSignInTask.class.getSimpleName();
++
++ protected final byte[] emailUTF8;
++ protected final PasswordStretcher passwordStretcher;
++
++ public FxAccountSignInTask(Context context, ProgressDisplay progressDisplay, String email, PasswordStretcher passwordStretcher, FxAccountClient client, RequestDelegate<LoginResponse> delegate) throws UnsupportedEncodingException {
++ super(context, progressDisplay, client, delegate);
++ this.emailUTF8 = email.getBytes("UTF-8");
++ this.passwordStretcher = passwordStretcher;
++ }
++
++ @Override
++ protected InnerRequestDelegate<LoginResponse> doInBackground(Void... arg0) {
++ try {
++ client.loginAndGetKeys(emailUTF8, passwordStretcher, innerDelegate);
++ latch.await();
++ return innerDelegate;
++ } catch (Exception e) {
++ Logger.error(LOG_TAG, "Got exception signing in.", e);
++ delegate.handleError(e);
++ }
++ return null;
++ }
++}
+--- mozilla-esr31/mobile/android/base/android-services.mozbuild 2015-02-25 19:21:05.000000000 +0100
++++ mozilla-release/mobile/android/base/android-services.mozbuild 2014-09-24 03:05:32.000000000 +0200
+@@ -555,7 +557,6 @@
+ 'fxa/activities/FxAccountCreateAccountActivity.java',
+ 'fxa/activities/FxAccountCreateAccountNotAllowedActivity.java',
+ 'fxa/activities/FxAccountGetStartedActivity.java',
+- 'fxa/activities/FxAccountSetupTask.java',
+ 'fxa/activities/FxAccountSignInActivity.java',
+ 'fxa/activities/FxAccountStatusActivity.java',
+ 'fxa/activities/FxAccountStatusFragment.java',
+@@ -589,6 +590,10 @@
+ 'fxa/sync/FxAccountSyncService.java',
+ 'fxa/sync/FxAccountSyncStatusHelper.java',
+ 'fxa/sync/SchedulePolicy.java',
++ 'fxa/tasks/FxAccountCodeResender.java',
++ 'fxa/tasks/FxAccountCreateAccountTask.java',
++ 'fxa/tasks/FxAccountSetupTask.java',
++ 'fxa/tasks/FxAccountSignInTask.java',
+ 'sync/AlreadySyncingException.java',
+ 'sync/BackoffHandler.java',
+ 'sync/BadRequiredFieldJSONException.java',