From 717dbae2661a93548cde0ddfc9146784f29a89c1 Mon Sep 17 00:00:00 2001 From: Ruben Rodriguez Date: Mon, 9 Mar 2015 21:09:15 +0100 Subject: Removed extra patches --- data/buildscripts/toolchain-mac | 41 + data/buildscripts/toolchain-macos.sh | 41 - ...-part-1-prompt-for-master-password-unlock.patch | 60 -- ...-part-2-Store-sensitive-FxA-credentials-i.patch | 551 ------------ ...-part-3-only-migrate-data-into-the-loginm.patch | 194 ---- ...part-4b-browserid_identity-and-sync-chang.patch | 286 ------ ...-part-5-stop-disabling-the-password-engin.patch | 519 ----------- .../disable/0001-upgrade-mobile-fxa-to-v32.patch | 992 --------------------- .../disable/0002-update-mobile-sqlite-to-v35.patch | 246 ----- .../disable/0003-upgrade-mobile-sync-to-v32.patch | 992 --------------------- 10 files changed, 41 insertions(+), 3881 deletions(-) create mode 100644 data/buildscripts/toolchain-mac delete mode 100644 data/buildscripts/toolchain-macos.sh delete mode 100644 data/patches/0001-Bug-1013064-part-1-prompt-for-master-password-unlock.patch delete mode 100644 data/patches/0002-Bug-1013064-part-2-Store-sensitive-FxA-credentials-i.patch delete mode 100644 data/patches/0003-Bug-1013064-part-3-only-migrate-data-into-the-loginm.patch delete mode 100644 data/patches/0004-Bug-1013064-part-4b-browserid_identity-and-sync-chang.patch delete mode 100644 data/patches/0005-Bug-1013064-part-5-stop-disabling-the-password-engin.patch delete mode 100644 data/patches/disable/0001-upgrade-mobile-fxa-to-v32.patch delete mode 100644 data/patches/disable/0002-update-mobile-sqlite-to-v35.patch delete mode 100644 data/patches/disable/0003-upgrade-mobile-sync-to-v32.patch diff --git a/data/buildscripts/toolchain-mac b/data/buildscripts/toolchain-mac new file mode 100644 index 0000000..20cb31c --- /dev/null +++ b/data/buildscripts/toolchain-mac @@ -0,0 +1,41 @@ +set -e + +WD=macos-build +apt-get -q -y --force-yes install cmake rsync zlib1g-dev libssl-dev mkisofs + +rm -rf $WD +mkdir $WD +cd $WD +#wget http://ppa.launchpad.net/flosoft/cross-apple/ubuntu/pool/main/a/apple-uni-sdk-10.6/apple-uni-sdk-10.6_20110407-0.flosoft1_i386.deb +wget http://ppa.launchpad.net/flosoft/cross-apple/ubuntu/pool/main/a/apple-uni-sdk-10.6/apple-uni-sdk-10.6_20110407-0.flosoft1_amd64.deb +dpkg -i *.deb + +wget https://mingw-and-ndk.googlecode.com/files/multiarch-darwin11-cctools127.2-gcc42-5666.3-llvmgcc42-2336.1-Linux-120724.tar.xz +unp multiarch-darwin* +rm apple-osx/man -rf +cp apple-osx/* /usr/local/ -a +# For OpenSSL +ln -sf /usr/local/bin/apple-osx-gcc /usr/local/bin/i686-apple-darwin11-cc +#For gmp, need to trick it so it knows we're doing a 64 bit build +for i in /usr/local/bin/i686-apple-darwin11-*; do j=`echo $i | sed 's/i686/x86_64/'`; ln -fs $i $j; done; + +wget https://people.torproject.org/~mikeperry/mirrors/sources/x86_64-apple-darwin10.tar.xz +unp x86_64-apple-darwin10.tar.xz +cp x-tools/x86_64-apple-darwin10/* /usr/local/ -a +# FIXME: path hacks: +mkdir -p /System/Library/ || true +ln -fs /usr/lib/apple/SDKs/MacOSX10.6.sdk /usr/lib/apple/SDKs/MacOSX10.5.sdk +ln -fs /usr/lib/apple/SDKs/MacOSX10.6.sdk /usr/lib/apple/SDKs/MacOSX10.5.sdk +ln -fs /usr/lib/apple/SDKs/MacOSX10.6.sdk/System/Library/Frameworks/ /System/Library/ +ln -fs /usr/lib/apple/SDKs/MacOSX10.6.sdk/System/Library/PrivateFrameworks/ /System/Library/ +ln -fs /usr/lib/apple/SDKs/MacOSX10.6.sdk/usr/lib/libstdc++.6.dylib /usr/lib/apple/SDKs/MacOSX10.6.sdk/usr/lib/libstdc++.dylib + +git clone https://git.torproject.org/builders/tor-browser-bundle.git +git clone https://github.com/vasi/libdmg-hfsplus.git +cd libdmg-hfsplus +git checkout dfd5e5cc3dc1191e37d3c3a6118975afdd1d7014 +git am ../tor-browser-bundle/gitian/patches/libdmg.patch +cmake CMakeLists.txt +cd dmg +make + diff --git a/data/buildscripts/toolchain-macos.sh b/data/buildscripts/toolchain-macos.sh deleted file mode 100644 index 20cb31c..0000000 --- a/data/buildscripts/toolchain-macos.sh +++ /dev/null @@ -1,41 +0,0 @@ -set -e - -WD=macos-build -apt-get -q -y --force-yes install cmake rsync zlib1g-dev libssl-dev mkisofs - -rm -rf $WD -mkdir $WD -cd $WD -#wget http://ppa.launchpad.net/flosoft/cross-apple/ubuntu/pool/main/a/apple-uni-sdk-10.6/apple-uni-sdk-10.6_20110407-0.flosoft1_i386.deb -wget http://ppa.launchpad.net/flosoft/cross-apple/ubuntu/pool/main/a/apple-uni-sdk-10.6/apple-uni-sdk-10.6_20110407-0.flosoft1_amd64.deb -dpkg -i *.deb - -wget https://mingw-and-ndk.googlecode.com/files/multiarch-darwin11-cctools127.2-gcc42-5666.3-llvmgcc42-2336.1-Linux-120724.tar.xz -unp multiarch-darwin* -rm apple-osx/man -rf -cp apple-osx/* /usr/local/ -a -# For OpenSSL -ln -sf /usr/local/bin/apple-osx-gcc /usr/local/bin/i686-apple-darwin11-cc -#For gmp, need to trick it so it knows we're doing a 64 bit build -for i in /usr/local/bin/i686-apple-darwin11-*; do j=`echo $i | sed 's/i686/x86_64/'`; ln -fs $i $j; done; - -wget https://people.torproject.org/~mikeperry/mirrors/sources/x86_64-apple-darwin10.tar.xz -unp x86_64-apple-darwin10.tar.xz -cp x-tools/x86_64-apple-darwin10/* /usr/local/ -a -# FIXME: path hacks: -mkdir -p /System/Library/ || true -ln -fs /usr/lib/apple/SDKs/MacOSX10.6.sdk /usr/lib/apple/SDKs/MacOSX10.5.sdk -ln -fs /usr/lib/apple/SDKs/MacOSX10.6.sdk /usr/lib/apple/SDKs/MacOSX10.5.sdk -ln -fs /usr/lib/apple/SDKs/MacOSX10.6.sdk/System/Library/Frameworks/ /System/Library/ -ln -fs /usr/lib/apple/SDKs/MacOSX10.6.sdk/System/Library/PrivateFrameworks/ /System/Library/ -ln -fs /usr/lib/apple/SDKs/MacOSX10.6.sdk/usr/lib/libstdc++.6.dylib /usr/lib/apple/SDKs/MacOSX10.6.sdk/usr/lib/libstdc++.dylib - -git clone https://git.torproject.org/builders/tor-browser-bundle.git -git clone https://github.com/vasi/libdmg-hfsplus.git -cd libdmg-hfsplus -git checkout dfd5e5cc3dc1191e37d3c3a6118975afdd1d7014 -git am ../tor-browser-bundle/gitian/patches/libdmg.patch -cmake CMakeLists.txt -cd dmg -make - 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 -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 -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 -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 -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 -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 @@ - - - - -- - - -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 @@ - - - - -- -- -- -- -- -- -- -- -- -- -- -+ - - - - - - -- -- -- -- -- -- -- -- -- -- -- -+ - - - 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 null 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 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 { -- protected static final String LOG_TAG = FxAccountResendCodeTask.class.getSimpleName(); -- -- protected final byte[] sessionToken; -- -- public FxAccountResendCodeTask(Context context, byte[] sessionToken, FxAccountClient client, RequestDelegate delegate) { -- super(context, null, client, delegate); -- this.sessionToken = sessionToken; -- } -- -- @Override -- protected InnerRequestDelegate 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 { -- 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 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 AsyncTask wrapper around signing up for, and signing in to, a -- * Firefox Account. -- *

