michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: package org.mozilla.gecko.sync.setup; michael@0: michael@0: import java.io.File; michael@0: import java.io.UnsupportedEncodingException; michael@0: import java.security.NoSuchAlgorithmException; michael@0: michael@0: import org.mozilla.gecko.background.common.GlobalConstants; michael@0: import org.mozilla.gecko.background.common.log.Logger; michael@0: import org.mozilla.gecko.db.BrowserContract; michael@0: import org.mozilla.gecko.sync.CredentialException; michael@0: import org.mozilla.gecko.sync.ExtendedJSONObject; michael@0: import org.mozilla.gecko.sync.SyncConfiguration; michael@0: import org.mozilla.gecko.sync.SyncConstants; michael@0: import org.mozilla.gecko.sync.ThreadPool; michael@0: import org.mozilla.gecko.sync.Utils; michael@0: import org.mozilla.gecko.sync.config.AccountPickler; michael@0: import org.mozilla.gecko.sync.repositories.android.RepoUtils; michael@0: michael@0: import android.accounts.Account; michael@0: import android.accounts.AccountManager; michael@0: import android.content.ActivityNotFoundException; michael@0: import android.content.ContentResolver; michael@0: import android.content.Context; michael@0: import android.content.Intent; michael@0: import android.content.SharedPreferences; michael@0: import android.content.pm.PackageManager.NameNotFoundException; michael@0: import android.os.Bundle; michael@0: import android.provider.Settings; michael@0: import android.util.Log; michael@0: michael@0: /** michael@0: * This class contains utilities that are of use to Fennec michael@0: * and Sync setup activities. michael@0: *
michael@0: * Do not break these APIs without correcting upstream code! michael@0: */ michael@0: public class SyncAccounts { michael@0: private static final String LOG_TAG = "SyncAccounts"; michael@0: michael@0: private static final String MOTO_BLUR_SETTINGS_ACTIVITY = "com.motorola.blur.settings.AccountsAndServicesPreferenceActivity"; michael@0: private static final String MOTO_BLUR_PACKAGE = "com.motorola.blur.setup"; michael@0: michael@0: /** michael@0: * Return Sync accounts. michael@0: * michael@0: * @param c michael@0: * Android context. michael@0: * @return Sync accounts. michael@0: */ michael@0: public static Account[] syncAccounts(final Context c) { michael@0: return AccountManager.get(c).getAccountsByType(SyncConstants.ACCOUNTTYPE_SYNC); michael@0: } michael@0: michael@0: /** michael@0: * Returns true if a Sync account is set up, or we have a pickled Sync account michael@0: * on disk that should be un-pickled (Bug 769745). If we have a pickled Sync michael@0: * account, try to un-pickle it and create the corresponding Sync account. michael@0: *
michael@0: * Do not call this method from the main thread.
michael@0: */
michael@0: public static boolean syncAccountsExist(Context c) {
michael@0: final boolean accountsExist = AccountManager.get(c).getAccountsByType(SyncConstants.ACCOUNTTYPE_SYNC).length > 0;
michael@0: if (accountsExist) {
michael@0: return true;
michael@0: }
michael@0:
michael@0: final File file = c.getFileStreamPath(Constants.ACCOUNT_PICKLE_FILENAME);
michael@0: if (!file.exists()) {
michael@0: return false;
michael@0: }
michael@0:
michael@0: // There is a small race window here: if the user creates a new Sync account
michael@0: // between our checks, this could erroneously report that no Sync accounts
michael@0: // exist.
michael@0: final Account account = AccountPickler.unpickle(c, Constants.ACCOUNT_PICKLE_FILENAME);
michael@0: return (account != null);
michael@0: }
michael@0:
michael@0: /**
michael@0: * This class encapsulates the parameters needed to create a new Firefox Sync
michael@0: * account.
michael@0: */
michael@0: public static class SyncAccountParameters {
michael@0: public final Context context;
michael@0: public final AccountManager accountManager;
michael@0:
michael@0:
michael@0: public final String username; // services.sync.account
michael@0: public final String syncKey; // in password manager: "chrome://weave (Mozilla Services Encryption Passphrase)"
michael@0: public final String password; // in password manager: "chrome://weave (Mozilla Services Password)"
michael@0: public final String serverURL; // services.sync.serverURL
michael@0: public final String clusterURL; // services.sync.clusterURL
michael@0: public final String clientName; // services.sync.client.name
michael@0: public final String clientGuid; // services.sync.client.GUID
michael@0:
michael@0: /**
michael@0: * Encapsulate the parameters needed to create a new Firefox Sync account.
michael@0: *
michael@0: * @param context
michael@0: * the current Context
; cannot be null.
michael@0: * @param accountManager
michael@0: * an AccountManager
instance to use; if null, get it
michael@0: * from context
.
michael@0: * @param username
michael@0: * the desired username; cannot be null.
michael@0: * @param syncKey
michael@0: * the desired sync key; cannot be null.
michael@0: * @param password
michael@0: * the desired password; cannot be null.
michael@0: * @param serverURL
michael@0: * the server URL to use; if null, use the default.
michael@0: * @param clusterURL
michael@0: * the cluster URL to use; if null, a fresh cluster URL will be
michael@0: * retrieved from the server during the next sync.
michael@0: * @param clientName
michael@0: * the client name; if null, a fresh client record will be uploaded
michael@0: * to the server during the next sync.
michael@0: * @param clientGuid
michael@0: * the client GUID; if null, a fresh client record will be uploaded
michael@0: * to the server during the next sync.
michael@0: */
michael@0: public SyncAccountParameters(Context context, AccountManager accountManager,
michael@0: String username, String syncKey, String password,
michael@0: String serverURL, String clusterURL,
michael@0: String clientName, String clientGuid) {
michael@0: if (context == null) {
michael@0: throw new IllegalArgumentException("Null context passed to SyncAccountParameters constructor.");
michael@0: }
michael@0: if (username == null) {
michael@0: throw new IllegalArgumentException("Null username passed to SyncAccountParameters constructor.");
michael@0: }
michael@0: if (syncKey == null) {
michael@0: throw new IllegalArgumentException("Null syncKey passed to SyncAccountParameters constructor.");
michael@0: }
michael@0: if (password == null) {
michael@0: throw new IllegalArgumentException("Null password passed to SyncAccountParameters constructor.");
michael@0: }
michael@0: this.context = context;
michael@0: this.accountManager = accountManager;
michael@0: this.username = username;
michael@0: this.syncKey = syncKey;
michael@0: this.password = password;
michael@0: this.serverURL = serverURL;
michael@0: this.clusterURL = clusterURL;
michael@0: this.clientName = clientName;
michael@0: this.clientGuid = clientGuid;
michael@0: }
michael@0:
michael@0: public SyncAccountParameters(Context context, AccountManager accountManager,
michael@0: String username, String syncKey, String password, String serverURL) {
michael@0: this(context, accountManager, username, syncKey, password, serverURL, null, null, null);
michael@0: }
michael@0:
michael@0: public SyncAccountParameters(final Context context, final AccountManager accountManager, final ExtendedJSONObject o) {
michael@0: this(context, accountManager,
michael@0: o.getString(Constants.JSON_KEY_ACCOUNT),
michael@0: o.getString(Constants.JSON_KEY_SYNCKEY),
michael@0: o.getString(Constants.JSON_KEY_PASSWORD),
michael@0: o.getString(Constants.JSON_KEY_SERVER),
michael@0: o.getString(Constants.JSON_KEY_CLUSTER),
michael@0: o.getString(Constants.JSON_KEY_CLIENT_NAME),
michael@0: o.getString(Constants.JSON_KEY_CLIENT_GUID));
michael@0: }
michael@0:
michael@0: public ExtendedJSONObject asJSON() {
michael@0: final ExtendedJSONObject o = new ExtendedJSONObject();
michael@0: o.put(Constants.JSON_KEY_ACCOUNT, username);
michael@0: o.put(Constants.JSON_KEY_PASSWORD, password);
michael@0: o.put(Constants.JSON_KEY_SERVER, serverURL);
michael@0: o.put(Constants.JSON_KEY_SYNCKEY, syncKey);
michael@0: o.put(Constants.JSON_KEY_CLUSTER, clusterURL);
michael@0: o.put(Constants.JSON_KEY_CLIENT_NAME, clientName);
michael@0: o.put(Constants.JSON_KEY_CLIENT_GUID, clientGuid);
michael@0: return o;
michael@0: }
michael@0: }
michael@0:
michael@0: /**
michael@0: * Create a sync account, clearing any existing preferences, and set it to
michael@0: * sync automatically.
michael@0: *
michael@0: * Do not call this method from the main thread.
michael@0: *
michael@0: * @param syncAccount
michael@0: * parameters of the account to be created.
michael@0: * @return created Account
, or null if an error occurred and the
michael@0: * account could not be added.
michael@0: */
michael@0: public static Account createSyncAccount(SyncAccountParameters syncAccount) {
michael@0: return createSyncAccount(syncAccount, true, true);
michael@0: }
michael@0:
michael@0: /**
michael@0: * Create a sync account, clearing any existing preferences.
michael@0: *
michael@0: * Do not call this method from the main thread. michael@0: *
michael@0: * Intended for testing; use
michael@0: * createSyncAccount(SyncAccountParameters)
instead.
michael@0: *
michael@0: * @param syncAccount
michael@0: * parameters of the account to be created.
michael@0: * @param syncAutomatically
michael@0: * whether to start syncing this Account automatically (
michael@0: * false
for test accounts).
michael@0: * @return created Android Account
, or null if an error occurred
michael@0: * and the account could not be added.
michael@0: */
michael@0: public static Account createSyncAccount(SyncAccountParameters syncAccount,
michael@0: boolean syncAutomatically) {
michael@0: return createSyncAccount(syncAccount, syncAutomatically, true);
michael@0: }
michael@0:
michael@0: public static Account createSyncAccountPreservingExistingPreferences(SyncAccountParameters syncAccount,
michael@0: boolean syncAutomatically) {
michael@0: return createSyncAccount(syncAccount, syncAutomatically, false);
michael@0: }
michael@0:
michael@0: /**
michael@0: * Create a sync account.
michael@0: *
michael@0: * Do not call this method from the main thread. michael@0: *
michael@0: * Intended for testing; use
michael@0: * createSyncAccount(SyncAccountParameters)
instead.
michael@0: *
michael@0: * @param syncAccount
michael@0: * parameters of the account to be created.
michael@0: * @param syncAutomatically
michael@0: * whether to start syncing this Account automatically (
michael@0: * false
for test accounts).
michael@0: * @param clearPreferences
michael@0: * true
to clear existing preferences before creating.
michael@0: * @return created Android Account
, or null if an error occurred
michael@0: * and the account could not be added.
michael@0: */
michael@0: protected static Account createSyncAccount(SyncAccountParameters syncAccount,
michael@0: boolean syncAutomatically, boolean clearPreferences) {
michael@0: final Context context = syncAccount.context;
michael@0: final AccountManager accountManager = (syncAccount.accountManager == null) ?
michael@0: AccountManager.get(syncAccount.context) : syncAccount.accountManager;
michael@0: final String username = syncAccount.username;
michael@0: final String syncKey = syncAccount.syncKey;
michael@0: final String password = syncAccount.password;
michael@0: final String serverURL = (syncAccount.serverURL == null) ?
michael@0: SyncConstants.DEFAULT_AUTH_SERVER : syncAccount.serverURL;
michael@0:
michael@0: Logger.debug(LOG_TAG, "Using account manager " + accountManager);
michael@0: if (!RepoUtils.stringsEqual(syncAccount.serverURL, SyncConstants.DEFAULT_AUTH_SERVER)) {
michael@0: Logger.info(LOG_TAG, "Setting explicit server URL: " + serverURL);
michael@0: }
michael@0:
michael@0: final Account account = new Account(username, SyncConstants.ACCOUNTTYPE_SYNC);
michael@0: final Bundle userbundle = new Bundle();
michael@0:
michael@0: // Add sync key and server URL.
michael@0: userbundle.putString(Constants.OPTION_SYNCKEY, syncKey);
michael@0: userbundle.putString(Constants.OPTION_SERVER, serverURL);
michael@0: Logger.debug(LOG_TAG, "Adding account for " + SyncConstants.ACCOUNTTYPE_SYNC);
michael@0: boolean result = false;
michael@0: try {
michael@0: result = accountManager.addAccountExplicitly(account, password, userbundle);
michael@0: } catch (SecurityException e) {
michael@0: // We use Log rather than Logger here to avoid possibly hiding these errors.
michael@0: final String message = e.getMessage();
michael@0: if (message != null && (message.indexOf("is different than the authenticator's uid") > 0)) {
michael@0: Log.wtf(SyncConstants.GLOBAL_LOG_TAG,
michael@0: "Unable to create account. " +
michael@0: "If you have more than one version of " +
michael@0: "Firefox/Beta/Aurora/Nightly/Fennec installed, that's why.",
michael@0: e);
michael@0: } else {
michael@0: Log.e(SyncConstants.GLOBAL_LOG_TAG, "Unable to create account.", e);
michael@0: }
michael@0: }
michael@0:
michael@0: if (!result) {
michael@0: Logger.error(LOG_TAG, "Failed to add account " + account + "!");
michael@0: return null;
michael@0: }
michael@0: Logger.debug(LOG_TAG, "Account " + account + " added successfully.");
michael@0:
michael@0: setSyncAutomatically(account, syncAutomatically);
michael@0: setIsSyncable(account, syncAutomatically);
michael@0: Logger.debug(LOG_TAG, "Set account to sync automatically? " + syncAutomatically + ".");
michael@0:
michael@0: try {
michael@0: final String product = GlobalConstants.BROWSER_INTENT_PACKAGE;
michael@0: final String profile = Constants.DEFAULT_PROFILE;
michael@0: final long version = SyncConfiguration.CURRENT_PREFS_VERSION;
michael@0:
michael@0: final SharedPreferences.Editor editor = Utils.getSharedPreferences(context, product, username, serverURL, profile, version).edit();
michael@0: if (clearPreferences) {
michael@0: final String prefsPath = Utils.getPrefsPath(product, username, serverURL, profile, version);
michael@0: Logger.info(LOG_TAG, "Clearing preferences path " + prefsPath + " for this account.");
michael@0: editor.clear();
michael@0: }
michael@0:
michael@0: if (syncAccount.clusterURL != null) {
michael@0: editor.putString(SyncConfiguration.PREF_CLUSTER_URL, syncAccount.clusterURL);
michael@0: }
michael@0:
michael@0: if (syncAccount.clientName != null && syncAccount.clientGuid != null) {
michael@0: Logger.debug(LOG_TAG, "Setting client name to " + syncAccount.clientName + " and client GUID to " + syncAccount.clientGuid + ".");
michael@0: editor.putString(SyncConfiguration.PREF_CLIENT_NAME, syncAccount.clientName);
michael@0: editor.putString(SyncConfiguration.PREF_ACCOUNT_GUID, syncAccount.clientGuid);
michael@0: } else {
michael@0: Logger.debug(LOG_TAG, "Client name and guid not both non-null, so not setting client data.");
michael@0: }
michael@0:
michael@0: editor.commit();
michael@0: } catch (Exception e) {
michael@0: Logger.error(LOG_TAG, "Could not clear prefs path!", e);
michael@0: }
michael@0: return account;
michael@0: }
michael@0:
michael@0: public static void setIsSyncable(Account account, boolean isSyncable) {
michael@0: String authority = BrowserContract.AUTHORITY;
michael@0: ContentResolver.setIsSyncable(account, authority, isSyncable ? 1 : 0);
michael@0: }
michael@0:
michael@0: public static void setSyncAutomatically(Account account, boolean syncAutomatically) {
michael@0: if (syncAutomatically) {
michael@0: ContentResolver.setMasterSyncAutomatically(true);
michael@0: }
michael@0:
michael@0: String authority = BrowserContract.AUTHORITY;
michael@0: Logger.debug(LOG_TAG, "Setting authority " + authority + " to " +
michael@0: (syncAutomatically ? "" : "not ") + "sync automatically.");
michael@0: ContentResolver.setSyncAutomatically(account, authority, syncAutomatically);
michael@0: }
michael@0:
michael@0: public static void backgroundSetSyncAutomatically(final Account account, final boolean syncAutomatically) {
michael@0: ThreadPool.run(new Runnable() {
michael@0: @Override
michael@0: public void run() {
michael@0: setSyncAutomatically(account, syncAutomatically);
michael@0: }
michael@0: });
michael@0: }
michael@0: /**
michael@0: * Bug 721760: try to start a vendor-specific Accounts & Sync activity on Moto
michael@0: * Blur devices.
michael@0: *
michael@0: * Bug 773562: actually start and catch ActivityNotFoundException
,
michael@0: * rather than just returning the Intent
only, because some
michael@0: * Moto devices fail to start the activity.
michael@0: *
michael@0: * @param context
michael@0: * current Android context.
michael@0: * @param vendorPackage
michael@0: * vendor specific package name.
michael@0: * @param vendorClass
michael@0: * vendor specific class name.
michael@0: * @return null on failure, otherwise the Intent
started.
michael@0: */
michael@0: protected static Intent openVendorSyncSettings(Context context, final String vendorPackage, final String vendorClass) {
michael@0: try {
michael@0: final int contextFlags = Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY;
michael@0: Context foreignContext = context.createPackageContext(vendorPackage, contextFlags);
michael@0: Class> klass = foreignContext.getClassLoader().loadClass(vendorClass);
michael@0:
michael@0: final Intent intent = new Intent(foreignContext, klass);
michael@0: context.startActivity(intent);
michael@0: Logger.info(LOG_TAG, "Vendor package " + vendorPackage + " and class " +
michael@0: vendorClass + " found, and activity launched.");
michael@0: return intent;
michael@0: } catch (NameNotFoundException e) {
michael@0: Logger.debug(LOG_TAG, "Vendor package " + vendorPackage + " not found. Skipping.");
michael@0: } catch (ClassNotFoundException e) {
michael@0: Logger.debug(LOG_TAG, "Vendor package " + vendorPackage + " found but class " +
michael@0: vendorClass + " not found. Skipping.", e);
michael@0: } catch (ActivityNotFoundException e) {
michael@0: // Bug 773562 - android.content.ActivityNotFoundException on Motorola devices.
michael@0: Logger.warn(LOG_TAG, "Vendor package " + vendorPackage + " and class " +
michael@0: vendorClass + " found, but activity not launched. Skipping.", e);
michael@0: } catch (Exception e) {
michael@0: // Just in case.
michael@0: Logger.warn(LOG_TAG, "Caught exception launching activity from vendor package " + vendorPackage +
michael@0: " and class " + vendorClass + ". Ignoring.", e);
michael@0: }
michael@0: return null;
michael@0: }
michael@0:
michael@0: /**
michael@0: * Start Sync settings activity.
michael@0: *
michael@0: * @param context
michael@0: * current Android context.
michael@0: * @return the Intent
started.
michael@0: */
michael@0: public static Intent openSyncSettings(Context context) {
michael@0: // Bug 721760 - opening Sync settings takes user to Battery & Data Manager
michael@0: // on a variety of Motorola devices. This work around tries to load the
michael@0: // correct Intent by hand. Oh, Android.
michael@0: Intent intent = openVendorSyncSettings(context, MOTO_BLUR_PACKAGE, MOTO_BLUR_SETTINGS_ACTIVITY);
michael@0: if (intent != null) {
michael@0: return intent;
michael@0: }
michael@0:
michael@0: // Open default Sync settings activity.
michael@0: intent = new Intent(Settings.ACTION_SYNC_SETTINGS);
michael@0: // Bug 774233: do not start activity as a new task (second run fails on some HTC devices).
michael@0: context.startActivity(intent); // We should always find this Activity.
michael@0: return intent;
michael@0: }
michael@0:
michael@0: /**
michael@0: * Synchronously extract Sync account parameters from Android account version
michael@0: * 0, using plain auth token type.
michael@0: *
michael@0: * Safe to call from main thread.
michael@0: *
michael@0: * @param context
michael@0: * Android context.
michael@0: * @param accountManager
michael@0: * Android account manager.
michael@0: * @param account
michael@0: * Android Account.
michael@0: * @return Sync account parameters, always non-null; fields username,
michael@0: * password, serverURL, and syncKey always non-null.
michael@0: */
michael@0: public static SyncAccountParameters blockingFromAndroidAccountV0(final Context context, final AccountManager accountManager, final Account account)
michael@0: throws CredentialException {
michael@0: String username;
michael@0: try {
michael@0: username = Utils.usernameFromAccount(account.name);
michael@0: } catch (NoSuchAlgorithmException e) {
michael@0: throw new CredentialException.MissingCredentialException("username");
michael@0: } catch (UnsupportedEncodingException e) {
michael@0: throw new CredentialException.MissingCredentialException("username");
michael@0: }
michael@0:
michael@0: /*
michael@0: * If we are accessing an Account that we don't own, Android will throw an
michael@0: * unchecked SecurityException
saying
michael@0: * "W FxSync(XXXX) java.lang.SecurityException: caller uid XXXXX is different than the authenticator's uid".
michael@0: * We catch that error and throw accordingly.
michael@0: */
michael@0: String password;
michael@0: String syncKey;
michael@0: String serverURL;
michael@0: try {
michael@0: password = accountManager.getPassword(account);
michael@0: syncKey = accountManager.getUserData(account, Constants.OPTION_SYNCKEY);
michael@0: serverURL = accountManager.getUserData(account, Constants.OPTION_SERVER);
michael@0: } catch (SecurityException e) {
michael@0: Logger.warn(LOG_TAG, "Got security exception fetching Sync account parameters; throwing.");
michael@0: throw new CredentialException.MissingAllCredentialsException(e);
michael@0: }
michael@0:
michael@0: if (password == null &&
michael@0: username == null &&
michael@0: syncKey == null &&
michael@0: serverURL == null) {
michael@0: throw new CredentialException.MissingAllCredentialsException();
michael@0: }
michael@0:
michael@0: if (password == null) {
michael@0: throw new CredentialException.MissingCredentialException("password");
michael@0: }
michael@0:
michael@0: if (syncKey == null) {
michael@0: throw new CredentialException.MissingCredentialException("syncKey");
michael@0: }
michael@0:
michael@0: if (serverURL == null) {
michael@0: throw new CredentialException.MissingCredentialException("serverURL");
michael@0: }
michael@0:
michael@0: try {
michael@0: // SyncAccountParameters constructor throws on null inputs. This shouldn't
michael@0: // happen, but let's be safe.
michael@0: return new SyncAccountParameters(context, accountManager, username, syncKey, password, serverURL);
michael@0: } catch (Exception e) {
michael@0: Logger.warn(LOG_TAG, "Got exception fetching Sync account parameters; throwing.");
michael@0: throw new CredentialException.MissingAllCredentialsException(e);
michael@0: }
michael@0: }
michael@0:
michael@0: /**
michael@0: * Bug 790931: create an intent announcing that a Sync account will be
michael@0: * deleted.
michael@0: *
michael@0: * This intent must be broadcast with secure permissions, because it michael@0: * contains sensitive user information including the Sync account password and michael@0: * Sync key. michael@0: *
michael@0: * Version 1 of the created intent includes extras with keys
michael@0: * Constants.JSON_KEY_VERSION
,
michael@0: * Constants.JSON_KEY_TIMESTAMP
, and
michael@0: * Constants.JSON_KEY_ACCOUNT
(which is the Android Account name,
michael@0: * not the encoded Sync Account name).
michael@0: *
michael@0: * If possible, it contains the key Constants.JSON_KEY_PAYLOAD
michael@0: * with value the Sync account parameters as JSON, except the Sync key has
michael@0: * been replaced with the empty string. (We replace, rather than remove,
michael@0: * the Sync key because SyncAccountParameters expects a non-null Sync key.)
michael@0: *
michael@0: * @see SyncAccountParameters#asJSON
michael@0: *
michael@0: * @param context
michael@0: * Android context.
michael@0: * @param accountManager
michael@0: * Android account manager.
michael@0: * @param account
michael@0: * Android account being removed.
michael@0: * @return Intent
to broadcast.
michael@0: */
michael@0: public static Intent makeSyncAccountDeletedIntent(final Context context, final AccountManager accountManager, final Account account) {
michael@0: final Intent intent = new Intent(SyncConstants.SYNC_ACCOUNT_DELETED_ACTION);
michael@0:
michael@0: intent.putExtra(Constants.JSON_KEY_VERSION, Long.valueOf(SyncConstants.SYNC_ACCOUNT_DELETED_INTENT_VERSION));
michael@0: intent.putExtra(Constants.JSON_KEY_TIMESTAMP, Long.valueOf(System.currentTimeMillis()));
michael@0: intent.putExtra(Constants.JSON_KEY_ACCOUNT, account.name);
michael@0:
michael@0: SyncAccountParameters accountParameters = null;
michael@0: try {
michael@0: accountParameters = SyncAccounts.blockingFromAndroidAccountV0(context, accountManager, account);
michael@0: } catch (Exception e) {
michael@0: Logger.warn(LOG_TAG, "Caught exception fetching account parameters.", e);
michael@0: }
michael@0:
michael@0: if (accountParameters != null) {
michael@0: ExtendedJSONObject json = accountParameters.asJSON();
michael@0: json.put(Constants.JSON_KEY_SYNCKEY, ""); // Reduce attack surface area by removing Sync key.
michael@0: intent.putExtra(Constants.JSON_KEY_PAYLOAD, json.toJSONString());
michael@0: }
michael@0:
michael@0: return intent;
michael@0: }
michael@0:
michael@0: /**
michael@0: * Synchronously fetch SharedPreferences of a profile associated with a Sync
michael@0: * account.
michael@0: *
michael@0: * Safe to call from main thread. michael@0: * michael@0: * @param context michael@0: * Android context. michael@0: * @param accountManager michael@0: * Android account manager. michael@0: * @param account michael@0: * Android Account. michael@0: * @param product michael@0: * package. michael@0: * @param profile michael@0: * of account. michael@0: * @param version michael@0: * number. michael@0: * @return SharedPreferences associated with Sync account. michael@0: * @throws CredentialException michael@0: * @throws NoSuchAlgorithmException michael@0: * @throws UnsupportedEncodingException michael@0: */ michael@0: public static SharedPreferences blockingPrefsFromAndroidAccountV0(final Context context, final AccountManager accountManager, final Account account, michael@0: final String product, final String profile, final long version) michael@0: throws CredentialException, NoSuchAlgorithmException, UnsupportedEncodingException { michael@0: SyncAccountParameters params = SyncAccounts.blockingFromAndroidAccountV0(context, accountManager, account); michael@0: String prefsPath = Utils.getPrefsPath(product, params.username, params.serverURL, profile, version); michael@0: michael@0: return context.getSharedPreferences(prefsPath, Utils.SHARED_PREFERENCES_MODE); michael@0: } michael@0: michael@0: /** michael@0: * Synchronously fetch SharedPreferences of a profile associated with the michael@0: * default Firefox profile of a Sync Account. michael@0: *
michael@0: * Uses the default package, default profile, and current version. michael@0: *
michael@0: * Safe to call from main thread. michael@0: * michael@0: * @param context michael@0: * Android context. michael@0: * @param accountManager michael@0: * Android account manager. michael@0: * @param account michael@0: * Android Account. michael@0: * @return SharedPreferences associated with Sync account. michael@0: * @throws CredentialException michael@0: * @throws NoSuchAlgorithmException michael@0: * @throws UnsupportedEncodingException michael@0: */ michael@0: public static SharedPreferences blockingPrefsFromDefaultProfileV0(final Context context, final AccountManager accountManager, final Account account) michael@0: throws CredentialException, NoSuchAlgorithmException, UnsupportedEncodingException { michael@0: final String product = GlobalConstants.BROWSER_INTENT_PACKAGE; michael@0: final String profile = Constants.DEFAULT_PROFILE; michael@0: final long version = SyncConfiguration.CURRENT_PREFS_VERSION; michael@0: michael@0: return blockingPrefsFromAndroidAccountV0(context, accountManager, account, product, profile, version); michael@0: } michael@0: }