diff --git a/mobile/android/base/sync/GlobalSession.java b/mobile/android/base/sync/GlobalSession.java index 8e87a1cd907..856c9d4db1d 100644 --- a/mobile/android/base/sync/GlobalSession.java +++ b/mobile/android/base/sync/GlobalSession.java @@ -190,10 +190,7 @@ public class GlobalSession implements CredentialsSource, PrefsSource, HttpRespon // TODO: more stages. stages.put(Stage.syncTabs, new FennecTabsServerSyncStage()); - - // Disable until Bug 744816 is fixed. - // stages.put(Stage.syncPasswords, new PasswordsServerSyncStage()); - + stages.put(Stage.syncPasswords, new PasswordsServerSyncStage()); stages.put(Stage.syncBookmarks, new AndroidBrowserBookmarksServerSyncStage()); stages.put(Stage.syncHistory, new AndroidBrowserHistoryServerSyncStage()); stages.put(Stage.syncFormHistory, new FormHistoryServerSyncStage()); diff --git a/mobile/android/base/sync/SyncConfiguration.java b/mobile/android/base/sync/SyncConfiguration.java index ee3c4f73730..27edeb19f70 100644 --- a/mobile/android/base/sync/SyncConfiguration.java +++ b/mobile/android/base/sync/SyncConfiguration.java @@ -192,6 +192,8 @@ public class SyncConfiguration implements CredentialsSource { public PrefsSource prefsSource; public static final String CLIENT_RECORD_TIMESTAMP = "serverClientRecordTimestamp"; + public static final String PREF_CLUSTER_URL = "clusterURL"; + public static final String PREF_SYNC_ID = "syncID"; /** * Create a new SyncConfiguration instance. Pass in a PrefsSource to @@ -220,8 +222,8 @@ public class SyncConfiguration implements CredentialsSource { public void loadFromPrefs(SharedPreferences prefs) { - if (prefs.contains("clusterURL")) { - String u = prefs.getString("clusterURL", null); + if (prefs.contains(PREF_CLUSTER_URL)) { + String u = prefs.getString(PREF_CLUSTER_URL, null); try { clusterURL = new URI(u); Logger.info(LOG_TAG, "Set clusterURL from bundle: " + u); @@ -229,8 +231,8 @@ public class SyncConfiguration implements CredentialsSource { Logger.warn(LOG_TAG, "Ignoring bundle clusterURL (" + u + "): invalid URI.", e); } } - if (prefs.contains("syncID")) { - syncID = prefs.getString("syncID", null); + if (prefs.contains(PREF_SYNC_ID)) { + syncID = prefs.getString(PREF_SYNC_ID, null); Logger.info(LOG_TAG, "Set syncID from bundle: " + syncID); } // We don't set crypto/keys here because we need the syncKeyBundle to decrypt the JSON @@ -245,12 +247,12 @@ public class SyncConfiguration implements CredentialsSource { public void persistToPrefs(SharedPreferences prefs) { Editor edit = prefs.edit(); if (clusterURL == null) { - edit.remove("clusterURL"); + edit.remove(PREF_CLUSTER_URL); } else { - edit.putString("clusterURL", clusterURL.toASCIIString()); + edit.putString(PREF_CLUSTER_URL, clusterURL.toASCIIString()); } if (syncID != null) { - edit.putString("syncID", syncID); + edit.putString(PREF_SYNC_ID, syncID); } edit.commit(); // TODO: keys. @@ -339,7 +341,7 @@ public class SyncConfiguration implements CredentialsSource { clusterURL = u; if (shouldPersist) { Editor edit = prefs.edit(); - edit.putString("clusterURL", clusterURL.toASCIIString()); + edit.putString(PREF_CLUSTER_URL, clusterURL.toASCIIString()); edit.commit(); } } diff --git a/mobile/android/base/sync/setup/SyncAccounts.java b/mobile/android/base/sync/setup/SyncAccounts.java index fd7b1c1f5d6..072e29d6b9b 100644 --- a/mobile/android/base/sync/setup/SyncAccounts.java +++ b/mobile/android/base/sync/setup/SyncAccounts.java @@ -6,13 +6,16 @@ package org.mozilla.gecko.sync.setup; import org.mozilla.gecko.db.BrowserContract; import org.mozilla.gecko.sync.Logger; +import org.mozilla.gecko.sync.SyncConfiguration; import org.mozilla.gecko.sync.Utils; +import org.mozilla.gecko.sync.repositories.android.RepoUtils; +import org.mozilla.gecko.sync.syncadapter.SyncAdapter; import android.accounts.Account; import android.accounts.AccountManager; import android.content.ContentResolver; import android.content.Context; -import android.content.Intent; +import android.content.SharedPreferences; import android.os.AsyncTask; import android.os.Bundle; import android.util.Log; @@ -20,27 +23,28 @@ import android.util.Log; /** * This class contains utilities that are of use to Fennec * and Sync setup activities. - * + *
* Do not break these APIs without correcting upstream code! */ public class SyncAccounts { - private final static String DEFAULT_SERVER = "https://auth.services.mozilla.com/"; + public final static String DEFAULT_SERVER = "https://auth.services.mozilla.com/"; private static final String LOG_TAG = "SyncAccounts"; + private static final String GLOBAL_LOG_TAG = "FxSync"; /** * Returns true if a Sync account is set up. - * + *
* Do not call this method from the main thread. */ public static boolean syncAccountsExist(Context c) { - return AccountManager.get(c).getAccountsByType("org.mozilla.firefox_sync").length > 0; + return AccountManager.get(c).getAccountsByType(Constants.ACCOUNTTYPE_SYNC).length > 0; } /** * This class provides background-thread abstracted access to whether a * Firefox Sync account has been set up on this device. - * + *
* Subclass this task and override `onPostExecute` to act on the result.
*/
public static class AccountsExistTask extends AsyncTask
+ * Subclass this task and override `onPostExecute` to act on the result. The
+ *
+ * Do not call this method from the main thread.
+ *
+ * @param syncAccount
+ * The parameters of the account to be created.
+ * @return The created Context
; cannot be null.
+ * @param accountManager
+ * an AccountManager
instance to use; if null, get it
+ * from context
.
+ * @param username
+ * the desired username; cannot be null.
+ * @param syncKey
+ * the desired sync key; cannot be null.
+ * @param password
+ * the desired password; cannot be null.
+ * @param serverURL
+ * the server URL to use; if null, use the default.
+ * @param clusterURL
+ * the cluster URL to use; if null, a fresh cluster URL will be
+ * retrieved from the server during the next sync.
+ * @param clientName
+ * the client name; if null, a fresh client record will be uploaded
+ * to the server during the next sync.
+ * @param clientGuid
+ * the client GUID; if null, a fresh client record will be uploaded
+ * to the server during the next sync.
+ */
+ public SyncAccountParameters(Context context, AccountManager accountManager,
+ String username, String syncKey, String password,
+ String serverURL, String clusterURL,
+ String clientName, String clientGuid) {
+ if (context == null) {
+ throw new IllegalArgumentException("Null context passed to SyncAccountParameters constructor.");
+ }
+ if (username == null) {
+ throw new IllegalArgumentException("Null username passed to SyncAccountParameters constructor.");
+ }
+ if (syncKey == null) {
+ throw new IllegalArgumentException("Null syncKey passed to SyncAccountParameters constructor.");
+ }
+ if (password == null) {
+ throw new IllegalArgumentException("Null password passed to SyncAccountParameters constructor.");
+ }
+ this.context = context;
+ this.accountManager = accountManager;
+ this.username = username;
+ this.syncKey = syncKey;
+ this.password = password;
+ this.serverURL = serverURL;
+ this.clusterURL = clusterURL;
+ this.clientName = clientName;
+ this.clientGuid = clientGuid;
+ }
+
+ public SyncAccountParameters(Context context, AccountManager accountManager,
+ String username, String syncKey, String password, String serverURL) {
+ this(context, accountManager, username, syncKey, password, serverURL, null, null, null);
+ }
+ }
+
+ /**
+ * This class provides background-thread abstracted access to creating a
+ * Firefox Sync account.
+ * Result
(of type Account
) is null if an error
+ * occurred and the account could not be added.
+ */
+ public static class CreateSyncAccountTask extends AsyncTaskAccount
, or null if an error occurred and
+ * the account could not be added.
+ */
+ public static Account createSyncAccount(SyncAccountParameters syncAccount) {
+ final Context context = syncAccount.context;
+ final AccountManager accountManager = (syncAccount.accountManager == null) ?
+ AccountManager.get(syncAccount.context) : syncAccount.accountManager;
+ final String username = syncAccount.username;
+ final String syncKey = syncAccount.syncKey;
+ final String password = syncAccount.password;
+ final String serverURL = (syncAccount.serverURL == null) ?
+ DEFAULT_SERVER : syncAccount.serverURL;
+
+ Logger.debug(LOG_TAG, "Using account manager " + accountManager);
+ if (!RepoUtils.stringsEqual(syncAccount.serverURL, DEFAULT_SERVER)) {
+ Logger.info(LOG_TAG, "Setting explicit server URL: " + serverURL);
+ }
final Account account = new Account(username, Constants.ACCOUNTTYPE_SYNC);
final Bundle userbundle = new Bundle();
// Add sync key and server URL.
userbundle.putString(Constants.OPTION_SYNCKEY, syncKey);
- if (serverURL != null) {
- Logger.info(LOG_TAG, "Setting explicit server URL: " + serverURL);
- userbundle.putString(Constants.OPTION_SERVER, serverURL);
- } else {
- userbundle.putString(Constants.OPTION_SERVER, DEFAULT_SERVER);
- }
+ userbundle.putString(Constants.OPTION_SERVER, serverURL);
Logger.debug(LOG_TAG, "Adding account for " + Constants.ACCOUNTTYPE_SYNC);
boolean result = false;
try {
result = accountManager.addAccountExplicitly(account, password, userbundle);
} catch (SecurityException e) {
+ // We use Log rather than Logger here to avoid possibly hiding these errors.
final String message = e.getMessage();
if (message != null && (message.indexOf("is different than the authenticator's uid") > 0)) {
- Log.wtf("FirefoxSync",
+ Log.wtf(GLOBAL_LOG_TAG,
"Unable to create account. " +
"If you have more than one version of " +
"Firefox/Beta/Aurora/Nightly/Fennec installed, that's why.",
e);
} else {
- Log.e("FirefoxSync", "Unable to create account.", e);
+ Log.e(GLOBAL_LOG_TAG, "Unable to create account.", e);
}
}
- Logger.debug(LOG_TAG, "Account: " + account + " added successfully? " + result);
if (!result) {
- Logger.error(LOG_TAG, "Failed to add account!");
+ Logger.error(LOG_TAG, "Failed to add account " + account + "!");
+ return null;
}
+ Logger.debug(LOG_TAG, "Account " + account + " added successfully.");
- // Set components to sync (default: all).
- ContentResolver.setMasterSyncAutomatically(true);
-
- String authority = BrowserContract.AUTHORITY;
- Logger.debug(LOG_TAG, "Setting authority " + authority + " to sync automatically.");
- ContentResolver.setSyncAutomatically(account, authority, true);
- ContentResolver.setIsSyncable(account, authority, 1);
+ setSyncAutomatically(account);
+ setClientRecord(context, accountManager, account, syncAccount.clientName, syncAccount.clientGuid);
// TODO: add other ContentProviders as needed (e.g. passwords)
// TODO: for each, also add to res/xml to make visible in account settings
@@ -106,15 +217,33 @@ public class SyncAccounts {
// TODO: correctly implement Sync Options.
Logger.info(LOG_TAG, "Clearing preferences for this account.");
try {
- Utils.getSharedPreferences(context, username, serverURL).edit().clear().commit();
+ SharedPreferences.Editor editor = Utils.getSharedPreferences(context, username, serverURL).edit().clear();
+ if (syncAccount.clusterURL != null) {
+ editor.putString(SyncConfiguration.PREF_CLUSTER_URL, syncAccount.clusterURL);
+ }
+ editor.commit();
} catch (Exception e) {
Logger.error(LOG_TAG, "Could not clear prefs path!", e);
}
+ return account;
+ }
- final Intent intent = new Intent();
- intent.putExtra(AccountManager.KEY_ACCOUNT_NAME, username);
- intent.putExtra(AccountManager.KEY_ACCOUNT_TYPE, Constants.ACCOUNTTYPE_SYNC);
- intent.putExtra(AccountManager.KEY_AUTHTOKEN, Constants.ACCOUNTTYPE_SYNC);
- return intent;
+ protected static void setSyncAutomatically(Account account) {
+ ContentResolver.setMasterSyncAutomatically(true);
+ String authority = BrowserContract.AUTHORITY;
+ Logger.debug(LOG_TAG, "Setting authority " + authority + " to sync automatically.");
+ ContentResolver.setSyncAutomatically(account, authority, true);
+ ContentResolver.setIsSyncable(account, authority, 1);
+ }
+
+ protected static void setClientRecord(Context context, AccountManager accountManager, Account account,
+ String clientName, String clientGuid) {
+ if (clientName != null && clientGuid != null) {
+ Logger.debug(LOG_TAG, "Setting client name to " + clientName + " and client GUID to " + clientGuid + ".");
+ SyncAdapter.setAccountGUID(accountManager, account, clientGuid);
+ SyncAdapter.setClientName(accountManager, account, clientName);
+ return;
+ }
+ Logger.debug(LOG_TAG, "Client name and guid not both non-null, so not setting client data.");
}
}
diff --git a/mobile/android/base/sync/setup/activities/AccountActivity.java b/mobile/android/base/sync/setup/activities/AccountActivity.java
index 9d5b4e36299..19b3b2bba4d 100644
--- a/mobile/android/base/sync/setup/activities/AccountActivity.java
+++ b/mobile/android/base/sync/setup/activities/AccountActivity.java
@@ -10,7 +10,9 @@ import org.mozilla.gecko.R;
import org.mozilla.gecko.sync.setup.Constants;
import org.mozilla.gecko.sync.setup.InvalidSyncKeyException;
import org.mozilla.gecko.sync.setup.SyncAccounts;
+import org.mozilla.gecko.sync.setup.SyncAccounts.SyncAccountParameters;
+import android.accounts.Account;
import android.accounts.AccountAuthenticatorActivity;
import android.accounts.AccountManager;
import android.content.Context;
@@ -178,21 +180,34 @@ public class AccountActivity extends AccountAuthenticatorActivity {
private void authCallback() {
// Create and add account to AccountManager
// TODO: only allow one account to be added?
- Log.d(LOG_TAG, "Using account manager " + mAccountManager);
- final Intent intent = SyncAccounts.createAccount(mContext, mAccountManager,
- username,
- key, password, server);
+ final SyncAccountParameters syncAccount = new SyncAccountParameters(mContext, mAccountManager,
+ username, key, password, server);
+ final Account account = SyncAccounts.createSyncAccount(syncAccount);
+ final boolean result = (account != null);
+
+ final Intent intent = new Intent(); // The intent to return.
+ intent.putExtra(AccountManager.KEY_ACCOUNT_NAME, syncAccount.username);
+ intent.putExtra(AccountManager.KEY_ACCOUNT_TYPE, Constants.ACCOUNTTYPE_SYNC);
+ intent.putExtra(AccountManager.KEY_AUTHTOKEN, Constants.ACCOUNTTYPE_SYNC);
setAccountAuthenticatorResult(intent.getExtras());
- // Testing out the authFailure case
- // authFailure();
+ if (!result) {
+ // Failed to add account!
+ setResult(RESULT_CANCELED, intent);
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ authFailure();
+ }
+ });
+ return;
+ }
// TODO: Currently, we do not actually authenticate username/pass against
// Moz sync server.
- // Successful authentication result
+ // Successfully added account.
setResult(RESULT_OK, intent);
-
runOnUiThread(new Runnable() {
@Override
public void run() {
@@ -201,7 +216,6 @@ public class AccountActivity extends AccountAuthenticatorActivity {
});
}
- @SuppressWarnings("unused")
private void authFailure() {
enableCredEntry(true);
Intent intent = new Intent(mContext, SetupFailureActivity.class);
diff --git a/mobile/android/base/sync/setup/activities/SetupSyncActivity.java b/mobile/android/base/sync/setup/activities/SetupSyncActivity.java
index 3745455ad10..01f48b79a93 100644
--- a/mobile/android/base/sync/setup/activities/SetupSyncActivity.java
+++ b/mobile/android/base/sync/setup/activities/SetupSyncActivity.java
@@ -14,6 +14,7 @@ import org.mozilla.gecko.sync.jpake.JPakeClient;
import org.mozilla.gecko.sync.jpake.JPakeNoActivePairingException;
import org.mozilla.gecko.sync.setup.Constants;
import org.mozilla.gecko.sync.setup.SyncAccounts;
+import org.mozilla.gecko.sync.setup.SyncAccounts.SyncAccountParameters;
import android.accounts.Account;
import android.accounts.AccountAuthenticatorActivity;
@@ -356,26 +357,38 @@ public class SetupSyncActivity extends AccountAuthenticatorActivity {
* @param jCreds
*/
public void onComplete(JSONObject jCreds) {
+ boolean result = true;
+
if (!pairWithPin) {
String accountName = (String) jCreds.get(Constants.JSON_KEY_ACCOUNT);
String password = (String) jCreds.get(Constants.JSON_KEY_PASSWORD);
String syncKey = (String) jCreds.get(Constants.JSON_KEY_SYNCKEY);
String serverURL = (String) jCreds.get(Constants.JSON_KEY_SERVER);
- Logger.debug(LOG_TAG, "Using account manager " + mAccountManager);
- final Intent intent = SyncAccounts.createAccount(mContext, mAccountManager,
- accountName,
- syncKey, password, serverURL);
+ final SyncAccountParameters syncAccount = new SyncAccountParameters(mContext, mAccountManager,
+ accountName, syncKey, password, serverURL);
+ final Account account = SyncAccounts.createSyncAccount(syncAccount);
+ result = (account != null);
+
+ final Intent intent = new Intent(); // The intent to return.
+ intent.putExtra(AccountManager.KEY_ACCOUNT_NAME, syncAccount.username);
+ intent.putExtra(AccountManager.KEY_ACCOUNT_TYPE, Constants.ACCOUNTTYPE_SYNC);
+ intent.putExtra(AccountManager.KEY_AUTHTOKEN, Constants.ACCOUNTTYPE_SYNC);
setAccountAuthenticatorResult(intent.getExtras());
- setResult(RESULT_OK, intent);
+ if (result) {
+ setResult(RESULT_OK, intent);
+ } else {
+ setResult(RESULT_CANCELED, intent);
+ }
}
- jClient = null; // Sync is set up. Kill reference to JPakeClient object.
+ jClient = null; // Sync should be set up. Kill reference to JPakeClient object.
+ final boolean res = result;
runOnUiThread(new Runnable() {
@Override
public void run() {
- displayAccount(true);
+ displayResult(res);
}
});
}
@@ -396,16 +409,20 @@ public class SetupSyncActivity extends AccountAuthenticatorActivity {
}
/**
- * Displays Sync account setup completed feedback to user.
+ * Displays Sync account setup result to user.
*
* @param isSetup
- * boolean for whether success screen is reached during setup
- * completion, or otherwise.
+ * true is account was set up successfully, false otherwise.
*/
- private void displayAccount(boolean isSetup) {
- Intent intent = new Intent(mContext, SetupSuccessActivity.class);
+ private void displayResult(boolean isSuccess) {
+ Intent intent = null;
+ if (isSuccess) {
+ intent = new Intent(mContext, SetupSuccessActivity.class);
+ } else {
+ intent = new Intent(mContext, SetupFailureActivity.class);
+ }
intent.setFlags(Constants.FLAG_ACTIVITY_REORDER_TO_FRONT_NO_ANIMATION);
- intent.putExtra(Constants.INTENT_EXTRA_IS_SETUP, isSetup);
+ intent.putExtra(Constants.INTENT_EXTRA_IS_SETUP, !pairWithPin);
startActivity(intent);
finish();
}
diff --git a/mobile/android/base/sync/stage/GlobalSyncStage.java b/mobile/android/base/sync/stage/GlobalSyncStage.java
index a4fc82ed43a..18aa4198330 100644
--- a/mobile/android/base/sync/stage/GlobalSyncStage.java
+++ b/mobile/android/base/sync/stage/GlobalSyncStage.java
@@ -25,7 +25,7 @@ public interface GlobalSyncStage {
updateEnabledEngines,
*/
syncTabs,
- // syncPasswords,
+ syncPasswords,
syncBookmarks,
syncHistory,
syncFormHistory,
diff --git a/mobile/android/base/sync/syncadapter/SyncAdapter.java b/mobile/android/base/sync/syncadapter/SyncAdapter.java
index 56cf87b94d2..094eb74bfdb 100644
--- a/mobile/android/base/sync/syncadapter/SyncAdapter.java
+++ b/mobile/android/base/sync/syncadapter/SyncAdapter.java
@@ -421,21 +421,29 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter implements GlobalSe
if (accountGUID == null) {
Logger.info(LOG_TAG, "Account GUID was null. Creating a new one.");
accountGUID = Utils.generateGuid();
- mAccountManager.setUserData(localAccount, Constants.ACCOUNT_GUID, accountGUID);
+ setAccountGUID(mAccountManager, localAccount, accountGUID);
}
return accountGUID;
}
+ public static void setAccountGUID(AccountManager accountManager, Account account, String accountGUID) {
+ accountManager.setUserData(account, Constants.ACCOUNT_GUID, accountGUID);
+ }
+
@Override
public synchronized String getClientName() {
String clientName = mAccountManager.getUserData(localAccount, Constants.CLIENT_NAME);
if (clientName == null) {
clientName = GlobalConstants.PRODUCT_NAME + " on " + android.os.Build.MODEL;
- mAccountManager.setUserData(localAccount, Constants.CLIENT_NAME, clientName);
+ setClientName(mAccountManager, localAccount, clientName);
}
return clientName;
}
+ public static void setClientName(AccountManager accountManager, Account account, String clientName) {
+ accountManager.setUserData(account, Constants.CLIENT_NAME, clientName);
+ }
+
@Override
public synchronized void setClientsCount(int clientsCount) {
mAccountManager.setUserData(localAccount, Constants.NUM_CLIENTS,