mobile/android/base/sync/setup/SyncAccounts.java

Wed, 31 Dec 2014 07:22:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:22:50 +0100
branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
permissions
-rw-r--r--

Correct previous dual key logic pending first delivery installment.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 package org.mozilla.gecko.sync.setup;
michael@0 6
michael@0 7 import java.io.File;
michael@0 8 import java.io.UnsupportedEncodingException;
michael@0 9 import java.security.NoSuchAlgorithmException;
michael@0 10
michael@0 11 import org.mozilla.gecko.background.common.GlobalConstants;
michael@0 12 import org.mozilla.gecko.background.common.log.Logger;
michael@0 13 import org.mozilla.gecko.db.BrowserContract;
michael@0 14 import org.mozilla.gecko.sync.CredentialException;
michael@0 15 import org.mozilla.gecko.sync.ExtendedJSONObject;
michael@0 16 import org.mozilla.gecko.sync.SyncConfiguration;
michael@0 17 import org.mozilla.gecko.sync.SyncConstants;
michael@0 18 import org.mozilla.gecko.sync.ThreadPool;
michael@0 19 import org.mozilla.gecko.sync.Utils;
michael@0 20 import org.mozilla.gecko.sync.config.AccountPickler;
michael@0 21 import org.mozilla.gecko.sync.repositories.android.RepoUtils;
michael@0 22
michael@0 23 import android.accounts.Account;
michael@0 24 import android.accounts.AccountManager;
michael@0 25 import android.content.ActivityNotFoundException;
michael@0 26 import android.content.ContentResolver;
michael@0 27 import android.content.Context;
michael@0 28 import android.content.Intent;
michael@0 29 import android.content.SharedPreferences;
michael@0 30 import android.content.pm.PackageManager.NameNotFoundException;
michael@0 31 import android.os.Bundle;
michael@0 32 import android.provider.Settings;
michael@0 33 import android.util.Log;
michael@0 34
michael@0 35 /**
michael@0 36 * This class contains utilities that are of use to Fennec
michael@0 37 * and Sync setup activities.
michael@0 38 * <p>
michael@0 39 * Do not break these APIs without correcting upstream code!
michael@0 40 */
michael@0 41 public class SyncAccounts {
michael@0 42 private static final String LOG_TAG = "SyncAccounts";
michael@0 43
michael@0 44 private static final String MOTO_BLUR_SETTINGS_ACTIVITY = "com.motorola.blur.settings.AccountsAndServicesPreferenceActivity";
michael@0 45 private static final String MOTO_BLUR_PACKAGE = "com.motorola.blur.setup";
michael@0 46
michael@0 47 /**
michael@0 48 * Return Sync accounts.
michael@0 49 *
michael@0 50 * @param c
michael@0 51 * Android context.
michael@0 52 * @return Sync accounts.
michael@0 53 */
michael@0 54 public static Account[] syncAccounts(final Context c) {
michael@0 55 return AccountManager.get(c).getAccountsByType(SyncConstants.ACCOUNTTYPE_SYNC);
michael@0 56 }
michael@0 57
michael@0 58 /**
michael@0 59 * Returns true if a Sync account is set up, or we have a pickled Sync account
michael@0 60 * on disk that should be un-pickled (Bug 769745). If we have a pickled Sync
michael@0 61 * account, try to un-pickle it and create the corresponding Sync account.
michael@0 62 * <p>
michael@0 63 * Do not call this method from the main thread.
michael@0 64 */
michael@0 65 public static boolean syncAccountsExist(Context c) {
michael@0 66 final boolean accountsExist = AccountManager.get(c).getAccountsByType(SyncConstants.ACCOUNTTYPE_SYNC).length > 0;
michael@0 67 if (accountsExist) {
michael@0 68 return true;
michael@0 69 }
michael@0 70
michael@0 71 final File file = c.getFileStreamPath(Constants.ACCOUNT_PICKLE_FILENAME);
michael@0 72 if (!file.exists()) {
michael@0 73 return false;
michael@0 74 }
michael@0 75
michael@0 76 // There is a small race window here: if the user creates a new Sync account
michael@0 77 // between our checks, this could erroneously report that no Sync accounts
michael@0 78 // exist.
michael@0 79 final Account account = AccountPickler.unpickle(c, Constants.ACCOUNT_PICKLE_FILENAME);
michael@0 80 return (account != null);
michael@0 81 }
michael@0 82
michael@0 83 /**
michael@0 84 * This class encapsulates the parameters needed to create a new Firefox Sync
michael@0 85 * account.
michael@0 86 */
michael@0 87 public static class SyncAccountParameters {
michael@0 88 public final Context context;
michael@0 89 public final AccountManager accountManager;
michael@0 90
michael@0 91
michael@0 92 public final String username; // services.sync.account
michael@0 93 public final String syncKey; // in password manager: "chrome://weave (Mozilla Services Encryption Passphrase)"
michael@0 94 public final String password; // in password manager: "chrome://weave (Mozilla Services Password)"
michael@0 95 public final String serverURL; // services.sync.serverURL
michael@0 96 public final String clusterURL; // services.sync.clusterURL
michael@0 97 public final String clientName; // services.sync.client.name
michael@0 98 public final String clientGuid; // services.sync.client.GUID
michael@0 99
michael@0 100 /**
michael@0 101 * Encapsulate the parameters needed to create a new Firefox Sync account.
michael@0 102 *
michael@0 103 * @param context
michael@0 104 * the current <code>Context</code>; cannot be null.
michael@0 105 * @param accountManager
michael@0 106 * an <code>AccountManager</code> instance to use; if null, get it
michael@0 107 * from <code>context</code>.
michael@0 108 * @param username
michael@0 109 * the desired username; cannot be null.
michael@0 110 * @param syncKey
michael@0 111 * the desired sync key; cannot be null.
michael@0 112 * @param password
michael@0 113 * the desired password; cannot be null.
michael@0 114 * @param serverURL
michael@0 115 * the server URL to use; if null, use the default.
michael@0 116 * @param clusterURL
michael@0 117 * the cluster URL to use; if null, a fresh cluster URL will be
michael@0 118 * retrieved from the server during the next sync.
michael@0 119 * @param clientName
michael@0 120 * the client name; if null, a fresh client record will be uploaded
michael@0 121 * to the server during the next sync.
michael@0 122 * @param clientGuid
michael@0 123 * the client GUID; if null, a fresh client record will be uploaded
michael@0 124 * to the server during the next sync.
michael@0 125 */
michael@0 126 public SyncAccountParameters(Context context, AccountManager accountManager,
michael@0 127 String username, String syncKey, String password,
michael@0 128 String serverURL, String clusterURL,
michael@0 129 String clientName, String clientGuid) {
michael@0 130 if (context == null) {
michael@0 131 throw new IllegalArgumentException("Null context passed to SyncAccountParameters constructor.");
michael@0 132 }
michael@0 133 if (username == null) {
michael@0 134 throw new IllegalArgumentException("Null username passed to SyncAccountParameters constructor.");
michael@0 135 }
michael@0 136 if (syncKey == null) {
michael@0 137 throw new IllegalArgumentException("Null syncKey passed to SyncAccountParameters constructor.");
michael@0 138 }
michael@0 139 if (password == null) {
michael@0 140 throw new IllegalArgumentException("Null password passed to SyncAccountParameters constructor.");
michael@0 141 }
michael@0 142 this.context = context;
michael@0 143 this.accountManager = accountManager;
michael@0 144 this.username = username;
michael@0 145 this.syncKey = syncKey;
michael@0 146 this.password = password;
michael@0 147 this.serverURL = serverURL;
michael@0 148 this.clusterURL = clusterURL;
michael@0 149 this.clientName = clientName;
michael@0 150 this.clientGuid = clientGuid;
michael@0 151 }
michael@0 152
michael@0 153 public SyncAccountParameters(Context context, AccountManager accountManager,
michael@0 154 String username, String syncKey, String password, String serverURL) {
michael@0 155 this(context, accountManager, username, syncKey, password, serverURL, null, null, null);
michael@0 156 }
michael@0 157
michael@0 158 public SyncAccountParameters(final Context context, final AccountManager accountManager, final ExtendedJSONObject o) {
michael@0 159 this(context, accountManager,
michael@0 160 o.getString(Constants.JSON_KEY_ACCOUNT),
michael@0 161 o.getString(Constants.JSON_KEY_SYNCKEY),
michael@0 162 o.getString(Constants.JSON_KEY_PASSWORD),
michael@0 163 o.getString(Constants.JSON_KEY_SERVER),
michael@0 164 o.getString(Constants.JSON_KEY_CLUSTER),
michael@0 165 o.getString(Constants.JSON_KEY_CLIENT_NAME),
michael@0 166 o.getString(Constants.JSON_KEY_CLIENT_GUID));
michael@0 167 }
michael@0 168
michael@0 169 public ExtendedJSONObject asJSON() {
michael@0 170 final ExtendedJSONObject o = new ExtendedJSONObject();
michael@0 171 o.put(Constants.JSON_KEY_ACCOUNT, username);
michael@0 172 o.put(Constants.JSON_KEY_PASSWORD, password);
michael@0 173 o.put(Constants.JSON_KEY_SERVER, serverURL);
michael@0 174 o.put(Constants.JSON_KEY_SYNCKEY, syncKey);
michael@0 175 o.put(Constants.JSON_KEY_CLUSTER, clusterURL);
michael@0 176 o.put(Constants.JSON_KEY_CLIENT_NAME, clientName);
michael@0 177 o.put(Constants.JSON_KEY_CLIENT_GUID, clientGuid);
michael@0 178 return o;
michael@0 179 }
michael@0 180 }
michael@0 181
michael@0 182 /**
michael@0 183 * Create a sync account, clearing any existing preferences, and set it to
michael@0 184 * sync automatically.
michael@0 185 * <p>
michael@0 186 * Do not call this method from the main thread.
michael@0 187 *
michael@0 188 * @param syncAccount
michael@0 189 * parameters of the account to be created.
michael@0 190 * @return created <code>Account</code>, or null if an error occurred and the
michael@0 191 * account could not be added.
michael@0 192 */
michael@0 193 public static Account createSyncAccount(SyncAccountParameters syncAccount) {
michael@0 194 return createSyncAccount(syncAccount, true, true);
michael@0 195 }
michael@0 196
michael@0 197 /**
michael@0 198 * Create a sync account, clearing any existing preferences.
michael@0 199 * <p>
michael@0 200 * Do not call this method from the main thread.
michael@0 201 * <p>
michael@0 202 * Intended for testing; use
michael@0 203 * <code>createSyncAccount(SyncAccountParameters)</code> instead.
michael@0 204 *
michael@0 205 * @param syncAccount
michael@0 206 * parameters of the account to be created.
michael@0 207 * @param syncAutomatically
michael@0 208 * whether to start syncing this Account automatically (
michael@0 209 * <code>false</code> for test accounts).
michael@0 210 * @return created Android <code>Account</code>, or null if an error occurred
michael@0 211 * and the account could not be added.
michael@0 212 */
michael@0 213 public static Account createSyncAccount(SyncAccountParameters syncAccount,
michael@0 214 boolean syncAutomatically) {
michael@0 215 return createSyncAccount(syncAccount, syncAutomatically, true);
michael@0 216 }
michael@0 217
michael@0 218 public static Account createSyncAccountPreservingExistingPreferences(SyncAccountParameters syncAccount,
michael@0 219 boolean syncAutomatically) {
michael@0 220 return createSyncAccount(syncAccount, syncAutomatically, false);
michael@0 221 }
michael@0 222
michael@0 223 /**
michael@0 224 * Create a sync account.
michael@0 225 * <p>
michael@0 226 * Do not call this method from the main thread.
michael@0 227 * <p>
michael@0 228 * Intended for testing; use
michael@0 229 * <code>createSyncAccount(SyncAccountParameters)</code> instead.
michael@0 230 *
michael@0 231 * @param syncAccount
michael@0 232 * parameters of the account to be created.
michael@0 233 * @param syncAutomatically
michael@0 234 * whether to start syncing this Account automatically (
michael@0 235 * <code>false</code> for test accounts).
michael@0 236 * @param clearPreferences
michael@0 237 * <code>true</code> to clear existing preferences before creating.
michael@0 238 * @return created Android <code>Account</code>, or null if an error occurred
michael@0 239 * and the account could not be added.
michael@0 240 */
michael@0 241 protected static Account createSyncAccount(SyncAccountParameters syncAccount,
michael@0 242 boolean syncAutomatically, boolean clearPreferences) {
michael@0 243 final Context context = syncAccount.context;
michael@0 244 final AccountManager accountManager = (syncAccount.accountManager == null) ?
michael@0 245 AccountManager.get(syncAccount.context) : syncAccount.accountManager;
michael@0 246 final String username = syncAccount.username;
michael@0 247 final String syncKey = syncAccount.syncKey;
michael@0 248 final String password = syncAccount.password;
michael@0 249 final String serverURL = (syncAccount.serverURL == null) ?
michael@0 250 SyncConstants.DEFAULT_AUTH_SERVER : syncAccount.serverURL;
michael@0 251
michael@0 252 Logger.debug(LOG_TAG, "Using account manager " + accountManager);
michael@0 253 if (!RepoUtils.stringsEqual(syncAccount.serverURL, SyncConstants.DEFAULT_AUTH_SERVER)) {
michael@0 254 Logger.info(LOG_TAG, "Setting explicit server URL: " + serverURL);
michael@0 255 }
michael@0 256
michael@0 257 final Account account = new Account(username, SyncConstants.ACCOUNTTYPE_SYNC);
michael@0 258 final Bundle userbundle = new Bundle();
michael@0 259
michael@0 260 // Add sync key and server URL.
michael@0 261 userbundle.putString(Constants.OPTION_SYNCKEY, syncKey);
michael@0 262 userbundle.putString(Constants.OPTION_SERVER, serverURL);
michael@0 263 Logger.debug(LOG_TAG, "Adding account for " + SyncConstants.ACCOUNTTYPE_SYNC);
michael@0 264 boolean result = false;
michael@0 265 try {
michael@0 266 result = accountManager.addAccountExplicitly(account, password, userbundle);
michael@0 267 } catch (SecurityException e) {
michael@0 268 // We use Log rather than Logger here to avoid possibly hiding these errors.
michael@0 269 final String message = e.getMessage();
michael@0 270 if (message != null && (message.indexOf("is different than the authenticator's uid") > 0)) {
michael@0 271 Log.wtf(SyncConstants.GLOBAL_LOG_TAG,
michael@0 272 "Unable to create account. " +
michael@0 273 "If you have more than one version of " +
michael@0 274 "Firefox/Beta/Aurora/Nightly/Fennec installed, that's why.",
michael@0 275 e);
michael@0 276 } else {
michael@0 277 Log.e(SyncConstants.GLOBAL_LOG_TAG, "Unable to create account.", e);
michael@0 278 }
michael@0 279 }
michael@0 280
michael@0 281 if (!result) {
michael@0 282 Logger.error(LOG_TAG, "Failed to add account " + account + "!");
michael@0 283 return null;
michael@0 284 }
michael@0 285 Logger.debug(LOG_TAG, "Account " + account + " added successfully.");
michael@0 286
michael@0 287 setSyncAutomatically(account, syncAutomatically);
michael@0 288 setIsSyncable(account, syncAutomatically);
michael@0 289 Logger.debug(LOG_TAG, "Set account to sync automatically? " + syncAutomatically + ".");
michael@0 290
michael@0 291 try {
michael@0 292 final String product = GlobalConstants.BROWSER_INTENT_PACKAGE;
michael@0 293 final String profile = Constants.DEFAULT_PROFILE;
michael@0 294 final long version = SyncConfiguration.CURRENT_PREFS_VERSION;
michael@0 295
michael@0 296 final SharedPreferences.Editor editor = Utils.getSharedPreferences(context, product, username, serverURL, profile, version).edit();
michael@0 297 if (clearPreferences) {
michael@0 298 final String prefsPath = Utils.getPrefsPath(product, username, serverURL, profile, version);
michael@0 299 Logger.info(LOG_TAG, "Clearing preferences path " + prefsPath + " for this account.");
michael@0 300 editor.clear();
michael@0 301 }
michael@0 302
michael@0 303 if (syncAccount.clusterURL != null) {
michael@0 304 editor.putString(SyncConfiguration.PREF_CLUSTER_URL, syncAccount.clusterURL);
michael@0 305 }
michael@0 306
michael@0 307 if (syncAccount.clientName != null && syncAccount.clientGuid != null) {
michael@0 308 Logger.debug(LOG_TAG, "Setting client name to " + syncAccount.clientName + " and client GUID to " + syncAccount.clientGuid + ".");
michael@0 309 editor.putString(SyncConfiguration.PREF_CLIENT_NAME, syncAccount.clientName);
michael@0 310 editor.putString(SyncConfiguration.PREF_ACCOUNT_GUID, syncAccount.clientGuid);
michael@0 311 } else {
michael@0 312 Logger.debug(LOG_TAG, "Client name and guid not both non-null, so not setting client data.");
michael@0 313 }
michael@0 314
michael@0 315 editor.commit();
michael@0 316 } catch (Exception e) {
michael@0 317 Logger.error(LOG_TAG, "Could not clear prefs path!", e);
michael@0 318 }
michael@0 319 return account;
michael@0 320 }
michael@0 321
michael@0 322 public static void setIsSyncable(Account account, boolean isSyncable) {
michael@0 323 String authority = BrowserContract.AUTHORITY;
michael@0 324 ContentResolver.setIsSyncable(account, authority, isSyncable ? 1 : 0);
michael@0 325 }
michael@0 326
michael@0 327 public static void setSyncAutomatically(Account account, boolean syncAutomatically) {
michael@0 328 if (syncAutomatically) {
michael@0 329 ContentResolver.setMasterSyncAutomatically(true);
michael@0 330 }
michael@0 331
michael@0 332 String authority = BrowserContract.AUTHORITY;
michael@0 333 Logger.debug(LOG_TAG, "Setting authority " + authority + " to " +
michael@0 334 (syncAutomatically ? "" : "not ") + "sync automatically.");
michael@0 335 ContentResolver.setSyncAutomatically(account, authority, syncAutomatically);
michael@0 336 }
michael@0 337
michael@0 338 public static void backgroundSetSyncAutomatically(final Account account, final boolean syncAutomatically) {
michael@0 339 ThreadPool.run(new Runnable() {
michael@0 340 @Override
michael@0 341 public void run() {
michael@0 342 setSyncAutomatically(account, syncAutomatically);
michael@0 343 }
michael@0 344 });
michael@0 345 }
michael@0 346 /**
michael@0 347 * Bug 721760: try to start a vendor-specific Accounts & Sync activity on Moto
michael@0 348 * Blur devices.
michael@0 349 * <p>
michael@0 350 * Bug 773562: actually start and catch <code>ActivityNotFoundException</code>,
michael@0 351 * rather than just returning the <code>Intent</code> only, because some
michael@0 352 * Moto devices fail to start the activity.
michael@0 353 *
michael@0 354 * @param context
michael@0 355 * current Android context.
michael@0 356 * @param vendorPackage
michael@0 357 * vendor specific package name.
michael@0 358 * @param vendorClass
michael@0 359 * vendor specific class name.
michael@0 360 * @return null on failure, otherwise the <code>Intent</code> started.
michael@0 361 */
michael@0 362 protected static Intent openVendorSyncSettings(Context context, final String vendorPackage, final String vendorClass) {
michael@0 363 try {
michael@0 364 final int contextFlags = Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY;
michael@0 365 Context foreignContext = context.createPackageContext(vendorPackage, contextFlags);
michael@0 366 Class<?> klass = foreignContext.getClassLoader().loadClass(vendorClass);
michael@0 367
michael@0 368 final Intent intent = new Intent(foreignContext, klass);
michael@0 369 context.startActivity(intent);
michael@0 370 Logger.info(LOG_TAG, "Vendor package " + vendorPackage + " and class " +
michael@0 371 vendorClass + " found, and activity launched.");
michael@0 372 return intent;
michael@0 373 } catch (NameNotFoundException e) {
michael@0 374 Logger.debug(LOG_TAG, "Vendor package " + vendorPackage + " not found. Skipping.");
michael@0 375 } catch (ClassNotFoundException e) {
michael@0 376 Logger.debug(LOG_TAG, "Vendor package " + vendorPackage + " found but class " +
michael@0 377 vendorClass + " not found. Skipping.", e);
michael@0 378 } catch (ActivityNotFoundException e) {
michael@0 379 // Bug 773562 - android.content.ActivityNotFoundException on Motorola devices.
michael@0 380 Logger.warn(LOG_TAG, "Vendor package " + vendorPackage + " and class " +
michael@0 381 vendorClass + " found, but activity not launched. Skipping.", e);
michael@0 382 } catch (Exception e) {
michael@0 383 // Just in case.
michael@0 384 Logger.warn(LOG_TAG, "Caught exception launching activity from vendor package " + vendorPackage +
michael@0 385 " and class " + vendorClass + ". Ignoring.", e);
michael@0 386 }
michael@0 387 return null;
michael@0 388 }
michael@0 389
michael@0 390 /**
michael@0 391 * Start Sync settings activity.
michael@0 392 *
michael@0 393 * @param context
michael@0 394 * current Android context.
michael@0 395 * @return the <code>Intent</code> started.
michael@0 396 */
michael@0 397 public static Intent openSyncSettings(Context context) {
michael@0 398 // Bug 721760 - opening Sync settings takes user to Battery & Data Manager
michael@0 399 // on a variety of Motorola devices. This work around tries to load the
michael@0 400 // correct Intent by hand. Oh, Android.
michael@0 401 Intent intent = openVendorSyncSettings(context, MOTO_BLUR_PACKAGE, MOTO_BLUR_SETTINGS_ACTIVITY);
michael@0 402 if (intent != null) {
michael@0 403 return intent;
michael@0 404 }
michael@0 405
michael@0 406 // Open default Sync settings activity.
michael@0 407 intent = new Intent(Settings.ACTION_SYNC_SETTINGS);
michael@0 408 // Bug 774233: do not start activity as a new task (second run fails on some HTC devices).
michael@0 409 context.startActivity(intent); // We should always find this Activity.
michael@0 410 return intent;
michael@0 411 }
michael@0 412
michael@0 413 /**
michael@0 414 * Synchronously extract Sync account parameters from Android account version
michael@0 415 * 0, using plain auth token type.
michael@0 416 * <p>
michael@0 417 * Safe to call from main thread.
michael@0 418 *
michael@0 419 * @param context
michael@0 420 * Android context.
michael@0 421 * @param accountManager
michael@0 422 * Android account manager.
michael@0 423 * @param account
michael@0 424 * Android Account.
michael@0 425 * @return Sync account parameters, always non-null; fields username,
michael@0 426 * password, serverURL, and syncKey always non-null.
michael@0 427 */
michael@0 428 public static SyncAccountParameters blockingFromAndroidAccountV0(final Context context, final AccountManager accountManager, final Account account)
michael@0 429 throws CredentialException {
michael@0 430 String username;
michael@0 431 try {
michael@0 432 username = Utils.usernameFromAccount(account.name);
michael@0 433 } catch (NoSuchAlgorithmException e) {
michael@0 434 throw new CredentialException.MissingCredentialException("username");
michael@0 435 } catch (UnsupportedEncodingException e) {
michael@0 436 throw new CredentialException.MissingCredentialException("username");
michael@0 437 }
michael@0 438
michael@0 439 /*
michael@0 440 * If we are accessing an Account that we don't own, Android will throw an
michael@0 441 * unchecked <code>SecurityException</code> saying
michael@0 442 * "W FxSync(XXXX) java.lang.SecurityException: caller uid XXXXX is different than the authenticator's uid".
michael@0 443 * We catch that error and throw accordingly.
michael@0 444 */
michael@0 445 String password;
michael@0 446 String syncKey;
michael@0 447 String serverURL;
michael@0 448 try {
michael@0 449 password = accountManager.getPassword(account);
michael@0 450 syncKey = accountManager.getUserData(account, Constants.OPTION_SYNCKEY);
michael@0 451 serverURL = accountManager.getUserData(account, Constants.OPTION_SERVER);
michael@0 452 } catch (SecurityException e) {
michael@0 453 Logger.warn(LOG_TAG, "Got security exception fetching Sync account parameters; throwing.");
michael@0 454 throw new CredentialException.MissingAllCredentialsException(e);
michael@0 455 }
michael@0 456
michael@0 457 if (password == null &&
michael@0 458 username == null &&
michael@0 459 syncKey == null &&
michael@0 460 serverURL == null) {
michael@0 461 throw new CredentialException.MissingAllCredentialsException();
michael@0 462 }
michael@0 463
michael@0 464 if (password == null) {
michael@0 465 throw new CredentialException.MissingCredentialException("password");
michael@0 466 }
michael@0 467
michael@0 468 if (syncKey == null) {
michael@0 469 throw new CredentialException.MissingCredentialException("syncKey");
michael@0 470 }
michael@0 471
michael@0 472 if (serverURL == null) {
michael@0 473 throw new CredentialException.MissingCredentialException("serverURL");
michael@0 474 }
michael@0 475
michael@0 476 try {
michael@0 477 // SyncAccountParameters constructor throws on null inputs. This shouldn't
michael@0 478 // happen, but let's be safe.
michael@0 479 return new SyncAccountParameters(context, accountManager, username, syncKey, password, serverURL);
michael@0 480 } catch (Exception e) {
michael@0 481 Logger.warn(LOG_TAG, "Got exception fetching Sync account parameters; throwing.");
michael@0 482 throw new CredentialException.MissingAllCredentialsException(e);
michael@0 483 }
michael@0 484 }
michael@0 485
michael@0 486 /**
michael@0 487 * Bug 790931: create an intent announcing that a Sync account will be
michael@0 488 * deleted.
michael@0 489 * <p>
michael@0 490 * This intent <b>must</b> be broadcast with secure permissions, because it
michael@0 491 * contains sensitive user information including the Sync account password and
michael@0 492 * Sync key.
michael@0 493 * <p>
michael@0 494 * Version 1 of the created intent includes extras with keys
michael@0 495 * <code>Constants.JSON_KEY_VERSION</code>,
michael@0 496 * <code>Constants.JSON_KEY_TIMESTAMP</code>, and
michael@0 497 * <code>Constants.JSON_KEY_ACCOUNT</code> (which is the Android Account name,
michael@0 498 * not the encoded Sync Account name).
michael@0 499 * <p>
michael@0 500 * If possible, it contains the key <code>Constants.JSON_KEY_PAYLOAD</code>
michael@0 501 * with value the Sync account parameters as JSON, <b>except the Sync key has
michael@0 502 * been replaced with the empty string</b>. (We replace, rather than remove,
michael@0 503 * the Sync key because SyncAccountParameters expects a non-null Sync key.)
michael@0 504 *
michael@0 505 * @see SyncAccountParameters#asJSON
michael@0 506 *
michael@0 507 * @param context
michael@0 508 * Android context.
michael@0 509 * @param accountManager
michael@0 510 * Android account manager.
michael@0 511 * @param account
michael@0 512 * Android account being removed.
michael@0 513 * @return <code>Intent</code> to broadcast.
michael@0 514 */
michael@0 515 public static Intent makeSyncAccountDeletedIntent(final Context context, final AccountManager accountManager, final Account account) {
michael@0 516 final Intent intent = new Intent(SyncConstants.SYNC_ACCOUNT_DELETED_ACTION);
michael@0 517
michael@0 518 intent.putExtra(Constants.JSON_KEY_VERSION, Long.valueOf(SyncConstants.SYNC_ACCOUNT_DELETED_INTENT_VERSION));
michael@0 519 intent.putExtra(Constants.JSON_KEY_TIMESTAMP, Long.valueOf(System.currentTimeMillis()));
michael@0 520 intent.putExtra(Constants.JSON_KEY_ACCOUNT, account.name);
michael@0 521
michael@0 522 SyncAccountParameters accountParameters = null;
michael@0 523 try {
michael@0 524 accountParameters = SyncAccounts.blockingFromAndroidAccountV0(context, accountManager, account);
michael@0 525 } catch (Exception e) {
michael@0 526 Logger.warn(LOG_TAG, "Caught exception fetching account parameters.", e);
michael@0 527 }
michael@0 528
michael@0 529 if (accountParameters != null) {
michael@0 530 ExtendedJSONObject json = accountParameters.asJSON();
michael@0 531 json.put(Constants.JSON_KEY_SYNCKEY, ""); // Reduce attack surface area by removing Sync key.
michael@0 532 intent.putExtra(Constants.JSON_KEY_PAYLOAD, json.toJSONString());
michael@0 533 }
michael@0 534
michael@0 535 return intent;
michael@0 536 }
michael@0 537
michael@0 538 /**
michael@0 539 * Synchronously fetch SharedPreferences of a profile associated with a Sync
michael@0 540 * account.
michael@0 541 * <p>
michael@0 542 * Safe to call from main thread.
michael@0 543 *
michael@0 544 * @param context
michael@0 545 * Android context.
michael@0 546 * @param accountManager
michael@0 547 * Android account manager.
michael@0 548 * @param account
michael@0 549 * Android Account.
michael@0 550 * @param product
michael@0 551 * package.
michael@0 552 * @param profile
michael@0 553 * of account.
michael@0 554 * @param version
michael@0 555 * number.
michael@0 556 * @return SharedPreferences associated with Sync account.
michael@0 557 * @throws CredentialException
michael@0 558 * @throws NoSuchAlgorithmException
michael@0 559 * @throws UnsupportedEncodingException
michael@0 560 */
michael@0 561 public static SharedPreferences blockingPrefsFromAndroidAccountV0(final Context context, final AccountManager accountManager, final Account account,
michael@0 562 final String product, final String profile, final long version)
michael@0 563 throws CredentialException, NoSuchAlgorithmException, UnsupportedEncodingException {
michael@0 564 SyncAccountParameters params = SyncAccounts.blockingFromAndroidAccountV0(context, accountManager, account);
michael@0 565 String prefsPath = Utils.getPrefsPath(product, params.username, params.serverURL, profile, version);
michael@0 566
michael@0 567 return context.getSharedPreferences(prefsPath, Utils.SHARED_PREFERENCES_MODE);
michael@0 568 }
michael@0 569
michael@0 570 /**
michael@0 571 * Synchronously fetch SharedPreferences of a profile associated with the
michael@0 572 * default Firefox profile of a Sync Account.
michael@0 573 * <p>
michael@0 574 * Uses the default package, default profile, and current version.
michael@0 575 * <p>
michael@0 576 * Safe to call from main thread.
michael@0 577 *
michael@0 578 * @param context
michael@0 579 * Android context.
michael@0 580 * @param accountManager
michael@0 581 * Android account manager.
michael@0 582 * @param account
michael@0 583 * Android Account.
michael@0 584 * @return SharedPreferences associated with Sync account.
michael@0 585 * @throws CredentialException
michael@0 586 * @throws NoSuchAlgorithmException
michael@0 587 * @throws UnsupportedEncodingException
michael@0 588 */
michael@0 589 public static SharedPreferences blockingPrefsFromDefaultProfileV0(final Context context, final AccountManager accountManager, final Account account)
michael@0 590 throws CredentialException, NoSuchAlgorithmException, UnsupportedEncodingException {
michael@0 591 final String product = GlobalConstants.BROWSER_INTENT_PACKAGE;
michael@0 592 final String profile = Constants.DEFAULT_PROFILE;
michael@0 593 final long version = SyncConfiguration.CURRENT_PREFS_VERSION;
michael@0 594
michael@0 595 return blockingPrefsFromAndroidAccountV0(context, accountManager, account, product, profile, version);
michael@0 596 }
michael@0 597 }

mercurial