1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/mobile/android/base/sync/setup/SyncAccounts.java Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,597 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +package org.mozilla.gecko.sync.setup; 1.9 + 1.10 +import java.io.File; 1.11 +import java.io.UnsupportedEncodingException; 1.12 +import java.security.NoSuchAlgorithmException; 1.13 + 1.14 +import org.mozilla.gecko.background.common.GlobalConstants; 1.15 +import org.mozilla.gecko.background.common.log.Logger; 1.16 +import org.mozilla.gecko.db.BrowserContract; 1.17 +import org.mozilla.gecko.sync.CredentialException; 1.18 +import org.mozilla.gecko.sync.ExtendedJSONObject; 1.19 +import org.mozilla.gecko.sync.SyncConfiguration; 1.20 +import org.mozilla.gecko.sync.SyncConstants; 1.21 +import org.mozilla.gecko.sync.ThreadPool; 1.22 +import org.mozilla.gecko.sync.Utils; 1.23 +import org.mozilla.gecko.sync.config.AccountPickler; 1.24 +import org.mozilla.gecko.sync.repositories.android.RepoUtils; 1.25 + 1.26 +import android.accounts.Account; 1.27 +import android.accounts.AccountManager; 1.28 +import android.content.ActivityNotFoundException; 1.29 +import android.content.ContentResolver; 1.30 +import android.content.Context; 1.31 +import android.content.Intent; 1.32 +import android.content.SharedPreferences; 1.33 +import android.content.pm.PackageManager.NameNotFoundException; 1.34 +import android.os.Bundle; 1.35 +import android.provider.Settings; 1.36 +import android.util.Log; 1.37 + 1.38 +/** 1.39 + * This class contains utilities that are of use to Fennec 1.40 + * and Sync setup activities. 1.41 + * <p> 1.42 + * Do not break these APIs without correcting upstream code! 1.43 + */ 1.44 +public class SyncAccounts { 1.45 + private static final String LOG_TAG = "SyncAccounts"; 1.46 + 1.47 + private static final String MOTO_BLUR_SETTINGS_ACTIVITY = "com.motorola.blur.settings.AccountsAndServicesPreferenceActivity"; 1.48 + private static final String MOTO_BLUR_PACKAGE = "com.motorola.blur.setup"; 1.49 + 1.50 + /** 1.51 + * Return Sync accounts. 1.52 + * 1.53 + * @param c 1.54 + * Android context. 1.55 + * @return Sync accounts. 1.56 + */ 1.57 + public static Account[] syncAccounts(final Context c) { 1.58 + return AccountManager.get(c).getAccountsByType(SyncConstants.ACCOUNTTYPE_SYNC); 1.59 + } 1.60 + 1.61 + /** 1.62 + * Returns true if a Sync account is set up, or we have a pickled Sync account 1.63 + * on disk that should be un-pickled (Bug 769745). If we have a pickled Sync 1.64 + * account, try to un-pickle it and create the corresponding Sync account. 1.65 + * <p> 1.66 + * Do not call this method from the main thread. 1.67 + */ 1.68 + public static boolean syncAccountsExist(Context c) { 1.69 + final boolean accountsExist = AccountManager.get(c).getAccountsByType(SyncConstants.ACCOUNTTYPE_SYNC).length > 0; 1.70 + if (accountsExist) { 1.71 + return true; 1.72 + } 1.73 + 1.74 + final File file = c.getFileStreamPath(Constants.ACCOUNT_PICKLE_FILENAME); 1.75 + if (!file.exists()) { 1.76 + return false; 1.77 + } 1.78 + 1.79 + // There is a small race window here: if the user creates a new Sync account 1.80 + // between our checks, this could erroneously report that no Sync accounts 1.81 + // exist. 1.82 + final Account account = AccountPickler.unpickle(c, Constants.ACCOUNT_PICKLE_FILENAME); 1.83 + return (account != null); 1.84 + } 1.85 + 1.86 + /** 1.87 + * This class encapsulates the parameters needed to create a new Firefox Sync 1.88 + * account. 1.89 + */ 1.90 + public static class SyncAccountParameters { 1.91 + public final Context context; 1.92 + public final AccountManager accountManager; 1.93 + 1.94 + 1.95 + public final String username; // services.sync.account 1.96 + public final String syncKey; // in password manager: "chrome://weave (Mozilla Services Encryption Passphrase)" 1.97 + public final String password; // in password manager: "chrome://weave (Mozilla Services Password)" 1.98 + public final String serverURL; // services.sync.serverURL 1.99 + public final String clusterURL; // services.sync.clusterURL 1.100 + public final String clientName; // services.sync.client.name 1.101 + public final String clientGuid; // services.sync.client.GUID 1.102 + 1.103 + /** 1.104 + * Encapsulate the parameters needed to create a new Firefox Sync account. 1.105 + * 1.106 + * @param context 1.107 + * the current <code>Context</code>; cannot be null. 1.108 + * @param accountManager 1.109 + * an <code>AccountManager</code> instance to use; if null, get it 1.110 + * from <code>context</code>. 1.111 + * @param username 1.112 + * the desired username; cannot be null. 1.113 + * @param syncKey 1.114 + * the desired sync key; cannot be null. 1.115 + * @param password 1.116 + * the desired password; cannot be null. 1.117 + * @param serverURL 1.118 + * the server URL to use; if null, use the default. 1.119 + * @param clusterURL 1.120 + * the cluster URL to use; if null, a fresh cluster URL will be 1.121 + * retrieved from the server during the next sync. 1.122 + * @param clientName 1.123 + * the client name; if null, a fresh client record will be uploaded 1.124 + * to the server during the next sync. 1.125 + * @param clientGuid 1.126 + * the client GUID; if null, a fresh client record will be uploaded 1.127 + * to the server during the next sync. 1.128 + */ 1.129 + public SyncAccountParameters(Context context, AccountManager accountManager, 1.130 + String username, String syncKey, String password, 1.131 + String serverURL, String clusterURL, 1.132 + String clientName, String clientGuid) { 1.133 + if (context == null) { 1.134 + throw new IllegalArgumentException("Null context passed to SyncAccountParameters constructor."); 1.135 + } 1.136 + if (username == null) { 1.137 + throw new IllegalArgumentException("Null username passed to SyncAccountParameters constructor."); 1.138 + } 1.139 + if (syncKey == null) { 1.140 + throw new IllegalArgumentException("Null syncKey passed to SyncAccountParameters constructor."); 1.141 + } 1.142 + if (password == null) { 1.143 + throw new IllegalArgumentException("Null password passed to SyncAccountParameters constructor."); 1.144 + } 1.145 + this.context = context; 1.146 + this.accountManager = accountManager; 1.147 + this.username = username; 1.148 + this.syncKey = syncKey; 1.149 + this.password = password; 1.150 + this.serverURL = serverURL; 1.151 + this.clusterURL = clusterURL; 1.152 + this.clientName = clientName; 1.153 + this.clientGuid = clientGuid; 1.154 + } 1.155 + 1.156 + public SyncAccountParameters(Context context, AccountManager accountManager, 1.157 + String username, String syncKey, String password, String serverURL) { 1.158 + this(context, accountManager, username, syncKey, password, serverURL, null, null, null); 1.159 + } 1.160 + 1.161 + public SyncAccountParameters(final Context context, final AccountManager accountManager, final ExtendedJSONObject o) { 1.162 + this(context, accountManager, 1.163 + o.getString(Constants.JSON_KEY_ACCOUNT), 1.164 + o.getString(Constants.JSON_KEY_SYNCKEY), 1.165 + o.getString(Constants.JSON_KEY_PASSWORD), 1.166 + o.getString(Constants.JSON_KEY_SERVER), 1.167 + o.getString(Constants.JSON_KEY_CLUSTER), 1.168 + o.getString(Constants.JSON_KEY_CLIENT_NAME), 1.169 + o.getString(Constants.JSON_KEY_CLIENT_GUID)); 1.170 + } 1.171 + 1.172 + public ExtendedJSONObject asJSON() { 1.173 + final ExtendedJSONObject o = new ExtendedJSONObject(); 1.174 + o.put(Constants.JSON_KEY_ACCOUNT, username); 1.175 + o.put(Constants.JSON_KEY_PASSWORD, password); 1.176 + o.put(Constants.JSON_KEY_SERVER, serverURL); 1.177 + o.put(Constants.JSON_KEY_SYNCKEY, syncKey); 1.178 + o.put(Constants.JSON_KEY_CLUSTER, clusterURL); 1.179 + o.put(Constants.JSON_KEY_CLIENT_NAME, clientName); 1.180 + o.put(Constants.JSON_KEY_CLIENT_GUID, clientGuid); 1.181 + return o; 1.182 + } 1.183 + } 1.184 + 1.185 + /** 1.186 + * Create a sync account, clearing any existing preferences, and set it to 1.187 + * sync automatically. 1.188 + * <p> 1.189 + * Do not call this method from the main thread. 1.190 + * 1.191 + * @param syncAccount 1.192 + * parameters of the account to be created. 1.193 + * @return created <code>Account</code>, or null if an error occurred and the 1.194 + * account could not be added. 1.195 + */ 1.196 + public static Account createSyncAccount(SyncAccountParameters syncAccount) { 1.197 + return createSyncAccount(syncAccount, true, true); 1.198 + } 1.199 + 1.200 + /** 1.201 + * Create a sync account, clearing any existing preferences. 1.202 + * <p> 1.203 + * Do not call this method from the main thread. 1.204 + * <p> 1.205 + * Intended for testing; use 1.206 + * <code>createSyncAccount(SyncAccountParameters)</code> instead. 1.207 + * 1.208 + * @param syncAccount 1.209 + * parameters of the account to be created. 1.210 + * @param syncAutomatically 1.211 + * whether to start syncing this Account automatically ( 1.212 + * <code>false</code> for test accounts). 1.213 + * @return created Android <code>Account</code>, or null if an error occurred 1.214 + * and the account could not be added. 1.215 + */ 1.216 + public static Account createSyncAccount(SyncAccountParameters syncAccount, 1.217 + boolean syncAutomatically) { 1.218 + return createSyncAccount(syncAccount, syncAutomatically, true); 1.219 + } 1.220 + 1.221 + public static Account createSyncAccountPreservingExistingPreferences(SyncAccountParameters syncAccount, 1.222 + boolean syncAutomatically) { 1.223 + return createSyncAccount(syncAccount, syncAutomatically, false); 1.224 + } 1.225 + 1.226 + /** 1.227 + * Create a sync account. 1.228 + * <p> 1.229 + * Do not call this method from the main thread. 1.230 + * <p> 1.231 + * Intended for testing; use 1.232 + * <code>createSyncAccount(SyncAccountParameters)</code> instead. 1.233 + * 1.234 + * @param syncAccount 1.235 + * parameters of the account to be created. 1.236 + * @param syncAutomatically 1.237 + * whether to start syncing this Account automatically ( 1.238 + * <code>false</code> for test accounts). 1.239 + * @param clearPreferences 1.240 + * <code>true</code> to clear existing preferences before creating. 1.241 + * @return created Android <code>Account</code>, or null if an error occurred 1.242 + * and the account could not be added. 1.243 + */ 1.244 + protected static Account createSyncAccount(SyncAccountParameters syncAccount, 1.245 + boolean syncAutomatically, boolean clearPreferences) { 1.246 + final Context context = syncAccount.context; 1.247 + final AccountManager accountManager = (syncAccount.accountManager == null) ? 1.248 + AccountManager.get(syncAccount.context) : syncAccount.accountManager; 1.249 + final String username = syncAccount.username; 1.250 + final String syncKey = syncAccount.syncKey; 1.251 + final String password = syncAccount.password; 1.252 + final String serverURL = (syncAccount.serverURL == null) ? 1.253 + SyncConstants.DEFAULT_AUTH_SERVER : syncAccount.serverURL; 1.254 + 1.255 + Logger.debug(LOG_TAG, "Using account manager " + accountManager); 1.256 + if (!RepoUtils.stringsEqual(syncAccount.serverURL, SyncConstants.DEFAULT_AUTH_SERVER)) { 1.257 + Logger.info(LOG_TAG, "Setting explicit server URL: " + serverURL); 1.258 + } 1.259 + 1.260 + final Account account = new Account(username, SyncConstants.ACCOUNTTYPE_SYNC); 1.261 + final Bundle userbundle = new Bundle(); 1.262 + 1.263 + // Add sync key and server URL. 1.264 + userbundle.putString(Constants.OPTION_SYNCKEY, syncKey); 1.265 + userbundle.putString(Constants.OPTION_SERVER, serverURL); 1.266 + Logger.debug(LOG_TAG, "Adding account for " + SyncConstants.ACCOUNTTYPE_SYNC); 1.267 + boolean result = false; 1.268 + try { 1.269 + result = accountManager.addAccountExplicitly(account, password, userbundle); 1.270 + } catch (SecurityException e) { 1.271 + // We use Log rather than Logger here to avoid possibly hiding these errors. 1.272 + final String message = e.getMessage(); 1.273 + if (message != null && (message.indexOf("is different than the authenticator's uid") > 0)) { 1.274 + Log.wtf(SyncConstants.GLOBAL_LOG_TAG, 1.275 + "Unable to create account. " + 1.276 + "If you have more than one version of " + 1.277 + "Firefox/Beta/Aurora/Nightly/Fennec installed, that's why.", 1.278 + e); 1.279 + } else { 1.280 + Log.e(SyncConstants.GLOBAL_LOG_TAG, "Unable to create account.", e); 1.281 + } 1.282 + } 1.283 + 1.284 + if (!result) { 1.285 + Logger.error(LOG_TAG, "Failed to add account " + account + "!"); 1.286 + return null; 1.287 + } 1.288 + Logger.debug(LOG_TAG, "Account " + account + " added successfully."); 1.289 + 1.290 + setSyncAutomatically(account, syncAutomatically); 1.291 + setIsSyncable(account, syncAutomatically); 1.292 + Logger.debug(LOG_TAG, "Set account to sync automatically? " + syncAutomatically + "."); 1.293 + 1.294 + try { 1.295 + final String product = GlobalConstants.BROWSER_INTENT_PACKAGE; 1.296 + final String profile = Constants.DEFAULT_PROFILE; 1.297 + final long version = SyncConfiguration.CURRENT_PREFS_VERSION; 1.298 + 1.299 + final SharedPreferences.Editor editor = Utils.getSharedPreferences(context, product, username, serverURL, profile, version).edit(); 1.300 + if (clearPreferences) { 1.301 + final String prefsPath = Utils.getPrefsPath(product, username, serverURL, profile, version); 1.302 + Logger.info(LOG_TAG, "Clearing preferences path " + prefsPath + " for this account."); 1.303 + editor.clear(); 1.304 + } 1.305 + 1.306 + if (syncAccount.clusterURL != null) { 1.307 + editor.putString(SyncConfiguration.PREF_CLUSTER_URL, syncAccount.clusterURL); 1.308 + } 1.309 + 1.310 + if (syncAccount.clientName != null && syncAccount.clientGuid != null) { 1.311 + Logger.debug(LOG_TAG, "Setting client name to " + syncAccount.clientName + " and client GUID to " + syncAccount.clientGuid + "."); 1.312 + editor.putString(SyncConfiguration.PREF_CLIENT_NAME, syncAccount.clientName); 1.313 + editor.putString(SyncConfiguration.PREF_ACCOUNT_GUID, syncAccount.clientGuid); 1.314 + } else { 1.315 + Logger.debug(LOG_TAG, "Client name and guid not both non-null, so not setting client data."); 1.316 + } 1.317 + 1.318 + editor.commit(); 1.319 + } catch (Exception e) { 1.320 + Logger.error(LOG_TAG, "Could not clear prefs path!", e); 1.321 + } 1.322 + return account; 1.323 + } 1.324 + 1.325 + public static void setIsSyncable(Account account, boolean isSyncable) { 1.326 + String authority = BrowserContract.AUTHORITY; 1.327 + ContentResolver.setIsSyncable(account, authority, isSyncable ? 1 : 0); 1.328 + } 1.329 + 1.330 + public static void setSyncAutomatically(Account account, boolean syncAutomatically) { 1.331 + if (syncAutomatically) { 1.332 + ContentResolver.setMasterSyncAutomatically(true); 1.333 + } 1.334 + 1.335 + String authority = BrowserContract.AUTHORITY; 1.336 + Logger.debug(LOG_TAG, "Setting authority " + authority + " to " + 1.337 + (syncAutomatically ? "" : "not ") + "sync automatically."); 1.338 + ContentResolver.setSyncAutomatically(account, authority, syncAutomatically); 1.339 + } 1.340 + 1.341 + public static void backgroundSetSyncAutomatically(final Account account, final boolean syncAutomatically) { 1.342 + ThreadPool.run(new Runnable() { 1.343 + @Override 1.344 + public void run() { 1.345 + setSyncAutomatically(account, syncAutomatically); 1.346 + } 1.347 + }); 1.348 + } 1.349 + /** 1.350 + * Bug 721760: try to start a vendor-specific Accounts & Sync activity on Moto 1.351 + * Blur devices. 1.352 + * <p> 1.353 + * Bug 773562: actually start and catch <code>ActivityNotFoundException</code>, 1.354 + * rather than just returning the <code>Intent</code> only, because some 1.355 + * Moto devices fail to start the activity. 1.356 + * 1.357 + * @param context 1.358 + * current Android context. 1.359 + * @param vendorPackage 1.360 + * vendor specific package name. 1.361 + * @param vendorClass 1.362 + * vendor specific class name. 1.363 + * @return null on failure, otherwise the <code>Intent</code> started. 1.364 + */ 1.365 + protected static Intent openVendorSyncSettings(Context context, final String vendorPackage, final String vendorClass) { 1.366 + try { 1.367 + final int contextFlags = Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY; 1.368 + Context foreignContext = context.createPackageContext(vendorPackage, contextFlags); 1.369 + Class<?> klass = foreignContext.getClassLoader().loadClass(vendorClass); 1.370 + 1.371 + final Intent intent = new Intent(foreignContext, klass); 1.372 + context.startActivity(intent); 1.373 + Logger.info(LOG_TAG, "Vendor package " + vendorPackage + " and class " + 1.374 + vendorClass + " found, and activity launched."); 1.375 + return intent; 1.376 + } catch (NameNotFoundException e) { 1.377 + Logger.debug(LOG_TAG, "Vendor package " + vendorPackage + " not found. Skipping."); 1.378 + } catch (ClassNotFoundException e) { 1.379 + Logger.debug(LOG_TAG, "Vendor package " + vendorPackage + " found but class " + 1.380 + vendorClass + " not found. Skipping.", e); 1.381 + } catch (ActivityNotFoundException e) { 1.382 + // Bug 773562 - android.content.ActivityNotFoundException on Motorola devices. 1.383 + Logger.warn(LOG_TAG, "Vendor package " + vendorPackage + " and class " + 1.384 + vendorClass + " found, but activity not launched. Skipping.", e); 1.385 + } catch (Exception e) { 1.386 + // Just in case. 1.387 + Logger.warn(LOG_TAG, "Caught exception launching activity from vendor package " + vendorPackage + 1.388 + " and class " + vendorClass + ". Ignoring.", e); 1.389 + } 1.390 + return null; 1.391 + } 1.392 + 1.393 + /** 1.394 + * Start Sync settings activity. 1.395 + * 1.396 + * @param context 1.397 + * current Android context. 1.398 + * @return the <code>Intent</code> started. 1.399 + */ 1.400 + public static Intent openSyncSettings(Context context) { 1.401 + // Bug 721760 - opening Sync settings takes user to Battery & Data Manager 1.402 + // on a variety of Motorola devices. This work around tries to load the 1.403 + // correct Intent by hand. Oh, Android. 1.404 + Intent intent = openVendorSyncSettings(context, MOTO_BLUR_PACKAGE, MOTO_BLUR_SETTINGS_ACTIVITY); 1.405 + if (intent != null) { 1.406 + return intent; 1.407 + } 1.408 + 1.409 + // Open default Sync settings activity. 1.410 + intent = new Intent(Settings.ACTION_SYNC_SETTINGS); 1.411 + // Bug 774233: do not start activity as a new task (second run fails on some HTC devices). 1.412 + context.startActivity(intent); // We should always find this Activity. 1.413 + return intent; 1.414 + } 1.415 + 1.416 + /** 1.417 + * Synchronously extract Sync account parameters from Android account version 1.418 + * 0, using plain auth token type. 1.419 + * <p> 1.420 + * Safe to call from main thread. 1.421 + * 1.422 + * @param context 1.423 + * Android context. 1.424 + * @param accountManager 1.425 + * Android account manager. 1.426 + * @param account 1.427 + * Android Account. 1.428 + * @return Sync account parameters, always non-null; fields username, 1.429 + * password, serverURL, and syncKey always non-null. 1.430 + */ 1.431 + public static SyncAccountParameters blockingFromAndroidAccountV0(final Context context, final AccountManager accountManager, final Account account) 1.432 + throws CredentialException { 1.433 + String username; 1.434 + try { 1.435 + username = Utils.usernameFromAccount(account.name); 1.436 + } catch (NoSuchAlgorithmException e) { 1.437 + throw new CredentialException.MissingCredentialException("username"); 1.438 + } catch (UnsupportedEncodingException e) { 1.439 + throw new CredentialException.MissingCredentialException("username"); 1.440 + } 1.441 + 1.442 + /* 1.443 + * If we are accessing an Account that we don't own, Android will throw an 1.444 + * unchecked <code>SecurityException</code> saying 1.445 + * "W FxSync(XXXX) java.lang.SecurityException: caller uid XXXXX is different than the authenticator's uid". 1.446 + * We catch that error and throw accordingly. 1.447 + */ 1.448 + String password; 1.449 + String syncKey; 1.450 + String serverURL; 1.451 + try { 1.452 + password = accountManager.getPassword(account); 1.453 + syncKey = accountManager.getUserData(account, Constants.OPTION_SYNCKEY); 1.454 + serverURL = accountManager.getUserData(account, Constants.OPTION_SERVER); 1.455 + } catch (SecurityException e) { 1.456 + Logger.warn(LOG_TAG, "Got security exception fetching Sync account parameters; throwing."); 1.457 + throw new CredentialException.MissingAllCredentialsException(e); 1.458 + } 1.459 + 1.460 + if (password == null && 1.461 + username == null && 1.462 + syncKey == null && 1.463 + serverURL == null) { 1.464 + throw new CredentialException.MissingAllCredentialsException(); 1.465 + } 1.466 + 1.467 + if (password == null) { 1.468 + throw new CredentialException.MissingCredentialException("password"); 1.469 + } 1.470 + 1.471 + if (syncKey == null) { 1.472 + throw new CredentialException.MissingCredentialException("syncKey"); 1.473 + } 1.474 + 1.475 + if (serverURL == null) { 1.476 + throw new CredentialException.MissingCredentialException("serverURL"); 1.477 + } 1.478 + 1.479 + try { 1.480 + // SyncAccountParameters constructor throws on null inputs. This shouldn't 1.481 + // happen, but let's be safe. 1.482 + return new SyncAccountParameters(context, accountManager, username, syncKey, password, serverURL); 1.483 + } catch (Exception e) { 1.484 + Logger.warn(LOG_TAG, "Got exception fetching Sync account parameters; throwing."); 1.485 + throw new CredentialException.MissingAllCredentialsException(e); 1.486 + } 1.487 + } 1.488 + 1.489 + /** 1.490 + * Bug 790931: create an intent announcing that a Sync account will be 1.491 + * deleted. 1.492 + * <p> 1.493 + * This intent <b>must</b> be broadcast with secure permissions, because it 1.494 + * contains sensitive user information including the Sync account password and 1.495 + * Sync key. 1.496 + * <p> 1.497 + * Version 1 of the created intent includes extras with keys 1.498 + * <code>Constants.JSON_KEY_VERSION</code>, 1.499 + * <code>Constants.JSON_KEY_TIMESTAMP</code>, and 1.500 + * <code>Constants.JSON_KEY_ACCOUNT</code> (which is the Android Account name, 1.501 + * not the encoded Sync Account name). 1.502 + * <p> 1.503 + * If possible, it contains the key <code>Constants.JSON_KEY_PAYLOAD</code> 1.504 + * with value the Sync account parameters as JSON, <b>except the Sync key has 1.505 + * been replaced with the empty string</b>. (We replace, rather than remove, 1.506 + * the Sync key because SyncAccountParameters expects a non-null Sync key.) 1.507 + * 1.508 + * @see SyncAccountParameters#asJSON 1.509 + * 1.510 + * @param context 1.511 + * Android context. 1.512 + * @param accountManager 1.513 + * Android account manager. 1.514 + * @param account 1.515 + * Android account being removed. 1.516 + * @return <code>Intent</code> to broadcast. 1.517 + */ 1.518 + public static Intent makeSyncAccountDeletedIntent(final Context context, final AccountManager accountManager, final Account account) { 1.519 + final Intent intent = new Intent(SyncConstants.SYNC_ACCOUNT_DELETED_ACTION); 1.520 + 1.521 + intent.putExtra(Constants.JSON_KEY_VERSION, Long.valueOf(SyncConstants.SYNC_ACCOUNT_DELETED_INTENT_VERSION)); 1.522 + intent.putExtra(Constants.JSON_KEY_TIMESTAMP, Long.valueOf(System.currentTimeMillis())); 1.523 + intent.putExtra(Constants.JSON_KEY_ACCOUNT, account.name); 1.524 + 1.525 + SyncAccountParameters accountParameters = null; 1.526 + try { 1.527 + accountParameters = SyncAccounts.blockingFromAndroidAccountV0(context, accountManager, account); 1.528 + } catch (Exception e) { 1.529 + Logger.warn(LOG_TAG, "Caught exception fetching account parameters.", e); 1.530 + } 1.531 + 1.532 + if (accountParameters != null) { 1.533 + ExtendedJSONObject json = accountParameters.asJSON(); 1.534 + json.put(Constants.JSON_KEY_SYNCKEY, ""); // Reduce attack surface area by removing Sync key. 1.535 + intent.putExtra(Constants.JSON_KEY_PAYLOAD, json.toJSONString()); 1.536 + } 1.537 + 1.538 + return intent; 1.539 + } 1.540 + 1.541 + /** 1.542 + * Synchronously fetch SharedPreferences of a profile associated with a Sync 1.543 + * account. 1.544 + * <p> 1.545 + * Safe to call from main thread. 1.546 + * 1.547 + * @param context 1.548 + * Android context. 1.549 + * @param accountManager 1.550 + * Android account manager. 1.551 + * @param account 1.552 + * Android Account. 1.553 + * @param product 1.554 + * package. 1.555 + * @param profile 1.556 + * of account. 1.557 + * @param version 1.558 + * number. 1.559 + * @return SharedPreferences associated with Sync account. 1.560 + * @throws CredentialException 1.561 + * @throws NoSuchAlgorithmException 1.562 + * @throws UnsupportedEncodingException 1.563 + */ 1.564 + public static SharedPreferences blockingPrefsFromAndroidAccountV0(final Context context, final AccountManager accountManager, final Account account, 1.565 + final String product, final String profile, final long version) 1.566 + throws CredentialException, NoSuchAlgorithmException, UnsupportedEncodingException { 1.567 + SyncAccountParameters params = SyncAccounts.blockingFromAndroidAccountV0(context, accountManager, account); 1.568 + String prefsPath = Utils.getPrefsPath(product, params.username, params.serverURL, profile, version); 1.569 + 1.570 + return context.getSharedPreferences(prefsPath, Utils.SHARED_PREFERENCES_MODE); 1.571 + } 1.572 + 1.573 + /** 1.574 + * Synchronously fetch SharedPreferences of a profile associated with the 1.575 + * default Firefox profile of a Sync Account. 1.576 + * <p> 1.577 + * Uses the default package, default profile, and current version. 1.578 + * <p> 1.579 + * Safe to call from main thread. 1.580 + * 1.581 + * @param context 1.582 + * Android context. 1.583 + * @param accountManager 1.584 + * Android account manager. 1.585 + * @param account 1.586 + * Android Account. 1.587 + * @return SharedPreferences associated with Sync account. 1.588 + * @throws CredentialException 1.589 + * @throws NoSuchAlgorithmException 1.590 + * @throws UnsupportedEncodingException 1.591 + */ 1.592 + public static SharedPreferences blockingPrefsFromDefaultProfileV0(final Context context, final AccountManager accountManager, final Account account) 1.593 + throws CredentialException, NoSuchAlgorithmException, UnsupportedEncodingException { 1.594 + final String product = GlobalConstants.BROWSER_INTENT_PACKAGE; 1.595 + final String profile = Constants.DEFAULT_PROFILE; 1.596 + final long version = SyncConfiguration.CURRENT_PREFS_VERSION; 1.597 + 1.598 + return blockingPrefsFromAndroidAccountV0(context, accountManager, account, product, profile, version); 1.599 + } 1.600 +}