-- * 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 extends AsyncTask> { -- 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 innerDelegate = new InnerRequestDelegate(latch); -- -- protected final RequestDelegate delegate; -- -- public FxAccountSetupTask(Context context, ProgressDisplay progressDisplay, FxAccountClient client, RequestDelegate 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 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 result) { -- if (progressDisplay != null) { -- progressDisplay.dismissProgress(); -- } -- delegate.handleError(new IllegalStateException("Task was cancelled.")); -- } -- -- protected static class InnerRequestDelegate implements RequestDelegate { -- 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 { -- 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 delegate) throws UnsupportedEncodingException { -- super(context, progressDisplay, client, delegate); -- this.emailUTF8 = email.getBytes("UTF-8"); -- this.passwordStretcher = passwordStretcher; -- } -- -- @Override -- protected InnerRequestDelegate 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 { -- 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 delegate) throws UnsupportedEncodingException { -- super(context, progressDisplay, client, delegate); -- this.emailUTF8 = email.getBytes("UTF-8"); -- this.passwordStretcher = passwordStretcher; -- } -- -- @Override -- protected InnerRequestDelegate 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 { -+ protected static final String LOG_TAG = FxAccountResendCodeTask.class.getSimpleName(); -+ -+ protected final byte[] sessionToken; -+ -+ public FxAccountResendCodeTask(Context context, byte[] sessionToken, FxAccountClient client, RequestDelegate delegate) { -+ super(context, null, client, delegate); -+ this.sessionToken = sessionToken; -+ } -+ -+ @Override -+ protected InnerRequestDelegate 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 { -+ 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 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 { -+ 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 delegate) throws UnsupportedEncodingException { -+ super(context, progressDisplay, client, delegate); -+ this.emailUTF8 = email.getBytes("UTF-8"); -+ this.passwordStretcher = passwordStretcher; -+ } -+ -+ @Override -+ protected InnerRequestDelegate 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 AsyncTask wrapper around signing up for, and signing in to, a -+ * Firefox Account. -+ *

-+ * 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 extends AsyncTask> { -+ 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 innerDelegate = new InnerRequestDelegate(latch); -+ -+ protected final RequestDelegate delegate; -+ -+ public FxAccountSetupTask(Context context, ProgressDisplay progressDisplay, FxAccountClient client, RequestDelegate 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 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 result) { -+ if (progressDisplay != null) { -+ progressDisplay.dismissProgress(); -+ } -+ delegate.handleError(new IllegalStateException("Task was cancelled.")); -+ } -+ -+ protected static class InnerRequestDelegate implements RequestDelegate { -+ 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 { -+ 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 delegate) throws UnsupportedEncodingException { -+ super(context, progressDisplay, client, delegate); -+ this.emailUTF8 = email.getBytes("UTF-8"); -+ this.passwordStretcher = passwordStretcher; -+ } -+ -+ @Override -+ protected InnerRequestDelegate 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 null 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 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 { -- protected static final String LOG_TAG = FxAccountResendCodeTask.class.getSimpleName(); -- -- protected final byte[] sessionToken; -- -- public FxAccountResendCodeTask(Context context, byte[] sessionToken, FxAccountClient client, RequestDelegate delegate) { -- super(context, null, client, delegate); -- this.sessionToken = sessionToken; -- } -- -- @Override -- protected InnerRequestDelegate 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 { -- 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 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 AsyncTask wrapper around signing up for, and signing in to, a -- * Firefox Account. -- *

-- * 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 extends AsyncTask> { -- 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 innerDelegate = new InnerRequestDelegate(latch); -- -- protected final RequestDelegate delegate; -- -- public FxAccountSetupTask(Context context, ProgressDisplay progressDisplay, FxAccountClient client, RequestDelegate 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 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 result) { -- if (progressDisplay != null) { -- progressDisplay.dismissProgress(); -- } -- delegate.handleError(new IllegalStateException("Task was cancelled.")); -- } -- -- protected static class InnerRequestDelegate implements RequestDelegate { -- 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 { -- 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 delegate) throws UnsupportedEncodingException { -- super(context, progressDisplay, client, delegate); -- this.emailUTF8 = email.getBytes("UTF-8"); -- this.passwordStretcher = passwordStretcher; -- } -- -- @Override -- protected InnerRequestDelegate 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 { -- 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 delegate) throws UnsupportedEncodingException { -- super(context, progressDisplay, client, delegate); -- this.emailUTF8 = email.getBytes("UTF-8"); -- this.passwordStretcher = passwordStretcher; -- } -- -- @Override -- protected InnerRequestDelegate 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 { -+ protected static final String LOG_TAG = FxAccountResendCodeTask.class.getSimpleName(); -+ -+ protected final byte[] sessionToken; -+ -+ public FxAccountResendCodeTask(Context context, byte[] sessionToken, FxAccountClient client, RequestDelegate delegate) { -+ super(context, null, client, delegate); -+ this.sessionToken = sessionToken; -+ } -+ -+ @Override -+ protected InnerRequestDelegate 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 { -+ 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 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 { -+ 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 delegate) throws UnsupportedEncodingException { -+ super(context, progressDisplay, client, delegate); -+ this.emailUTF8 = email.getBytes("UTF-8"); -+ this.passwordStretcher = passwordStretcher; -+ } -+ -+ @Override -+ protected InnerRequestDelegate 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 AsyncTask wrapper around signing up for, and signing in to, a -+ * Firefox Account. -+ *

-+ * 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 extends AsyncTask> { -+ 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 innerDelegate = new InnerRequestDelegate(latch); -+ -+ protected final RequestDelegate delegate; -+ -+ public FxAccountSetupTask(Context context, ProgressDisplay progressDisplay, FxAccountClient client, RequestDelegate 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 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 result) { -+ if (progressDisplay != null) { -+ progressDisplay.dismissProgress(); -+ } -+ delegate.handleError(new IllegalStateException("Task was cancelled.")); -+ } -+ -+ protected static class InnerRequestDelegate implements RequestDelegate { -+ 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 { -+ 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 delegate) throws UnsupportedEncodingException { -+ super(context, progressDisplay, client, delegate); -+ this.emailUTF8 = email.getBytes("UTF-8"); -+ this.passwordStretcher = passwordStretcher; -+ } -+ -+ @Override -+ protected InnerRequestDelegate 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', -- cgit v1.2.3