summaryrefslogtreecommitdiff
path: root/data/patches
diff options
context:
space:
mode:
Diffstat (limited to 'data/patches')
-rw-r--r--data/patches/0001-Bug-1013064-part-1-prompt-for-master-password-unlock.patch60
-rw-r--r--data/patches/0002-Bug-1013064-part-2-Store-sensitive-FxA-credentials-i.patch551
-rw-r--r--data/patches/0003-Bug-1013064-part-3-only-migrate-data-into-the-loginm.patch194
-rw-r--r--data/patches/0004-Bug-1013064-part-4b-browserid_identity-and-sync-chang.patch286
-rw-r--r--data/patches/0005-Bug-1013064-part-5-stop-disabling-the-password-engin.patch519
-rw-r--r--data/patches/disable/0001-upgrade-mobile-fxa-to-v32.patch992
-rw-r--r--data/patches/disable/0002-update-mobile-sqlite-to-v35.patch246
-rw-r--r--data/patches/disable/0003-upgrade-mobile-sync-to-v32.patch992
8 files changed, 0 insertions, 3840 deletions
diff --git a/data/patches/0001-Bug-1013064-part-1-prompt-for-master-password-unlock.patch b/data/patches/0001-Bug-1013064-part-1-prompt-for-master-password-unlock.patch
deleted file mode 100644
index 1880604..0000000
--- a/data/patches/0001-Bug-1013064-part-1-prompt-for-master-password-unlock.patch
+++ /dev/null
@@ -1,60 +0,0 @@
-From 18f9d132020afee6004e63c9006245e4d3b04e18 Mon Sep 17 00:00:00 2001
-From: Mark Hammond <mhammond@skippinet.com.au>
-Date: Wed, 11 Jun 2014 17:34:24 +1000
-Subject: Bug 1013064 (part 1) - prompt for master-password unlock when
- interacting with about:accounts. r=ttaubert
-
----
- browser/base/content/aboutaccounts/aboutaccounts.js | 9 +++++++++
- 1 file changed, 9 insertions(+)
-
-diff --git a/browser/base/content/aboutaccounts/aboutaccounts.js b/browser/base/content/aboutaccounts/aboutaccounts.js
-index 5cceae15..af31be9 100644
---- a/browser/base/content/aboutaccounts/aboutaccounts.js
-+++ b/browser/base/content/aboutaccounts/aboutaccounts.js
-@@ -7,16 +7,19 @@
- const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
-
- Cu.import("resource://gre/modules/Services.jsm");
- Cu.import("resource://gre/modules/FxAccounts.jsm");
-
- let fxAccountsCommon = {};
- Cu.import("resource://gre/modules/FxAccountsCommon.js", fxAccountsCommon);
-
-+// for master-password utilities
-+Cu.import("resource://services-sync/util.js");
-+
- const PREF_LAST_FXA_USER = "identity.fxaccounts.lastSignedInUserHash";
- const PREF_SYNC_SHOW_CUSTOMIZATION = "services.sync.ui.showCustomizationDialog";
-
- const OBSERVER_TOPICS = [
- fxAccountsCommon.ONVERIFIED_NOTIFICATION,
- fxAccountsCommon.ONLOGOUT_NOTIFICATION,
- ];
-
-@@ -99,16 +102,22 @@ let wrapper = {
- .wrappedJSObject;
-
- // Don't show about:accounts with FxA disabled.
- if (!weave.fxAccountsEnabled) {
- document.body.remove();
- return;
- }
-
-+ // If a master-password is enabled, we want to encourage the user to
-+ // unlock it. Things still work if not, but the user will probably need
-+ // to re-auth next startup (in which case we will get here again and
-+ // re-prompt)
-+ Utils.ensureMPUnlocked();
-+
- let iframe = document.getElementById("remote");
- this.iframe = iframe;
- iframe.addEventListener("load", this);
-
- try {
- iframe.src = url || fxAccounts.getAccountsSignUpURI();
- } catch (e) {
- error("Couldn't init Firefox Account wrapper: " + e.message);
---
-1.8.3.msysgit.0
-
diff --git a/data/patches/0002-Bug-1013064-part-2-Store-sensitive-FxA-credentials-i.patch b/data/patches/0002-Bug-1013064-part-2-Store-sensitive-FxA-credentials-i.patch
deleted file mode 100644
index bbd6f8d..0000000
--- a/data/patches/0002-Bug-1013064-part-2-Store-sensitive-FxA-credentials-i.patch
+++ /dev/null
@@ -1,551 +0,0 @@
-From 4a92f9ee1ba35989f82a24bba18806f8616a5be8 Mon Sep 17 00:00:00 2001
-From: Mark Hammond <mhammond@skippinet.com.au>
-Date: Sat, 14 Jun 2014 14:33:20 +1000
-Subject: Bug 1013064 (part 2) - Store sensitive FxA credentials in the login
- manager. r=ckarlof,dveditz
-
----
- services/fxaccounts/FxAccounts.jsm | 170 ++++++++++++++++++
- services/fxaccounts/FxAccountsCommon.js | 13 ++
- services/fxaccounts/moz.build | 5 +-
- .../tests/xpcshell/test_loginmgr_storage.js | 196 +++++++++++++++++++++
- services/fxaccounts/tests/xpcshell/xpcshell.ini | 2 +
- services/sync/modules/util.js | 12 +-
- 6 files changed, 396 insertions(+), 2 deletions(-)
- create mode 100644 services/fxaccounts/tests/xpcshell/test_loginmgr_storage.js
-
-diff --git a/services/fxaccounts/FxAccounts.jsm b/services/fxaccounts/FxAccounts.jsm
-index cc521b8..e9328dd 100644
---- a/services/fxaccounts/FxAccounts.jsm
-+++ b/services/fxaccounts/FxAccounts.jsm
-@@ -298,17 +298,22 @@ function FxAccountsInternal() {
- // able to abort all work on the first sign-in process. The currentTimer and
- // currentAccountState are used for this purpose.
- // (XXX - should the timer be directly on the currentAccountState?)
- this.currentTimer = null;
- this.currentAccountState = new AccountState(this);
-
- // We don't reference |profileDir| in the top-level module scope
- // as we may be imported before we know where it is.
-+ // We only want the fancy new LoginManagerStorage on desktop.
-+#if defined(MOZ_B2G)
- this.signedInUserStorage = new JSONStorage({
-+#else
-+ this.signedInUserStorage = new LoginManagerStorage({
-+#endif
- filename: DEFAULT_STORAGE_FILENAME,
- baseDir: OS.Constants.Path.profileDir,
- });
- }
-
- /**
- * The internal API's prototype.
- */
-@@ -896,16 +901,181 @@ JSONStorage.prototype = {
- .then(CommonUtils.writeJSON.bind(null, contents, this.path));
- },
-
- get: function() {
- return CommonUtils.readJSON(this.path);
- }
- };
-
-+/**
-+ * LoginManagerStorage constructor that creates instances that may set/get
-+ * from a combination of a clear-text JSON file and stored securely in
-+ * the nsILoginManager.
-+ *
-+ * @param options {
-+ * filename: of the plain-text file to write to
-+ * baseDir: directory where the file resides
-+ * }
-+ * @return instance
-+ */
-+
-+function LoginManagerStorage(options) {
-+ // we reuse the JSONStorage for writing the plain-text stuff.
-+ this.jsonStorage = new JSONStorage(options);
-+}
-+
-+LoginManagerStorage.prototype = {
-+ // The fields in the credentials JSON object that are stored in plain-text
-+ // in the profile directory. All other fields are stored in the login manager,
-+ // and thus are only available when the master-password is unlocked.
-+
-+ // a hook point for testing.
-+ get _isLoggedIn() {
-+ return Services.logins.isLoggedIn;
-+ },
-+
-+ // Clear any data from the login manager. Returns true if the login manager
-+ // was unlocked (even if no existing logins existed) or false if it was
-+ // locked (meaning we don't even know if it existed or not.)
-+ _clearLoginMgrData: Task.async(function* () {
-+ try { // Services.logins might be third-party and broken...
-+ yield Services.logins.initializationPromise;
-+ if (!this._isLoggedIn) {
-+ return false;
-+ }
-+ let logins = Services.logins.findLogins({}, FXA_PWDMGR_HOST, null, FXA_PWDMGR_REALM);
-+ for (let login of logins) {
-+ Services.logins.removeLogin(login);
-+ }
-+ return true;
-+ } catch (ex) {
-+ log.error("Failed to clear login data: ${}", ex);
-+ return false;
-+ }
-+ }),
-+
-+ set: Task.async(function* (contents) {
-+ if (!contents) {
-+ // User is signing out - write the null to the json file.
-+ yield this.jsonStorage.set(contents);
-+
-+ // And nuke it from the login manager.
-+ let cleared = yield this._clearLoginMgrData();
-+ if (!cleared) {
-+ // just log a message - we verify that the email address matches when
-+ // we reload it, so having a stale entry doesn't really hurt.
-+ log.info("not removing credentials from login manager - not logged in");
-+ }
-+ return;
-+ }
-+
-+ // We are saving actual data.
-+ // Split the data into 2 chunks - one to go to the plain-text, and the
-+ // other to write to the login manager.
-+ let toWriteJSON = {version: contents.version};
-+ let accountDataJSON = toWriteJSON.accountData = {};
-+ let toWriteLoginMgr = {version: contents.version};
-+ let accountDataLoginMgr = toWriteLoginMgr.accountData = {};
-+ for (let [name, value] of Iterator(contents.accountData)) {
-+ if (FXA_PWDMGR_PLAINTEXT_FIELDS.indexOf(name) >= 0) {
-+ accountDataJSON[name] = value;
-+ } else {
-+ accountDataLoginMgr[name] = value;
-+ }
-+ }
-+ yield this.jsonStorage.set(toWriteJSON);
-+
-+ try { // Services.logins might be third-party and broken...
-+ // and the stuff into the login manager.
-+ yield Services.logins.initializationPromise;
-+ // If MP is locked we silently fail - the user may need to re-auth
-+ // next startup.
-+ if (!this._isLoggedIn) {
-+ log.info("not saving credentials to login manager - not logged in");
-+ return;
-+ }
-+ // write the rest of the data to the login manager.
-+ let loginInfo = new Components.Constructor(
-+ "@mozilla.org/login-manager/loginInfo;1", Ci.nsILoginInfo, "init");
-+ let login = new loginInfo(FXA_PWDMGR_HOST,
-+ null, // aFormSubmitURL,
-+ FXA_PWDMGR_REALM, // aHttpRealm,
-+ contents.accountData.email, // aUsername
-+ JSON.stringify(toWriteLoginMgr), // aPassword
-+ "", // aUsernameField
-+ "");// aPasswordField
-+
-+ let existingLogins = Services.logins.findLogins({}, FXA_PWDMGR_HOST, null,
-+ FXA_PWDMGR_REALM);
-+ if (existingLogins.length) {
-+ Services.logins.modifyLogin(existingLogins[0], login);
-+ } else {
-+ Services.logins.addLogin(login);
-+ }
-+ } catch (ex) {
-+ log.error("Failed to save data to the login manager: ${}", ex);
-+ }
-+ }),
-+
-+ get: Task.async(function* () {
-+ // we need to suck some data from the .json file in the profile dir and
-+ // some other from the login manager.
-+ let data = yield this.jsonStorage.get();
-+ if (!data) {
-+ // no user logged in, nuke the storage data incase we couldn't remove
-+ // it previously and then we are done.
-+ yield this._clearLoginMgrData();
-+ return null;
-+ }
-+
-+ // if we have encryption keys it must have been saved before we
-+ // used the login manager, so re-save it.
-+ if (data.accountData.kA || data.accountData.kB || data.keyFetchToken) {
-+ log.info("account data needs migration to the login manager.");
-+ yield this.set(data);
-+ }
-+
-+ try { // Services.logins might be third-party and broken...
-+ // read the data from the login manager and merge it for return.
-+ yield Services.logins.initializationPromise;
-+
-+ if (!this._isLoggedIn) {
-+ log.info("returning partial account data as the login manager is locked.");
-+ return data;
-+ }
-+
-+ let logins = Services.logins.findLogins({}, FXA_PWDMGR_HOST, null, FXA_PWDMGR_REALM);
-+ if (logins.length == 0) {
-+ // This could happen if the MP was locked when we wrote the data.
-+ log.info("Can't find the rest of the credentials in the login manager");
-+ return data;
-+ }
-+ let login = logins[0];
-+ if (login.username == data.accountData.email) {
-+ let lmData = JSON.parse(login.password);
-+ if (lmData.version == data.version) {
-+ // Merge the login manager data
-+ copyObjectProperties(lmData.accountData, data.accountData);
-+ } else {
-+ log.info("version field in the login manager doesn't match - ignoring it");
-+ yield this._clearLoginMgrData();
-+ }
-+ } else {
-+ log.info("username in the login manager doesn't match - ignoring it");
-+ yield this._clearLoginMgrData();
-+ }
-+ } catch (ex) {
-+ log.error("Failed to get data from the login manager: ${}", ex);
-+ }
-+ return data;
-+ }),
-+
-+}
-+
- // A getter for the instance to export
- XPCOMUtils.defineLazyGetter(this, "fxAccounts", function() {
- let a = new FxAccounts();
-
- // XXX Bug 947061 - We need a strategy for resuming email verification after
- // browser restart
- a.loadAndPoll();
-
-diff --git a/services/fxaccounts/FxAccountsCommon.js b/services/fxaccounts/FxAccountsCommon.js
-index 2bef093..476e528 100644
---- a/services/fxaccounts/FxAccountsCommon.js
-+++ b/services/fxaccounts/FxAccountsCommon.js
-@@ -170,10 +170,23 @@ SERVER_ERRNO_TO_ERROR[ERRNO_INVALID_AUTH_NONCE] = ERROR_INVALID_AUTH
- SERVER_ERRNO_TO_ERROR[ERRNO_ENDPOINT_NO_LONGER_SUPPORTED] = ERROR_ENDPOINT_NO_LONGER_SUPPORTED;
- SERVER_ERRNO_TO_ERROR[ERRNO_INCORRECT_LOGIN_METHOD] = ERROR_INCORRECT_LOGIN_METHOD;
- SERVER_ERRNO_TO_ERROR[ERRNO_INCORRECT_KEY_RETRIEVAL_METHOD] = ERROR_INCORRECT_KEY_RETRIEVAL_METHOD;
- SERVER_ERRNO_TO_ERROR[ERRNO_INCORRECT_API_VERSION] = ERROR_INCORRECT_API_VERSION;
- SERVER_ERRNO_TO_ERROR[ERRNO_INCORRECT_EMAIL_CASE] = ERROR_INCORRECT_EMAIL_CASE;
- SERVER_ERRNO_TO_ERROR[ERRNO_SERVICE_TEMP_UNAVAILABLE] = ERROR_SERVICE_TEMP_UNAVAILABLE;
- SERVER_ERRNO_TO_ERROR[ERRNO_UNKNOWN_ERROR] = ERROR_UNKNOWN;
-
-+// FxAccounts has the ability to "split" the credentials between a plain-text
-+// JSON file in the profile dir and in the login manager.
-+// These constants relate to that.
-+
-+// The fields we save in the plaintext JSON.
-+// See bug 1013064 comments 23-25 for why the sessionToken is "safe"
-+this.FXA_PWDMGR_PLAINTEXT_FIELDS = ["email", "verified", "authAt",
-+ "sessionToken", "uid"];
-+// The pseudo-host we use in the login manager
-+this.FXA_PWDMGR_HOST = "chrome://FirefoxAccounts";
-+// The realm we use in the login manager.
-+this.FXA_PWDMGR_REALM = "Firefox Accounts credentials";
-+
- // Allow this file to be imported via Components.utils.import().
- this.EXPORTED_SYMBOLS = Object.keys(this);
-diff --git a/services/fxaccounts/moz.build b/services/fxaccounts/moz.build
-index f959714..1bb8c09 100644
---- a/services/fxaccounts/moz.build
-+++ b/services/fxaccounts/moz.build
-@@ -5,16 +5,19 @@
- # file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
- PARALLEL_DIRS += ['interfaces']
-
- TEST_DIRS += ['tests']
-
- EXTRA_JS_MODULES += [
- 'Credentials.jsm',
-- 'FxAccounts.jsm',
- 'FxAccountsClient.jsm',
- 'FxAccountsCommon.js'
- ]
-
-+EXTRA_PP_JS_MODULES += [
-+ 'FxAccounts.jsm',
-+]
-+
- # For now, we will only be using the FxA manager in B2G.
- if CONFIG['MOZ_B2G']:
- EXTRA_JS_MODULES += ['FxAccountsManager.jsm']
-diff --git a/services/fxaccounts/tests/xpcshell/test_loginmgr_storage.js b/services/fxaccounts/tests/xpcshell/test_loginmgr_storage.js
-new file mode 100644
-index 0000000..297b256
---- /dev/null
-+++ b/services/fxaccounts/tests/xpcshell/test_loginmgr_storage.js
-@@ -0,0 +1,196 @@
-+/* Any copyright is dedicated to the Public Domain.
-+ * http://creativecommons.org/publicdomain/zero/1.0/ */
-+
-+"use strict";
-+
-+// Tests for FxAccounts, storage and the master password.
-+
-+// Stop us hitting the real auth server.
-+Services.prefs.setCharPref("identity.fxaccounts.auth.uri", "http://localhost");
-+
-+Cu.import("resource://gre/modules/Services.jsm");
-+Cu.import("resource://gre/modules/FxAccounts.jsm");
-+Cu.import("resource://gre/modules/FxAccountsClient.jsm");
-+Cu.import("resource://gre/modules/FxAccountsCommon.js");
-+Cu.import("resource://gre/modules/osfile.jsm");
-+Cu.import("resource://services-common/utils.js");
-+Cu.import("resource://gre/modules/FxAccountsCommon.js");
-+
-+initTestLogging("Trace");
-+// See verbose logging from FxAccounts.jsm
-+Services.prefs.setCharPref("identity.fxaccounts.loglevel", "DEBUG");
-+
-+function run_test() {
-+ run_next_test();
-+}
-+
-+function getLoginMgrData() {
-+ let logins = Services.logins.findLogins({}, FXA_PWDMGR_HOST, null, FXA_PWDMGR_REALM);
-+ if (logins.length == 0) {
-+ return null;
-+ }
-+ Assert.equal(logins.length, 1, "only 1 login available");
-+ return logins[0];
-+}
-+
-+add_task(function test_simple() {
-+ let fxa = new FxAccounts({});
-+
-+ let creds = {
-+ email: "test@example.com",
-+ sessionToken: "sessionToken",
-+ kA: "the kA value",
-+ kB: "the kB value",
-+ verified: true
-+ };
-+ yield fxa.setSignedInUser(creds);
-+
-+ // This should have stored stuff in both the .json file in the profile
-+ // dir, and the login dir.
-+ let path = OS.Path.join(OS.Constants.Path.profileDir, "signedInUser.json");
-+ let data = yield CommonUtils.readJSON(path);
-+
-+ Assert.strictEqual(data.accountData.email, creds.email, "correct email in the clear text");
-+ Assert.strictEqual(data.accountData.sessionToken, creds.sessionToken, "correct sessionToken in the clear text");
-+ Assert.strictEqual(data.accountData.verified, creds.verified, "correct verified flag");
-+
-+ Assert.ok(!("kA" in data.accountData), "kA not stored in clear text");
-+ Assert.ok(!("kB" in data.accountData), "kB not stored in clear text");
-+
-+ let login = getLoginMgrData();
-+ Assert.strictEqual(login.username, creds.email, "email matches");
-+ let loginData = JSON.parse(login.password);
-+ Assert.strictEqual(loginData.version, data.version, "same version flag in both places");
-+ Assert.strictEqual(loginData.accountData.kA, creds.kA, "correct kA in the login mgr");
-+ Assert.strictEqual(loginData.accountData.kB, creds.kB, "correct kB in the login mgr");
-+
-+ Assert.ok(!("email" in loginData), "email not stored in the login mgr json");
-+ Assert.ok(!("sessionToken" in loginData), "sessionToken not stored in the login mgr json");
-+ Assert.ok(!("verified" in loginData), "verified not stored in the login mgr json");
-+
-+ yield fxa.signOut(/* localOnly = */ true);
-+ Assert.strictEqual(getLoginMgrData(), null, "login mgr data deleted on logout");
-+});
-+
-+add_task(function test_MPLocked() {
-+ let fxa = new FxAccounts({});
-+
-+ let creds = {
-+ email: "test@example.com",
-+ sessionToken: "sessionToken",
-+ kA: "the kA value",
-+ kB: "the kB value",
-+ verified: true
-+ };
-+
-+ // tell the storage that the MP is locked.
-+ fxa.internal.signedInUserStorage.__defineGetter__("_isLoggedIn", function() false);
-+ yield fxa.setSignedInUser(creds);
-+
-+ // This should have stored stuff in the .json, and the login manager stuff
-+ // will not exist.
-+ let path = OS.Path.join(OS.Constants.Path.profileDir, "signedInUser.json");
-+ let data = yield CommonUtils.readJSON(path);
-+
-+ Assert.strictEqual(data.accountData.email, creds.email, "correct email in the clear text");
-+ Assert.strictEqual(data.accountData.sessionToken, creds.sessionToken, "correct sessionToken in the clear text");
-+ Assert.strictEqual(data.accountData.verified, creds.verified, "correct verified flag");
-+
-+ Assert.ok(!("kA" in data.accountData), "kA not stored in clear text");
-+ Assert.ok(!("kB" in data.accountData), "kB not stored in clear text");
-+
-+ Assert.strictEqual(getLoginMgrData(), null, "login mgr data doesn't exist");
-+ yield fxa.signOut(/* localOnly = */ true)
-+});
-+
-+add_task(function test_consistentWithMPEdgeCases() {
-+ let fxa = new FxAccounts({});
-+
-+ let creds1 = {
-+ email: "test@example.com",
-+ sessionToken: "sessionToken",
-+ kA: "the kA value",
-+ kB: "the kB value",
-+ verified: true
-+ };
-+
-+ let creds2 = {
-+ email: "test2@example.com",
-+ sessionToken: "sessionToken2",
-+ kA: "the kA value2",
-+ kB: "the kB value2",
-+ verified: false,
-+ };
-+
-+ // Log a user in while MP is unlocked.
-+ yield fxa.setSignedInUser(creds1);
-+
-+ // tell the storage that the MP is locked - this will prevent logout from
-+ // being able to clear the data.
-+ fxa.internal.signedInUserStorage.__defineGetter__("_isLoggedIn", function() false);
-+
-+ // now set the second credentials.
-+ yield fxa.setSignedInUser(creds2);
-+
-+ // We should still have creds1 data in the login manager.
-+ let login = getLoginMgrData();
-+ Assert.strictEqual(login.username, creds1.email);
-+ // and that we do have the first kA in the login manager.
-+ Assert.strictEqual(JSON.parse(login.password).accountData.kA, creds1.kA,
-+ "stale data still in login mgr");
-+
-+ // Make a new FxA instance (otherwise the values in memory will be used.)
-+ // Because we haven't overridden _isLoggedIn for this new instance it will
-+ // treat the MP as unlocked.
-+ let fxa = new FxAccounts({});
-+
-+ let accountData = yield fxa.getSignedInUser();
-+ Assert.strictEqual(accountData.email, creds2.email);
-+ // we should have no kA at all.
-+ Assert.strictEqual(accountData.kA, undefined, "stale kA wasn't used");
-+ yield fxa.signOut(/* localOnly = */ true)
-+});
-+
-+add_task(function test_migration() {
-+ // manually write out the full creds data to the JSON - this will look like
-+ // old data that needs migration.
-+ let creds = {
-+ email: "test@example.com",
-+ sessionToken: "sessionToken",
-+ kA: "the kA value",
-+ kB: "the kB value",
-+ verified: true
-+ };
-+ let toWrite = {
-+ version: 1,
-+ accountData: creds,
-+ };
-+
-+ let path = OS.Path.join(OS.Constants.Path.profileDir, "signedInUser.json");
-+ let data = yield CommonUtils.writeJSON(toWrite, path);
-+
-+ // Create an FxA object - and tell it to load the data.
-+ let fxa = new FxAccounts({});
-+ data = yield fxa.getSignedInUser();
-+
-+ Assert.deepEqual(data, creds, "we should have everything available");
-+
-+ // now sniff the data on disk - it should have been magically migrated.
-+ data = yield CommonUtils.readJSON(path);
-+
-+ Assert.strictEqual(data.accountData.email, creds.email, "correct email in the clear text");
-+ Assert.strictEqual(data.accountData.sessionToken, creds.sessionToken, "correct sessionToken in the clear text");
-+ Assert.strictEqual(data.accountData.verified, creds.verified, "correct verified flag");
-+
-+ Assert.ok(!("kA" in data.accountData), "kA not stored in clear text");
-+ Assert.ok(!("kB" in data.accountData), "kB not stored in clear text");
-+
-+ // and it should magically be in the login manager.
-+ let login = getLoginMgrData();
-+ Assert.strictEqual(login.username, creds.email);
-+ // and that we do have the first kA in the login manager.
-+ Assert.strictEqual(JSON.parse(login.password).accountData.kA, creds.kA,
-+ "kA was migrated");
-+
-+ yield fxa.signOut(/* localOnly = */ true)
-+});
-diff --git a/services/fxaccounts/tests/xpcshell/xpcshell.ini b/services/fxaccounts/tests/xpcshell/xpcshell.ini
-index 9d9bffe..4448d76 100644
---- a/services/fxaccounts/tests/xpcshell/xpcshell.ini
-+++ b/services/fxaccounts/tests/xpcshell/xpcshell.ini
-@@ -1,10 +1,12 @@
- [DEFAULT]
- head = head.js ../../../common/tests/unit/head_helpers.js ../../../common/tests/unit/head_http.js
- tail =
-
- [test_accounts.js]
- [test_client.js]
- [test_credentials.js]
-+[test_loginmgr_storage.js]
-+skip-if = appname == 'b2g' # login manager storage only used on desktop.
- [test_manager.js]
- run-if = appname == 'b2g'
- reason = FxAccountsManager is only available for B2G for now
-diff --git a/services/sync/modules/util.js b/services/sync/modules/util.js
-index 194e0b2..4691428 100644
---- a/services/sync/modules/util.js
-+++ b/services/sync/modules/util.js
-@@ -14,16 +14,23 @@ Cu.import("resource://services-common/async.js", this);
- Cu.import("resource://services-crypto/utils.js");
- Cu.import("resource://services-sync/constants.js");
- Cu.import("resource://gre/modules/Preferences.jsm");
- Cu.import("resource://gre/modules/Services.jsm", this);
- Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
- Cu.import("resource://gre/modules/osfile.jsm", this);
- Cu.import("resource://gre/modules/Task.jsm", this);
-
-+// FxAccountsCommon.js doesn't use a "namespace", so create one here.
-+XPCOMUtils.defineLazyGetter(this, "FxAccountsCommon", function() {
-+ let FxAccountsCommon = {};
-+ Cu.import("resource://gre/modules/FxAccountsCommon.js", FxAccountsCommon);
-+ return FxAccountsCommon;
-+});
-+
- /*
- * Utility functions
- */
-
- this.Utils = {
- // Alias in functions from CommonUtils. These previously were defined here.
- // In the ideal world, references to these would be removed.
- nextTick: CommonUtils.nextTick,
-@@ -589,18 +596,21 @@ this.Utils = {
- * reset when we drop sync credentials, etc.
- */
- getSyncCredentialsHosts: function() {
- // This is somewhat expensive and the result static, so we cache the result.
- if (this._syncCredentialsHosts) {
- return this._syncCredentialsHosts;
- }
- let result = new Set();
-- // the legacy sync host.
-+ // the legacy sync host
- result.add(PWDMGR_HOST);
-+ // the FxA host
-+ result.add(FxAccountsCommon.FXA_PWDMGR_HOST);
-+ //
- // The FxA hosts - these almost certainly all have the same hostname, but
- // better safe than sorry...
- for (let prefName of ["identity.fxaccounts.remote.force_auth.uri",
- "identity.fxaccounts.remote.signup.uri",
- "identity.fxaccounts.remote.signin.uri",
- "identity.fxaccounts.settings.uri"]) {
- let prefVal;
- try {
---
-1.8.3.msysgit.0
-
diff --git a/data/patches/0003-Bug-1013064-part-3-only-migrate-data-into-the-loginm.patch b/data/patches/0003-Bug-1013064-part-3-only-migrate-data-into-the-loginm.patch
deleted file mode 100644
index 063a031..0000000
--- a/data/patches/0003-Bug-1013064-part-3-only-migrate-data-into-the-loginm.patch
+++ /dev/null
@@ -1,194 +0,0 @@
-From 86c67f9b9081ce905442c86b38575b3422c8dce3 Mon Sep 17 00:00:00 2001
-From: Mark Hammond <mhammond@skippinet.com.au>
-Date: Wed, 18 Jun 2014 15:07:41 +1000
-Subject: Bug 1013064 (part 3) - only migrate data into the loginmgr when it is
- unlocked. r=ckarlof
-
----
- services/fxaccounts/FxAccounts.jsm | 25 ++++-
- .../tests/xpcshell/test_loginmgr_storage.js | 113 +++++++++++++++++++++
- 2 files changed, 137 insertions(+), 1 deletion(-)
-
-diff --git a/services/fxaccounts/FxAccounts.jsm b/services/fxaccounts/FxAccounts.jsm
-index e9328dd..14d71fc 100644
---- a/services/fxaccounts/FxAccounts.jsm
-+++ b/services/fxaccounts/FxAccounts.jsm
-@@ -1025,17 +1025,40 @@ LoginManagerStorage.prototype = {
- // it previously and then we are done.
- yield this._clearLoginMgrData();
- return null;
- }
-
- // if we have encryption keys it must have been saved before we
- // used the login manager, so re-save it.
- if (data.accountData.kA || data.accountData.kB || data.keyFetchToken) {
-- log.info("account data needs migration to the login manager.");
-+ // We need to migrate, but the MP might be locked (eg, on the first run
-+ // with this enabled, we will get here very soon after startup, so will
-+ // certainly be locked.) This means we can't actually store the data in
-+ // the login manager (and thus might lose it if we migrated now)
-+ // So if the MP is locked, we *don't* migrate, but still just return
-+ // the subset of data we now store in the JSON.
-+ // This will cause sync to notice the lack of keys, force an unlock then
-+ // re-fetch the account data to see if the keys are there. At *that*
-+ // point we will end up back here, but because the MP is now unlocked
-+ // we can actually perform the migration.
-+ if (!this._isLoggedIn) {
-+ // return the "safe" subset but leave the storage alone.
-+ log.info("account data needs migration to the login manager but the MP is locked.");
-+ let result = {
-+ version: data.version,
-+ accountData: {},
-+ };
-+ for (let fieldName of FXA_PWDMGR_PLAINTEXT_FIELDS) {
-+ result.accountData[fieldName] = data.accountData[fieldName];
-+ }
-+ return result;
-+ }
-+ // actually migrate - just calling .set() will split everything up.
-+ log.info("account data is being migrated to the login manager.");
- yield this.set(data);
- }
-
- try { // Services.logins might be third-party and broken...
- // read the data from the login manager and merge it for return.
- yield Services.logins.initializationPromise;
-
- if (!this._isLoggedIn) {
-diff --git a/services/fxaccounts/tests/xpcshell/test_loginmgr_storage.js b/services/fxaccounts/tests/xpcshell/test_loginmgr_storage.js
-index 297b256..a9cf5f4 100644
---- a/services/fxaccounts/tests/xpcshell/test_loginmgr_storage.js
-+++ b/services/fxaccounts/tests/xpcshell/test_loginmgr_storage.js
-@@ -98,16 +98,129 @@ add_task(function test_MPLocked() {
-
- Assert.ok(!("kA" in data.accountData), "kA not stored in clear text");
- Assert.ok(!("kB" in data.accountData), "kB not stored in clear text");
-
- Assert.strictEqual(getLoginMgrData(), null, "login mgr data doesn't exist");
- yield fxa.signOut(/* localOnly = */ true)
- });
-
-+add_task(function test_migrationMPUnlocked() {
-+ // first manually save a signedInUser.json to simulate a first-run with
-+ // pre-migrated data.
-+ let fxa = new FxAccounts({});
-+
-+ let creds = {
-+ email: "test@example.com",
-+ sessionToken: "sessionToken",
-+ kA: "the kA value",
-+ kB: "the kB value",
-+ verified: true
-+ };
-+ let toWrite = {
-+ version: fxa.version,
-+ accountData: creds,
-+ }
-+
-+ let path = OS.Path.join(OS.Constants.Path.profileDir, "signedInUser.json");
-+ yield CommonUtils.writeJSON(toWrite, path);
-+
-+ // now load it - it should migrate.
-+ let data = yield fxa.getSignedInUser();
-+ Assert.deepEqual(data, creds, "we got all the data back");
-+
-+ // and verify it was actually migrated - re-read signedInUser back.
-+ let data = yield CommonUtils.readJSON(path);
-+
-+ Assert.strictEqual(data.accountData.email, creds.email, "correct email in the clear text");
-+ Assert.strictEqual(data.accountData.sessionToken, creds.sessionToken, "correct sessionToken in the clear text");
-+ Assert.strictEqual(data.accountData.verified, creds.verified, "correct verified flag");
-+
-+ Assert.ok(!("kA" in data.accountData), "kA not stored in clear text");
-+ Assert.ok(!("kB" in data.accountData), "kB not stored in clear text");
-+
-+ let login = getLoginMgrData();
-+ Assert.strictEqual(login.username, creds.email, "email matches");
-+ let loginData = JSON.parse(login.password);
-+ Assert.strictEqual(loginData.version, data.version, "same version flag in both places");
-+ Assert.strictEqual(loginData.accountData.kA, creds.kA, "correct kA in the login mgr");
-+ Assert.strictEqual(loginData.accountData.kB, creds.kB, "correct kB in the login mgr");
-+
-+ Assert.ok(!("email" in loginData), "email not stored in the login mgr json");
-+ Assert.ok(!("sessionToken" in loginData), "sessionToken not stored in the login mgr json");
-+ Assert.ok(!("verified" in loginData), "verified not stored in the login mgr json");
-+
-+ yield fxa.signOut(/* localOnly = */ true);
-+ Assert.strictEqual(getLoginMgrData(), null, "login mgr data deleted on logout");
-+});
-+
-+add_task(function test_migrationMPLocked() {
-+ // first manually save a signedInUser.json to simulate a first-run with
-+ // pre-migrated data.
-+ let fxa = new FxAccounts({});
-+
-+ let creds = {
-+ email: "test@example.com",
-+ sessionToken: "sessionToken",
-+ kA: "the kA value",
-+ kB: "the kB value",
-+ verified: true
-+ };
-+ let toWrite = {
-+ version: fxa.version,
-+ accountData: creds,
-+ }
-+
-+ let path = OS.Path.join(OS.Constants.Path.profileDir, "signedInUser.json");
-+ yield CommonUtils.writeJSON(toWrite, path);
-+
-+ // pretend the MP is locked.
-+ fxa.internal.signedInUserStorage.__defineGetter__("_isLoggedIn", function() false);
-+
-+ // now load it - it should *not* migrate, but should only give the JSON-safe
-+ // data back.
-+ let data = yield fxa.getSignedInUser();
-+ Assert.ok(!data.kA);
-+ Assert.ok(!data.kB);
-+
-+ // and verify the data on disk wan't migrated.
-+ data = yield CommonUtils.readJSON(path);
-+ Assert.deepEqual(data, toWrite);
-+
-+ // Now "unlock" and re-ask for the signedInUser - it should migrate.
-+ fxa.internal.signedInUserStorage.__defineGetter__("_isLoggedIn", function() true);
-+ data = yield fxa.getSignedInUser();
-+ // this time we should have got all the data, not just the JSON-safe fields.
-+ Assert.strictEqual(data.kA, creds.kA);
-+ Assert.strictEqual(data.kB, creds.kB);
-+
-+ // And verify the data in the JSON was migrated
-+ data = yield CommonUtils.readJSON(path);
-+ Assert.strictEqual(data.accountData.email, creds.email, "correct email in the clear text");
-+ Assert.strictEqual(data.accountData.sessionToken, creds.sessionToken, "correct sessionToken in the clear text");
-+ Assert.strictEqual(data.accountData.verified, creds.verified, "correct verified flag");
-+
-+ Assert.ok(!("kA" in data.accountData), "kA not stored in clear text");
-+ Assert.ok(!("kB" in data.accountData), "kB not stored in clear text");
-+
-+ let login = getLoginMgrData();
-+ Assert.strictEqual(login.username, creds.email, "email matches");
-+ let loginData = JSON.parse(login.password);
-+ Assert.strictEqual(loginData.version, data.version, "same version flag in both places");
-+ Assert.strictEqual(loginData.accountData.kA, creds.kA, "correct kA in the login mgr");
-+ Assert.strictEqual(loginData.accountData.kB, creds.kB, "correct kB in the login mgr");
-+
-+ Assert.ok(!("email" in loginData), "email not stored in the login mgr json");
-+ Assert.ok(!("sessionToken" in loginData), "sessionToken not stored in the login mgr json");
-+ Assert.ok(!("verified" in loginData), "verified not stored in the login mgr json");
-+
-+ yield fxa.signOut(/* localOnly = */ true);
-+ Assert.strictEqual(getLoginMgrData(), null, "login mgr data deleted on logout");
-+});
-+
- add_task(function test_consistentWithMPEdgeCases() {
- let fxa = new FxAccounts({});
-
- let creds1 = {
- email: "test@example.com",
- sessionToken: "sessionToken",
- kA: "the kA value",
- kB: "the kB value",
---
-1.8.3.msysgit.0
-
diff --git a/data/patches/0004-Bug-1013064-part-4b-browserid_identity-and-sync-chang.patch b/data/patches/0004-Bug-1013064-part-4b-browserid_identity-and-sync-chang.patch
deleted file mode 100644
index 369bde6..0000000
--- a/data/patches/0004-Bug-1013064-part-4b-browserid_identity-and-sync-chang.patch
+++ /dev/null
@@ -1,286 +0,0 @@
-From 9717484083e66b78eedfa14e539d51382aba760f Mon Sep 17 00:00:00 2001
-From: Mark Hammond <mhammond@skippinet.com.au>
-Date: Sat, 14 Jun 2014 14:33:56 +1000
-Subject: Bug 1013064 (part 4) - browserid_identity and sync changes to support
- FxA credentials in the login manager. r=ckarlof,rnewman
-
----
- services/sync/modules/browserid_identity.js | 61 ++++++++++++++++++++--
- services/sync/modules/identity.js | 19 +++++++
- services/sync/modules/service.js | 20 ++++---
- .../sync/tests/unit/test_browserid_identity.js | 15 ++++++
- 4 files changed, 102 insertions(+), 13 deletions(-)
-
-diff --git a/services/sync/modules/browserid_identity.js b/services/sync/modules/browserid_identity.js
---- a/services/sync/modules/browserid_identity.js
-+++ b/services/sync/modules/browserid_identity.js
-@@ -394,41 +394,83 @@ this.BrowserIDManager.prototype = {
- this._syncKeyUpdated = true;
- this._shouldHaveSyncKeyBundle = false;
- },
-
- /**
- * The current state of the auth credentials.
- *
- * This essentially validates that enough credentials are available to use
-- * Sync.
-+ * Sync, although it effectively ignores the state of the master-password -
-+ * if that's locked and that's the only problem we can see, say everything
-+ * is OK - unlockAndVerifyAuthState will be used to perform the unlock
-+ * and re-verification if necessary.
- */
- get currentAuthState() {
- if (this._authFailureReason) {
- this._log.info("currentAuthState returning " + this._authFailureReason +
- " due to previous failure");
- return this._authFailureReason;
- }
- // TODO: need to revisit this. Currently this isn't ready to go until
- // both the username and syncKeyBundle are both configured and having no
- // username seems to make things fail fast so that's good.
- if (!this.username) {
- return LOGIN_FAILED_NO_USERNAME;
- }
-
- // No need to check this.syncKey as our getter for that attribute
- // uses this.syncKeyBundle
-- // If bundle creation started, but failed.
-- if (this._shouldHaveSyncKeyBundle && !this.syncKeyBundle) {
-- return LOGIN_FAILED_NO_PASSPHRASE;
-+ // If bundle creation started, but failed due to any reason other than
-+ // the MP being locked...
-+ if (this._shouldHaveSyncKeyBundle && !this.syncKeyBundle && !Utils.mpLocked()) {
-+ // Return a state that says a re-auth is necessary so we can get keys.
-+ return LOGIN_FAILED_LOGIN_REJECTED;
- }
-
- return STATUS_OK;
- },
-
-+ // Do we currently have keys, or do we have enough that we should be able
-+ // to successfully fetch them?
-+ _canFetchKeys: function() {
-+ let userData = this._signedInUser;
-+ // a keyFetchToken means we can almost certainly grab them.
-+ // kA and kB means we already have them.
-+ return userData && (userData.keyFetchToken || (userData.kA && userData.kB));
-+ },
-+
-+ /**
-+ * Verify the current auth state, unlocking the master-password if necessary.
-+ *
-+ * Returns a promise that resolves with the current auth state after
-+ * attempting to unlock.
-+ */
-+ unlockAndVerifyAuthState: function() {
-+ if (this._canFetchKeys()) {
-+ return Promise.resolve(STATUS_OK);
-+ }
-+ // so no keys - ensure MP unlocked.
-+ if (!Utils.ensureMPUnlocked()) {
-+ // user declined to unlock, so we don't know if they are stored there.
-+ return Promise.resolve(MASTER_PASSWORD_LOCKED);
-+ }
-+ // now we are unlocked we must re-fetch the user data as we may now have
-+ // the details that were previously locked away.
-+ return this._fxaService.getSignedInUser().then(
-+ accountData => {
-+ this._updateSignedInUser(accountData);
-+ // If we still can't get keys it probably means the user authenticated
-+ // without unlocking the MP or cleared the saved logins, so we've now
-+ // lost them - the user will need to reauth before continuing.
-+ return this._canFetchKeys() ? STATUS_OK : LOGIN_FAILED_LOGIN_REJECTED;
-+ }
-+ );
-+ },
-+
- /**
- * Do we have a non-null, not yet expired token for the user currently
- * signed in?
- */
- hasValidToken: function() {
- if (!this._token) {
- return false;
- }
-@@ -444,16 +486,24 @@ this.BrowserIDManager.prototype = {
- if (tokenServerURI.endsWith("/")) { // trailing slashes cause problems...
- tokenServerURI = tokenServerURI.slice(0, -1);
- }
- let log = this._log;
- let client = this._tokenServerClient;
- let fxa = this._fxaService;
- let userData = this._signedInUser;
-
-+ // We need kA and kB for things to work. If we don't have them, just
-+ // return null for the token - sync calling unlockAndVerifyAuthState()
-+ // before actually syncing will setup the error states if necessary.
-+ if (!this._canFetchKeys()) {
-+ log.info("_fetchTokenForUser has no keys to use.");
-+ return null;
-+ }
-+
- log.info("Fetching assertion and token from: " + tokenServerURI);
-
- let maybeFetchKeys = () => {
- // This is called at login time and every time we need a new token - in
- // the latter case we already have kA and kB, so optimise that case.
- if (userData.kA && userData.kB) {
- return;
- }
-@@ -519,17 +569,18 @@ this.BrowserIDManager.prototype = {
- // TODO: write tests to make sure that different auth error cases are handled here
- // properly: auth error getting assertion, auth error getting token (invalid generation
- // and client-state error)
- if (err instanceof AuthenticationError) {
- this._log.error("Authentication error in _fetchTokenForUser: " + err);
- // set it to the "fatal" LOGIN_FAILED_LOGIN_REJECTED reason.
- this._authFailureReason = LOGIN_FAILED_LOGIN_REJECTED;
- } else {
-- this._log.error("Non-authentication error in _fetchTokenForUser: " + err.message);
-+ this._log.error("Non-authentication error in _fetchTokenForUser: "
-+ + (err.message || err));
- // for now assume it is just a transient network related problem.
- this._authFailureReason = LOGIN_FAILED_NETWORK_ERROR;
- }
- // Drop the sync key bundle, but still expect to have one.
- // This will arrange for us to be in the right 'currentAuthState'
- // such that UI will show the right error.
- this._shouldHaveSyncKeyBundle = true;
- Weave.Status.login = this._authFailureReason;
-diff --git a/services/sync/modules/identity.js b/services/sync/modules/identity.js
---- a/services/sync/modules/identity.js
-+++ b/services/sync/modules/identity.js
-@@ -373,16 +373,35 @@ IdentityManager.prototype = {
- if (!this.syncKeyBundle) {
- return LOGIN_FAILED_INVALID_PASSPHRASE;
- }
-
- return STATUS_OK;
- },
-
- /**
-+ * Verify the current auth state, unlocking the master-password if necessary.
-+ *
-+ * Returns a promise that resolves with the current auth state after
-+ * attempting to unlock.
-+ */
-+ unlockAndVerifyAuthState: function() {
-+ // Try to fetch the passphrase - this will prompt for MP unlock as a
-+ // side-effect...
-+ try {
-+ this.syncKey;
-+ } catch (ex) {
-+ this._log.debug("Fetching passphrase threw " + ex +
-+ "; assuming master password locked.");
-+ return Promise.resolve(MASTER_PASSWORD_LOCKED);
-+ }
-+ return Promise.resolve(STATUS_OK);
-+ },
-+
-+ /**
- * Persist credentials to password store.
- *
- * When credentials are updated, they are changed in memory only. This will
- * need to be called to save them to the underlying password store.
- *
- * If the password store is locked (e.g. if the master password hasn't been
- * entered), this could throw an exception.
- */
-diff --git a/services/sync/modules/service.js b/services/sync/modules/service.js
---- a/services/sync/modules/service.js
-+++ b/services/sync/modules/service.js
-@@ -679,27 +679,31 @@ Sync11Service.prototype = {
- }
-
- if (!this.identity.username) {
- this._log.warn("No username in verifyLogin.");
- this.status.login = LOGIN_FAILED_NO_USERNAME;
- return false;
- }
-
-- // Unlock master password, or return.
- // Attaching auth credentials to a request requires access to
- // passwords, which means that Resource.get can throw MP-related
- // exceptions!
-- // Try to fetch the passphrase first, while we still have control.
-- try {
-- this.identity.syncKey;
-- } catch (ex) {
-- this._log.debug("Fetching passphrase threw " + ex +
-- "; assuming master password locked.");
-- this.status.login = MASTER_PASSWORD_LOCKED;
-+ // So we ask the identity to verify the login state after unlocking the
-+ // master password (ie, this call is expected to prompt for MP unlock
-+ // if necessary) while we still have control.
-+ let cb = Async.makeSpinningCallback();
-+ this.identity.unlockAndVerifyAuthState().then(
-+ result => cb(null, result),
-+ cb
-+ );
-+ let unlockedState = cb.wait();
-+ this._log.debug("Fetching unlocked auth state returned " + unlockedState);
-+ if (unlockedState != STATUS_OK) {
-+ this.status.login = unlockedState;
- return false;
- }
-
- try {
- // Make sure we have a cluster to verify against.
- // This is a little weird, if we don't get a node we pretend
- // to succeed, since that probably means we just don't have storage.
- if (this.clusterURL == "" && !this._clusterManager.setCluster()) {
-diff --git a/services/sync/tests/unit/test_browserid_identity.js b/services/sync/tests/unit/test_browserid_identity.js
---- a/services/sync/tests/unit/test_browserid_identity.js
-+++ b/services/sync/tests/unit/test_browserid_identity.js
-@@ -78,19 +78,33 @@ add_task(function test_initialializeWith
- browseridManager.initializeWithCurrentIdentity();
- yield browseridManager.whenReadyToAuthenticate.promise;
- do_check_true(!!browseridManager._token);
- do_check_true(browseridManager.hasValidToken());
- do_check_eq(browseridManager.account, identityConfig.fxaccount.user.email);
- }
- );
-
-+add_task(function test_initialializeWithNoKeys() {
-+ _("Verify start after initializeWithCurrentIdentity without kA, kB or keyFetchToken");
-+ let identityConfig = makeIdentityConfig();
-+ delete identityConfig.fxaccount.user.kA;
-+ delete identityConfig.fxaccount.user.kB;
-+ // there's no keyFetchToken by default, so the initialize should fail.
-+ configureFxAccountIdentity(browseridManager, identityConfig);
-+
-+ yield browseridManager.initializeWithCurrentIdentity();
-+ yield browseridManager.whenReadyToAuthenticate.promise;
-+ do_check_eq(Status.login, LOGIN_SUCCEEDED, "login succeeded even without keys");
-+ do_check_false(browseridManager._canFetchKeys(), "_canFetchKeys reflects lack of keys");
-+});
-
- add_test(function test_getResourceAuthenticator() {
- _("BrowserIDManager supplies a Resource Authenticator callback which returns a Hawk header.");
-+ configureFxAccountIdentity(browseridManager);
- let authenticator = browseridManager.getResourceAuthenticator();
- do_check_true(!!authenticator);
- let req = {uri: CommonUtils.makeURI(
- "https://example.net/somewhere/over/the/rainbow"),
- method: 'GET'};
- let output = authenticator(req, 'GET');
- do_check_true('headers' in output);
- do_check_true('authorization' in output.headers);
-@@ -235,16 +249,17 @@ add_test(function test_RESTResourceAuthe
- (getTimestampDelta(authHeader, now) - 12 * HOUR_MS) < 2 * MINUTE_MS);
-
- run_next_test();
- });
-
- add_task(function test_ensureLoggedIn() {
- configureFxAccountIdentity(browseridManager);
- yield browseridManager.initializeWithCurrentIdentity();
-+ yield browseridManager.whenReadyToAuthenticate.promise;
- Assert.equal(Status.login, LOGIN_SUCCEEDED, "original initialize worked");
- yield browseridManager.ensureLoggedIn();
- Assert.equal(Status.login, LOGIN_SUCCEEDED, "original ensureLoggedIn worked");
- Assert.ok(browseridManager._shouldHaveSyncKeyBundle,
- "_shouldHaveSyncKeyBundle should always be true after ensureLogin completes.");
-
- // arrange for no logged in user.
- let fxa = browseridManager._fxaService
diff --git a/data/patches/0005-Bug-1013064-part-5-stop-disabling-the-password-engin.patch b/data/patches/0005-Bug-1013064-part-5-stop-disabling-the-password-engin.patch
deleted file mode 100644
index 394d27c..0000000
--- a/data/patches/0005-Bug-1013064-part-5-stop-disabling-the-password-engin.patch
+++ /dev/null
@@ -1,519 +0,0 @@
-From 07aa9cc1fcd5479976effe29f6adf5ad5ba7a8f8 Mon Sep 17 00:00:00 2001
-From: Mark Hammond <mhammond@skippinet.com.au>
-Date: Thu, 12 Jun 2014 18:19:49 +1000
-Subject: Bug 1013064 (part 5) - stop disabling the password engine when MP
- enabled. r=ttaubert
-
-diff --git a/browser/base/content/sync/customize.js b/browser/base/content/sync/customize.js
---- a/browser/base/content/sync/customize.js
-+++ b/browser/base/content/sync/customize.js
-@@ -1,25 +1,11 @@
- /* 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/. */
-
- "use strict";
-
--const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
--
--Cu.import("resource://gre/modules/Services.jsm");
--
--let service = Cc["@mozilla.org/weave/service;1"]
-- .getService(Ci.nsISupports)
-- .wrappedJSObject;
--
--if (!service.allowPasswordsEngine) {
-- let checkbox = document.getElementById("fxa-pweng-chk");
-- checkbox.checked = false;
-- checkbox.disabled = true;
--}
--
- addEventListener("dialogaccept", function () {
- let pane = document.getElementById("sync-customize-pane");
- pane.writePreferences(true);
- window.arguments[0].accepted = true;
- });
-diff --git a/browser/base/content/sync/customize.xul b/browser/base/content/sync/customize.xul
---- a/browser/base/content/sync/customize.xul
-+++ b/browser/base/content/sync/customize.xul
-@@ -40,18 +40,17 @@
-
- <vbox align="start">
- <checkbox label="&engine.tabs.label;"
- accesskey="&engine.tabs.accesskey;"
- preference="engine.tabs"/>
- <checkbox label="&engine.bookmarks.label;"
- accesskey="&engine.bookmarks.accesskey;"
- preference="engine.bookmarks"/>
-- <checkbox id="fxa-pweng-chk"
-- label="&engine.passwords.label;"
-+ <checkbox label="&engine.passwords.label;"
- accesskey="&engine.passwords.accesskey;"
- preference="engine.passwords"/>
- <checkbox label="&engine.history.label;"
- accesskey="&engine.history.accesskey;"
- preference="engine.history"/>
- <checkbox label="&engine.addons.label;"
- accesskey="&engine.addons.accesskey;"
- preference="engine.addons"/>
-diff --git a/browser/base/content/sync/utils.js b/browser/base/content/sync/utils.js
---- a/browser/base/content/sync/utils.js
-+++ b/browser/base/content/sync/utils.js
-@@ -82,22 +82,16 @@ let gSyncUtils = {
- this._openLink(Weave.Svc.Prefs.get(root + "termsURL"));
- },
-
- openPrivacyPolicy: function () {
- let root = this.fxAccountsEnabled ? "fxa." : "";
- this._openLink(Weave.Svc.Prefs.get(root + "privacyURL"));
- },
-
-- openMPInfoPage: function (event) {
-- event.stopPropagation();
-- let baseURL = Services.urlFormatter.formatURLPref("app.support.baseURL");
-- this._openLink(baseURL + "sync-master-password");
-- },
--
- openFirstSyncProgressPage: function () {
- this._openLink("about:sync-progress");
- },
-
- /**
- * Prepare an invisible iframe with the passphrase backup document.
- * Used by both the print and saving methods.
- *
-diff --git a/browser/components/preferences/in-content/sync.js b/browser/components/preferences/in-content/sync.js
---- a/browser/components/preferences/in-content/sync.js
-+++ b/browser/components/preferences/in-content/sync.js
-@@ -149,27 +149,16 @@ let gSyncPane = {
- document.getElementById("fxaEmailAddress1").textContent = data.email;
- document.getElementById("fxaEmailAddress2").textContent = data.email;
- document.getElementById("fxaEmailAddress3").textContent = data.email;
- document.getElementById("fxaSyncComputerName").value = Weave.Service.clientsEngine.localName;
- let engines = document.getElementById("fxaSyncEngines")
- for (let checkbox of engines.querySelectorAll("checkbox")) {
- checkbox.disabled = enginesListDisabled;
- }
--
-- let checkbox = document.getElementById("fxa-pweng-chk");
-- let help = document.getElementById("fxa-pweng-help");
-- let allowPasswordsEngine = service.allowPasswordsEngine;
--
-- if (!allowPasswordsEngine) {
-- checkbox.checked = false;
-- }
--
-- checkbox.disabled = !allowPasswordsEngine || enginesListDisabled;
-- help.hidden = allowPasswordsEngine || enginesListDisabled;
- });
- // If fxAccountEnabled is false and we are in a "not configured" state,
- // then fxAccounts is probably fully disabled rather than just unconfigured,
- // so handle this case. This block can be removed once we remove support
- // for fxAccounts being disabled.
- } else if (Weave.Status.service == Weave.CLIENT_NOT_CONFIGURED ||
- Weave.Svc.Prefs.get("firstSync", "") == "notReady") {
- this.page = PAGE_NO_ACCOUNT;
-diff --git a/browser/components/preferences/in-content/sync.xul b/browser/components/preferences/in-content/sync.xul
---- a/browser/components/preferences/in-content/sync.xul
-+++ b/browser/components/preferences/in-content/sync.xul
-@@ -278,30 +278,19 @@
- <hbox id="fxaSyncEngines">
- <vbox>
- <checkbox label="&engine.tabs.label;"
- accesskey="&engine.tabs.accesskey;"
- preference="engine.tabs"/>
- <checkbox label="&engine.bookmarks.label;"
- accesskey="&engine.bookmarks.accesskey;"
- preference="engine.bookmarks"/>
-- <hbox>
-- <checkbox id="fxa-pweng-chk"
-- label="&engine.passwords.label;"
-- accesskey="&engine.passwords.accesskey;"
-- preference="engine.passwords"/>
--
-- <vbox id="fxa-pweng-help">
-- <spacer flex="1"/>
-- <hbox id="fxa-pweng-help-link">
-- <image onclick="gSyncUtils.openMPInfoPage(event);" />
-- </hbox>
-- <spacer flex="1"/>
-- </vbox>
-- </hbox>
-+ <checkbox label="&engine.passwords.label;"
-+ accesskey="&engine.passwords.accesskey;"
-+ preference="engine.passwords"/>
- <checkbox label="&engine.history.label;"
- accesskey="&engine.history.accesskey;"
- preference="engine.history"/>
- <checkbox label="&engine.addons.label;"
- accesskey="&engine.addons.accesskey;"
- preference="engine.addons"/>
- <checkbox label="&engine.prefs.label;"
- accesskey="&engine.prefs.accesskey;"
-diff --git a/browser/components/preferences/sync.js b/browser/components/preferences/sync.js
---- a/browser/components/preferences/sync.js
-+++ b/browser/components/preferences/sync.js
-@@ -149,27 +149,16 @@ let gSyncPane = {
- document.getElementById("fxaEmailAddress1").textContent = data.email;
- document.getElementById("fxaEmailAddress2").textContent = data.email;
- document.getElementById("fxaEmailAddress3").textContent = data.email;
- document.getElementById("fxaSyncComputerName").value = Weave.Service.clientsEngine.localName;
- let engines = document.getElementById("fxaSyncEngines")
- for (let checkbox of engines.querySelectorAll("checkbox")) {
- checkbox.disabled = enginesListDisabled;
- }
--
-- let checkbox = document.getElementById("fxa-pweng-chk");
-- let help = document.getElementById("fxa-pweng-help");
-- let allowPasswordsEngine = service.allowPasswordsEngine;
--
-- if (!allowPasswordsEngine) {
-- checkbox.checked = false;
-- }
--
-- checkbox.disabled = !allowPasswordsEngine || enginesListDisabled;
-- help.hidden = allowPasswordsEngine || enginesListDisabled;
- });
- // If fxAccountEnabled is false and we are in a "not configured" state,
- // then fxAccounts is probably fully disabled rather than just unconfigured,
- // so handle this case. This block can be removed once we remove support
- // for fxAccounts being disabled.
- } else if (Weave.Status.service == Weave.CLIENT_NOT_CONFIGURED ||
- Weave.Svc.Prefs.get("firstSync", "") == "notReady") {
- this.page = PAGE_NO_ACCOUNT;
-diff --git a/browser/components/preferences/sync.xul b/browser/components/preferences/sync.xul
---- a/browser/components/preferences/sync.xul
-+++ b/browser/components/preferences/sync.xul
-@@ -260,30 +260,19 @@
- <hbox id="fxaSyncEngines">
- <vbox>
- <checkbox label="&engine.tabs.label;"
- accesskey="&engine.tabs.accesskey;"
- preference="engine.tabs"/>
- <checkbox label="&engine.bookmarks.label;"
- accesskey="&engine.bookmarks.accesskey;"
- preference="engine.bookmarks"/>
-- <hbox>
-- <checkbox id="fxa-pweng-chk"
-- label="&engine.passwords.label;"
-- accesskey="&engine.passwords.accesskey;"
-- preference="engine.passwords"/>
--
-- <vbox id="fxa-pweng-help">
-- <spacer flex="1"/>
-- <hbox id="fxa-pweng-help-link">
-- <image onclick="gSyncUtils.openMPInfoPage(event);" />
-- </hbox>
-- <spacer flex="1"/>
-- </vbox>
-- </hbox>
-+ <checkbox label="&engine.passwords.label;"
-+ accesskey="&engine.passwords.accesskey;"
-+ preference="engine.passwords"/>
- <checkbox label="&engine.history.label;"
- accesskey="&engine.history.accesskey;"
- preference="engine.history"/>
- <checkbox label="&engine.addons.label;"
- accesskey="&engine.addons.accesskey;"
- preference="engine.addons"/>
- <checkbox label="&engine.prefs.label;"
- accesskey="&engine.prefs.accesskey;"
-diff --git a/browser/themes/linux/preferences/preferences.css b/browser/themes/linux/preferences/preferences.css
---- a/browser/themes/linux/preferences/preferences.css
-+++ b/browser/themes/linux/preferences/preferences.css
-@@ -166,17 +166,9 @@ label.small {
- margin: 5px;
- line-height: 1.2em;
- }
-
- #noFxaAccount > label:first-child {
- margin-bottom: 0.6em;
- }
-
--#fxa-pweng-help-link > label {
-- margin: 0;
--}
--
--#fxa-pweng-help-link > image {
-- list-style-image: url("chrome://global/skin/icons/question-16.png");
--}
--
- %endif
-diff --git a/browser/themes/osx/preferences/preferences.css b/browser/themes/osx/preferences/preferences.css
---- a/browser/themes/osx/preferences/preferences.css
-+++ b/browser/themes/osx/preferences/preferences.css
-@@ -228,25 +228,9 @@ html|a.inline-link:-moz-focusring {
- margin: 12px 4px;
- line-height: 1.2em;
- }
-
- #noFxaAccount > label:first-child {
- margin-bottom: 0.6em;
- }
-
--#fxa-pweng-help-link > label {
-- margin: 0;
--}
--
--#fxa-pweng-help-link > image {
-- width: 16px;
-- height: 16px;
-- list-style-image: url("chrome://global/skin/icons/question-16.png");
--}
--
--@media (min-resolution: 2dppx) {
-- #fxa-pweng-help-link > image {
-- list-style-image: url("chrome://global/skin/icons/question-32.png");
-- }
--}
--
- %endif
-diff --git a/browser/themes/windows/preferences/preferences.css b/browser/themes/windows/preferences/preferences.css
---- a/browser/themes/windows/preferences/preferences.css
-+++ b/browser/themes/windows/preferences/preferences.css
-@@ -156,17 +156,9 @@ label.small {
- margin: 6px;
- line-height: 1.2em;
- }
-
- #noFxaAccount > label:first-child {
- margin-bottom: 0.6em;
- }
-
--#fxa-pweng-help-link > label {
-- margin: 0;
--}
--
--#fxa-pweng-help-link > image {
-- list-style-image: url("chrome://global/skin/icons/question-16.png");
--}
--
- %endif
-diff --git a/services/sync/Weave.js b/services/sync/Weave.js
---- a/services/sync/Weave.js
-+++ b/services/sync/Weave.js
-@@ -104,26 +104,16 @@ WeaveService.prototype = {
- let username = Services.prefs.getCharPref(SYNC_PREFS_BRANCH + "username");
- return !username || username.contains('@');
- } catch (_) {
- return true; // No username == only allow FxA to be configured.
- }
- },
-
- /**
-- * Returns whether the password engine is allowed. We explicitly disallow
-- * the password engine when a master password is used to ensure those can't
-- * be accessed without the master key.
-- */
-- get allowPasswordsEngine() {
-- // This doesn't apply to old-style sync, it's only an issue for FxA.
-- return !this.fxAccountsEnabled || !Utils.mpEnabled();
-- },
--
-- /**
- * Whether Sync appears to be enabled.
- *
- * This returns true if all the Sync preferences for storing account
- * and server configuration are populated.
- *
- * It does *not* perform a robust check to see if the client is working.
- * For that, you'll want to check Weave.Status.checkSetup().
- */
-diff --git a/services/sync/modules/engines/passwords.js b/services/sync/modules/engines/passwords.js
---- a/services/sync/modules/engines/passwords.js
-+++ b/services/sync/modules/engines/passwords.js
-@@ -31,38 +31,16 @@ this.PasswordEngine = function PasswordE
- }
- PasswordEngine.prototype = {
- __proto__: SyncEngine.prototype,
- _storeObj: PasswordStore,
- _trackerObj: PasswordTracker,
- _recordObj: LoginRec,
- applyIncomingBatchSize: PASSWORDS_STORE_BATCH_SIZE,
-
-- get isAllowed() {
-- return Cc["@mozilla.org/weave/service;1"]
-- .getService(Ci.nsISupports)
-- .wrappedJSObject
-- .allowPasswordsEngine;
-- },
--
-- get enabled() {
-- // If we are disabled due to !isAllowed(), we must take care to ensure the
-- // engine has actually had the enabled setter called which reflects this state.
-- let prefVal = SyncEngine.prototype.__lookupGetter__("enabled").call(this);
-- let newVal = this.isAllowed && prefVal;
-- if (newVal != prefVal) {
-- this.enabled = newVal;
-- }
-- return newVal;
-- },
--
-- set enabled(val) {
-- SyncEngine.prototype.__lookupSetter__("enabled").call(this, this.isAllowed && val);
-- },
--
- _syncFinish: function _syncFinish() {
- SyncEngine.prototype._syncFinish.call(this);
-
- // Delete the weave credentials from the server once
- if (!Svc.Prefs.get("deletePwdFxA", false)) {
- try {
- let ids = [];
- for (let host of Utils.getSyncCredentialsHosts()) {
-diff --git a/services/sync/tests/unit/test_password_mpenabled.js b/services/sync/tests/unit/test_password_mpenabled.js
-deleted file mode 100644
---- a/services/sync/tests/unit/test_password_mpenabled.js
-+++ /dev/null
-@@ -1,137 +0,0 @@
--/* Any copyright is dedicated to the Public Domain.
-- http://creativecommons.org/publicdomain/zero/1.0/ */
--
--Cu.import("resource://gre/modules/Log.jsm");
--Cu.import("resource://services-sync/stages/enginesync.js");
--Cu.import("resource://services-sync/util.js");
--Cu.import("resource://services-sync/engines/passwords.js");
--Cu.import("resource://services-sync/service.js");
--Cu.import("resource://testing-common/services/sync/utils.js");
--
--function run_test() {
-- initTestLogging("Trace");
-- run_next_test();
--}
--
--add_test(function test_simple() {
-- ensureLegacyIdentityManager();
-- // Stub fxAccountsEnabled
-- let xpcs = Cc["@mozilla.org/weave/service;1"]
-- .getService(Components.interfaces.nsISupports)
-- .wrappedJSObject;
-- let fxaEnabledGetter = xpcs.__lookupGetter__("fxAccountsEnabled");
-- xpcs.__defineGetter__("fxAccountsEnabled", () => true);
--
-- // Stub mpEnabled.
-- let mpEnabledF = Utils.mpEnabled;
-- let mpEnabled = false;
-- Utils.mpEnabled = function() mpEnabled;
--
-- let manager = Service.engineManager;
--
-- Service.engineManager.register(PasswordEngine);
-- let engine = Service.engineManager.get("passwords");
-- let wipeCount = 0;
-- let engineWipeServerF = engine.wipeServer;
-- engine.wipeServer = function() {
-- ++wipeCount;
-- }
--
-- // A server for the metadata.
-- let server = new SyncServer();
-- let johndoe = server.registerUser("johndoe", "password");
-- johndoe.createContents({
-- meta: {global: {engines: {passwords: {version: engine.version,
-- syncID: engine.syncID}}}},
-- crypto: {},
-- clients: {}
-- });
-- server.start();
-- setBasicCredentials("johndoe", "password", "abcdeabcdeabcdeabcdeabcdea");
-- Service.serverURL = server.baseURI;
-- Service.clusterURL = server.baseURI;
--
-- let engineSync = new EngineSynchronizer(Service);
-- engineSync._log.level = Log.Level.Trace;
--
-- function assertEnabled(expected, message) {
-- Assert.strictEqual(engine.enabled, expected, message);
-- // The preference *must* reflect the actual state.
-- Assert.strictEqual(Svc.Prefs.get("engine." + engine.prefName), expected,
-- message + " (pref should match enabled state)");
-- }
--
-- try {
-- assertEnabled(true, "password engine should be enabled by default")
-- let engineMeta = Service.recordManager.get(engine.metaURL);
-- // This engine should be in the meta/global
-- Assert.notStrictEqual(engineMeta.payload.engines[engine.name], undefined,
-- "The engine should appear in the metadata");
-- Assert.ok(!engineMeta.changed, "the metadata for the password engine hasn't changed");
--
-- // (pretend to) enable a master-password
-- mpEnabled = true;
-- // The password engine should be locally disabled...
-- assertEnabled(false, "if mp is locked the engine should be disabled");
-- // ...but not declined.
-- Assert.ok(!manager.isDeclined("passwords"), "password engine is not declined");
-- // Next time a sync would happen, we call _updateEnabledEngines(), which
-- // would remove the engine from the metadata - call that now.
-- engineSync._updateEnabledEngines();
-- // The global meta should no longer list the engine.
-- engineMeta = Service.recordManager.get(engine.metaURL);
-- Assert.strictEqual(engineMeta.payload.engines[engine.name], undefined,
-- "The engine should have vanished");
-- // And we should have wiped the server data.
-- Assert.strictEqual(wipeCount, 1, "wipeServer should have been called");
--
-- // Now simulate an incoming meta/global indicating the engine should be
-- // enabled. We should fail to actually enable it - the pref should remain
-- // false and we wipe the server for anything another device might have
-- // stored.
-- let meta = {
-- payload: {
-- engines: {
-- "passwords": {"version":1,"syncID":"yfBi2v7PpFO2"},
-- },
-- },
-- };
-- engineSync._updateEnabledFromMeta(meta, 3, manager);
-- Assert.strictEqual(wipeCount, 2, "wipeServer should have been called");
-- Assert.ok(!manager.isDeclined("passwords"), "password engine is not declined");
-- assertEnabled(false, "engine still not enabled locally");
--
-- // Let's turn the MP off - but *not* re-enable it locally.
-- mpEnabled = false;
-- // Just disabling the MP isn't enough to force it back to enabled.
-- assertEnabled(false, "engine still not enabled locally");
-- // Another incoming metadata record with the engine enabled should cause
-- // it to be enabled locally.
-- meta = {
-- payload: {
-- engines: {
-- "passwords": 1,
-- },
-- },
-- };
-- engineSync._updateEnabledFromMeta(meta, 3, manager);
-- Assert.strictEqual(wipeCount, 2, "wipeServer should *not* have been called again");
-- Assert.ok(!manager.isDeclined("passwords"), "password engine is not declined");
-- // It should be enabled locally.
-- assertEnabled(true, "engine now enabled locally");
-- // Next time a sync starts it should magically re-appear in our meta/global
-- engine._syncStartup();
-- //engineSync._updateEnabledEngines();
-- engineMeta = Service.recordManager.get(engine.metaURL);
-- Assert.equal(engineMeta.payload.engines[engine.name].version, engine.version,
-- "The engine should re-appear in the metadata");
-- } finally {
-- // restore the damage we did above...
-- engine.wipeServer = engineWipeServerF;
-- engine._store.wipe();
-- // Un-stub mpEnabled and fxAccountsEnabled
-- Utils.mpEnabled = mpEnabledF;
-- xpcs.__defineGetter__("fxAccountsEnabled", fxaEnabledGetter);
-- server.stop(run_next_test);
-- }
--});
-diff --git a/services/sync/tests/unit/xpcshell.ini b/services/sync/tests/unit/xpcshell.ini
---- a/services/sync/tests/unit/xpcshell.ini
-+++ b/services/sync/tests/unit/xpcshell.ini
-@@ -164,10 +164,8 @@ skip-if = debug
- [test_prefs_store.js]
- [test_prefs_tracker.js]
- [test_tab_engine.js]
- [test_tab_store.js]
- [test_tab_tracker.js]
-
- [test_healthreport.js]
- skip-if = ! healthreport
--
--[test_password_mpenabled.js]
diff --git a/data/patches/disable/0001-upgrade-mobile-fxa-to-v32.patch b/data/patches/disable/0001-upgrade-mobile-fxa-to-v32.patch
deleted file mode 100644
index e420727..0000000
--- a/data/patches/disable/0001-upgrade-mobile-fxa-to-v32.patch
+++ /dev/null
@@ -1,992 +0,0 @@
-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',
diff --git a/data/patches/disable/0002-update-mobile-sqlite-to-v35.patch b/data/patches/disable/0002-update-mobile-sqlite-to-v35.patch
deleted file mode 100644
index 9a99ea2..0000000
--- a/data/patches/disable/0002-update-mobile-sqlite-to-v35.patch
+++ /dev/null
@@ -1,246 +0,0 @@
-diff -ru mobile/android/base/sqlite/ByteBufferInputStream.java /tmp/mozilla-release/mobile/android/base/sqlite/ByteBufferInputStream.java
---- a/mobile/android/base/sqlite/ByteBufferInputStream.java 2015-02-23 22:40:38.708934982 +0100
-+++ b/mobile/android/base/sqlite/ByteBufferInputStream.java 2015-01-23 07:00:04.000000000 +0100
-@@ -14,7 +14,7 @@
- * easier to use.
- */
- public class ByteBufferInputStream extends InputStream {
-- private ByteBuffer mByteBuffer;
-+ private final ByteBuffer mByteBuffer;
-
- public ByteBufferInputStream(ByteBuffer aByteBuffer) {
- mByteBuffer = aByteBuffer;
-diff -ru mobile/android/base/sqlite/MatrixBlobCursor.java /tmp/mozilla-release/mobile/android/base/sqlite/MatrixBlobCursor.java
---- a/mobile/android/base/sqlite/MatrixBlobCursor.java 2015-02-23 22:40:38.720934982 +0100
-+++ b/mobile/android/base/sqlite/MatrixBlobCursor.java 2015-01-23 07:00:04.000000000 +0100
-@@ -17,34 +17,37 @@
-
- package org.mozilla.gecko.sqlite;
-
-+import java.nio.ByteBuffer;
-+import java.util.ArrayList;
-+
-+import org.mozilla.gecko.AppConstants;
- import org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI;
-
- import android.database.AbstractCursor;
- import android.database.CursorIndexOutOfBoundsException;
-+import android.util.Log;
-
--import java.nio.ByteBuffer;
--import java.util.ArrayList;
--
--/*
-- * Android's AbstractCursor throws on getBlob()
-- * and MatrixCursor forgot to override it. This was fixed
-- * at some point but old devices are still SOL.
-- * Oh, and everything in MatrixCursor is private instead of
-- * protected, so we need to entirely duplicate it here,
-- * instad of just being able to add the missing method.
-- */
- /**
- * A mutable cursor implementation backed by an array of {@code Object}s. Use
- * {@link #newRow()} to add rows. Automatically expands internal capacity
- * as needed.
-+ *
-+ * This class provides one missing feature from Android's MatrixCursor:
-+ * the implementation of getBlob that was inadvertently omitted from API 9 (and
-+ * perhaps later; it's present in 14).
-+ *
-+ * MatrixCursor is all private, so we entirely duplicate it here.
- */
- public class MatrixBlobCursor extends AbstractCursor {
-+ private static final String LOGTAG = "GeckoMatrixCursor";
-
- private final String[] columnNames;
-- private Object[] data;
-- private int rowCount = 0;
- private final int columnCount;
-
-+ private int rowCount;
-+
-+ Object[] data;
-+
- /**
- * Constructs a new cursor with the given initial capacity.
- *
-@@ -140,17 +143,18 @@
- */
- @WrapElementForJNI
- public void addRow(Iterable<?> columnValues) {
-- int start = rowCount * columnCount;
-- int end = start + columnCount;
-- ensureCapacity(end);
-+ final int start = rowCount * columnCount;
-
- if (columnValues instanceof ArrayList<?>) {
- addRow((ArrayList<?>) columnValues, start);
- return;
- }
-
-+ final int end = start + columnCount;
- int current = start;
-- Object[] localData = data;
-+
-+ ensureCapacity(end);
-+ final Object[] localData = data;
- for (Object columnValue : columnValues) {
- if (current == end) {
- // TODO: null out row?
-@@ -173,39 +177,47 @@
- /** Optimization for {@link ArrayList}. */
- @WrapElementForJNI
- private void addRow(ArrayList<?> columnValues, int start) {
-- int size = columnValues.size();
-+ final int size = columnValues.size();
- if (size != columnCount) {
- throw new IllegalArgumentException("columnNames.length = "
- + columnCount + ", columnValues.size() = " + size);
- }
-
-- rowCount++;
-- Object[] localData = data;
-+ final int end = start + columnCount;
-+ ensureCapacity(end);
-+
-+ // Take a reference just in case someone calls ensureCapacity
-+ // and `data` gets replaced by a new array!
-+ final Object[] localData = data;
- for (int i = 0; i < size; i++) {
- localData[start + i] = columnValues.get(i);
- }
-+
-+ rowCount++;
- }
-
-- /** Ensures that this cursor has enough capacity. */
-- private void ensureCapacity(int size) {
-- if (size > data.length) {
-- Object[] oldData = this.data;
-- int newSize = data.length * 2;
-- if (newSize < size) {
-- newSize = size;
-- }
-- this.data = new Object[newSize];
-- System.arraycopy(oldData, 0, this.data, 0, oldData.length);
-+ /**
-+ * Ensures that this cursor has enough capacity. If it needs to allocate
-+ * a new array, the existing capacity will be at least doubled.
-+ */
-+ private void ensureCapacity(final int size) {
-+ if (size <= data.length) {
-+ return;
- }
-+
-+ final Object[] oldData = this.data;
-+ this.data = new Object[Math.max(size, data.length * 2)];
-+ System.arraycopy(oldData, 0, this.data, 0, oldData.length);
- }
-
- /**
- * Builds a row, starting from the left-most column and adding one column
- * value at a time. Follows the same ordering as the column names specified
- * at cursor construction time.
-+ *
-+ * Not thread-safe.
- */
- public class RowBuilder {
--
- private int index;
- private final int endIndex;
-
-@@ -221,10 +233,9 @@
- * values
- * @return this builder to support chaining
- */
-- public RowBuilder add(Object columnValue) {
-+ public RowBuilder add(final Object columnValue) {
- if (index == endIndex) {
-- throw new CursorIndexOutOfBoundsException(
-- "No more columns left.");
-+ throw new CursorIndexOutOfBoundsException("No more columns left.");
- }
-
- data[index++] = columnValue;
-@@ -232,6 +243,9 @@
- }
- }
-
-+ /**
-+ * Not thread safe.
-+ */
- public void set(int column, Object value) {
- if (column < 0 || column >= columnCount) {
- throw new CursorIndexOutOfBoundsException("Requested column: "
-@@ -266,7 +280,7 @@
-
- @Override
- public short getShort(int column) {
-- Object value = get(column);
-+ final Object value = get(column);
- if (value == null) return 0;
- if (value instanceof Number) return ((Number) value).shortValue();
- return Short.parseShort(value.toString());
-@@ -311,10 +325,11 @@
- if (value instanceof byte[]) {
- return (byte[]) value;
- }
-+
- if (value instanceof ByteBuffer) {
-- ByteBuffer data = (ByteBuffer)value;
-- byte[] byteArray = new byte[data.remaining()];
-- data.get(byteArray);
-+ final ByteBuffer bytes = (ByteBuffer) value;
-+ byte[] byteArray = new byte[bytes.remaining()];
-+ bytes.get(byteArray);
- return byteArray;
- }
- throw new UnsupportedOperationException("BLOB Object not of known type");
-@@ -324,4 +339,15 @@
- public boolean isNull(int column) {
- return get(column) == null;
- }
-+
-+ @Override
-+ protected void finalize() {
-+ if (AppConstants.DEBUG_BUILD) {
-+ if (!isClosed()) {
-+ Log.e(LOGTAG, "Cursor finalized without being closed", new RuntimeException("stack"));
-+ }
-+ }
-+
-+ super.finalize();
-+ }
- }
-diff -ru mobile/android/base/sqlite/SQLiteBridge.java /tmp/mozilla-release/mobile/android/base/sqlite/SQLiteBridge.java
---- a/mobile/android/base/sqlite/SQLiteBridge.java 2015-02-23 22:40:38.716934982 +0100
-+++ b/mobile/android/base/sqlite/SQLiteBridge.java 2015-01-23 07:00:04.000000000 +0100
-@@ -26,16 +26,16 @@
- private static final String LOGTAG = "SQLiteBridge";
-
- // Path to the database. If this database was not opened with openDatabase, we reopen it every query.
-- private String mDb;
-+ private final String mDb;
-
- // Pointer to the database if it was opened with openDatabase. 0 implies closed.
-- protected volatile long mDbPointer = 0L;
-+ protected volatile long mDbPointer;
-
- // Values remembered after a query.
- private long[] mQueryResults;
-
-- private boolean mTransactionSuccess = false;
-- private boolean mInTransaction = false;
-+ private boolean mTransactionSuccess;
-+ private boolean mInTransaction;
-
- private static final int RESULT_INSERT_ROW_ID = 0;
- private static final int RESULT_ROWS_CHANGED = 1;
-@@ -227,6 +227,7 @@
- cursor.moveToFirst();
- String version = cursor.getString(0);
- ret = Integer.parseInt(version);
-+ cursor.close();
- }
- return ret;
- }
diff --git a/data/patches/disable/0003-upgrade-mobile-sync-to-v32.patch b/data/patches/disable/0003-upgrade-mobile-sync-to-v32.patch
deleted file mode 100644
index e420727..0000000
--- a/data/patches/disable/0003-upgrade-mobile-sync-to-v32.patch
+++ /dev/null
@@ -1,992 +0,0 @@
-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',