mobile/android/base/sync/config/AccountPickler.java

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 package org.mozilla.gecko.sync.config;
     7 import java.io.FileOutputStream;
     8 import java.io.PrintStream;
    10 import org.mozilla.gecko.background.common.log.Logger;
    11 import org.mozilla.gecko.sync.ExtendedJSONObject;
    12 import org.mozilla.gecko.sync.Utils;
    13 import org.mozilla.gecko.sync.setup.Constants;
    14 import org.mozilla.gecko.sync.setup.SyncAccounts;
    15 import org.mozilla.gecko.sync.setup.SyncAccounts.SyncAccountParameters;
    17 import android.accounts.Account;
    18 import android.content.Context;
    20 /**
    21  * Bug 768102: Android deletes Account objects when the Authenticator that owns
    22  * the Account disappears. This happens when an App is installed to the SD card
    23  * and the SD card is un-mounted or the device is rebooted.
    24  * <p>
    25  * Bug 769745: Work around this by pickling the current Sync account data every
    26  * sync.
    27  * <p>
    28  * Bug 735842: Work around this by un-pickling when we check if Sync accounts
    29  * exist (called from Fennec).
    30  * <p>
    31  * Android just doesn't support installing Apps that define long-lived Services
    32  * and/or own Account types onto the SD card. The documentation says not to do
    33  * it. There are hordes of developers who want to do it, and have tried to
    34  * register for almost every "package installation changed" broadcast intent
    35  * that Android supports. They all explicitly state that the package that has
    36  * changed does *not* receive the broadcast intent, thereby preventing an App
    37  * from re-establishing its state.
    38  * <p>
    39  * <a href="http://developer.android.com/guide/topics/data/install-location.html">Reference.</a>
    40  * <p>
    41  * <b>Quote</b>: Your AbstractThreadedSyncAdapter and all its sync functionality
    42  * will not work until external storage is remounted.
    43  * <p>
    44  * <b>Quote</b>: Your running Service will be killed and will not be restarted
    45  * when external storage is remounted. You can, however, register for the
    46  * ACTION_EXTERNAL_APPLICATIONS_AVAILABLE broadcast Intent, which will notify
    47  * your application when applications installed on external storage have become
    48  * available to the system again. At which time, you can restart your Service.
    49  * <p>
    50  * Problem: <a href="http://code.google.com/p/android/issues/detail?id=8485">that intent doesn't work</a>!
    51  */
    52 public class AccountPickler {
    53   public static final String LOG_TAG = "AccountPickler";
    55   public static final long VERSION = 1;
    57   /**
    58    * Remove Sync account persisted to disk.
    59    *
    60    * @param context Android context.
    61    * @param filename name of persisted pickle file; must not contain path separators.
    62    * @return <code>true</code> if given pickle existed and was successfully deleted.
    63    */
    64   public static boolean deletePickle(final Context context, final String filename) {
    65     return context.deleteFile(filename);
    66   }
    68   /**
    69    * Persist Sync account to disk as a JSON object.
    70    * <p>
    71    * JSON object has keys:
    72    * <ul>
    73    * <li><code>Constants.JSON_KEY_ACCOUNT</code>: the Sync account's un-encoded username,
    74    * like "test@mozilla.com".</li>
    75    *
    76    * <li><code>Constants.JSON_KEY_PASSWORD</code>: the Sync account's password;</li>
    77    *
    78    * <li><code>Constants.JSON_KEY_SERVER</code>: the Sync account's server;</li>
    79    *
    80    * <li><code>Constants.JSON_KEY_SYNCKEY</code>: the Sync account's sync key;</li>
    81    *
    82    * <li><code>Constants.JSON_KEY_CLUSTER</code>: the Sync account's cluster (may be null);</li>
    83    *
    84    * <li><code>Constants.JSON_KEY_CLIENT_NAME</code>: the Sync account's client name (may be null);</li>
    85    *
    86    * <li><code>Constants.JSON_KEY_CLIENT_GUID</code>: the Sync account's client GUID (may be null);</li>
    87    *
    88    * <li><code>Constants.JSON_KEY_SYNC_AUTOMATICALLY</code>: true if the Android Account is syncing automically;</li>
    89    *
    90    * <li><code>Constants.JSON_KEY_VERSION</code>: version of this file;</li>
    91    *
    92    * <li><code>Constants.JSON_KEY_TIMESTAMP</code>: when this file was written.</li>
    93    * </ul>
    94    *
    95    *
    96    * @param context Android context.
    97    * @param filename name of file to persist to; must not contain path separators.
    98    * @param params the Sync account's parameters.
    99    * @param syncAutomatically whether the Android Account object is syncing automatically.
   100    */
   101   public static void pickle(final Context context, final String filename,
   102       final SyncAccountParameters params, final boolean syncAutomatically) {
   103     final ExtendedJSONObject o = params.asJSON();
   104     o.put(Constants.JSON_KEY_SYNC_AUTOMATICALLY, Boolean.valueOf(syncAutomatically));
   105     o.put(Constants.JSON_KEY_VERSION, new Long(VERSION));
   106     o.put(Constants.JSON_KEY_TIMESTAMP, new Long(System.currentTimeMillis()));
   108     PrintStream ps = null;
   109     try {
   110       final FileOutputStream fos = context.openFileOutput(filename, Context.MODE_PRIVATE);
   111       ps = new PrintStream(fos);
   112       ps.print(o.toJSONString());
   113       Logger.debug(LOG_TAG, "Persisted " + o.keySet().size() + " account settings to " + filename + ".");
   114     } catch (Exception e) {
   115       Logger.warn(LOG_TAG, "Caught exception persisting account settings to " + filename + "; ignoring.", e);
   116     } finally {
   117       if (ps != null) {
   118         ps.close();
   119       }
   120     }
   121   }
   123   /**
   124    * Create Android account from saved JSON object.
   125    *
   126    * @param context
   127    *          Android context.
   128    * @param filename
   129    *          name of file to read from; must not contain path separators.
   130    * @return created Android account, or null on error.
   131    */
   132   public static Account unpickle(final Context context, final String filename) {
   133     final String jsonString = Utils.readFile(context, filename);
   134     if (jsonString == null) {
   135       Logger.info(LOG_TAG, "Pickle file '" + filename + "' not found; aborting.");
   136       return null;
   137     }
   139     ExtendedJSONObject json = null;
   140     try {
   141       json = ExtendedJSONObject.parseJSONObject(jsonString);
   142     } catch (Exception e) {
   143       Logger.warn(LOG_TAG, "Got exception reading pickle file '" + filename + "'; aborting.", e);
   144       return null;
   145     }
   147     SyncAccountParameters params = null;
   148     try {
   149       // Null checking of inputs is done in constructor.
   150       params = new SyncAccountParameters(context, null, json);
   151     } catch (IllegalArgumentException e) {
   152       Logger.warn(LOG_TAG, "Un-pickled data included null username, password, or serverURL; aborting.", e);
   153       return null;
   154     }
   156     // Default to syncing automatically.
   157     boolean syncAutomatically = true;
   158     if (json.containsKey(Constants.JSON_KEY_SYNC_AUTOMATICALLY)) {
   159       if (Boolean.FALSE.equals(json.get(Constants.JSON_KEY_SYNC_AUTOMATICALLY))) {
   160         syncAutomatically = false;
   161       }
   162     }
   164     final Account account = SyncAccounts.createSyncAccountPreservingExistingPreferences(params, syncAutomatically);
   165     if (account == null) {
   166       Logger.warn(LOG_TAG, "Failed to add Android Account; aborting.");
   167       return null;
   168     }
   170     Integer version   = json.getIntegerSafely(Constants.JSON_KEY_VERSION);
   171     Integer timestamp = json.getIntegerSafely(Constants.JSON_KEY_TIMESTAMP);
   172     if (version == null || timestamp == null) {
   173       Logger.warn(LOG_TAG, "Did not find version or timestamp in pickle file; ignoring.");
   174       version = new Integer(-1);
   175       timestamp = new Integer(-1);
   176     }
   178     Logger.info(LOG_TAG, "Un-pickled Android account named " + params.username + " (version " + version + ", pickled at " + timestamp + ").");
   180     return account;
   181   }
   182 }

mercurial