mobile/android/base/sync/config/ConfigurationMigrator.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.

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.config;
michael@0 6
michael@0 7 import java.util.HashMap;
michael@0 8 import java.util.Map;
michael@0 9 import java.util.Map.Entry;
michael@0 10
michael@0 11 import org.mozilla.gecko.background.common.log.Logger;
michael@0 12 import org.mozilla.gecko.sync.SyncConfiguration;
michael@0 13 import org.mozilla.gecko.sync.Utils;
michael@0 14
michael@0 15 import android.accounts.Account;
michael@0 16 import android.accounts.AccountManager;
michael@0 17 import android.content.Context;
michael@0 18 import android.content.SharedPreferences;
michael@0 19 import android.content.SharedPreferences.Editor;
michael@0 20
michael@0 21 /**
michael@0 22 * Migrate Sync preferences between versions.
michael@0 23 * <p>
michael@0 24 * The original preferences were un-versioned; we refer to that as "version 0".
michael@0 25 * The original preferences were stored in three places:
michael@0 26 * <ul>
michael@0 27 * <li>most prefs were kept in per-Sync account Android shared prefs;</li>
michael@0 28 * <li>some prefs were kept in per-App Android shared prefs;</li>
michael@0 29 * <li>some client prefs were kept in the (assumed unique) Android Account.</li>
michael@0 30 * </ul>
michael@0 31 * <p>
michael@0 32 * Post version 0, all preferences are stored in per-Sync account Android shared prefs.
michael@0 33 */
michael@0 34 public class ConfigurationMigrator {
michael@0 35 public static final String LOG_TAG = "ConfigMigrator";
michael@0 36
michael@0 37 /**
michael@0 38 * Copy and rename preferences.
michael@0 39 *
michael@0 40 * @param from source.
michael@0 41 * @param to sink.
michael@0 42 * @param map map from old preference names to new preference names.
michael@0 43 * @return the number of preferences migrated.
michael@0 44 */
michael@0 45 protected static int copyPreferences(final SharedPreferences from, final Map<String, String> map, final Editor to) {
michael@0 46 int count = 0;
michael@0 47
michael@0 48 // SharedPreferences has no way to get a key/value pair without specifying the value type, so we do this instead.
michael@0 49 for (Entry<String, ?> entry : from.getAll().entrySet()) {
michael@0 50 String fromKey = entry.getKey();
michael@0 51 String toKey = map.get(fromKey);
michael@0 52 if (toKey == null) {
michael@0 53 continue;
michael@0 54 }
michael@0 55
michael@0 56 Object value = entry.getValue();
michael@0 57 if (value instanceof Boolean) {
michael@0 58 to.putBoolean(toKey, ((Boolean) value).booleanValue());
michael@0 59 } else if (value instanceof Float) {
michael@0 60 to.putFloat(toKey, ((Float) value).floatValue());
michael@0 61 } else if (value instanceof Integer) {
michael@0 62 to.putInt(toKey, ((Integer) value).intValue());
michael@0 63 } else if (value instanceof Long) {
michael@0 64 to.putLong(toKey, ((Long) value).longValue());
michael@0 65 } else if (value instanceof String) {
michael@0 66 to.putString(toKey, (String) value);
michael@0 67 } else {
michael@0 68 // Do nothing -- perhaps SharedPreferences accepts types we don't know about.
michael@0 69 }
michael@0 70
michael@0 71 if (Logger.LOG_PERSONAL_INFORMATION) {
michael@0 72 Logger.debug(LOG_TAG, "Migrated '" + fromKey + "' to '" + toKey + "' (" + value + ").");
michael@0 73 } else {
michael@0 74 Logger.debug(LOG_TAG, "Migrated '" + fromKey + "' to '" + toKey + "'.");
michael@0 75 }
michael@0 76 count += 1;
michael@0 77 }
michael@0 78
michael@0 79 return count;
michael@0 80 }
michael@0 81
michael@0 82 protected final static String V0_PREF_CLUSTER_URL_IS_STALE = "clusterurlisstale";
michael@0 83 protected final static String V1_PREF_CLUSTER_URL_IS_STALE = V0_PREF_CLUSTER_URL_IS_STALE;
michael@0 84 protected final static String V0_PREF_EARLIEST_NEXT_SYNC = "earliestnextsync";
michael@0 85 protected final static String V1_PREF_EARLIEST_NEXT_SYNC = V0_PREF_EARLIEST_NEXT_SYNC;
michael@0 86
michael@0 87 /**
michael@0 88 * Extract version 0 preferences from per-App Android shared prefs and write to version 1 per-Sync account shared prefs.
michael@0 89 *
michael@0 90 * @param from per-App version 0 Android shared prefs.
michael@0 91 * @param to per-Sync account version 1 shared prefs.
michael@0 92 * @return the number of preferences migrated.
michael@0 93 * @throws Exception
michael@0 94 */
michael@0 95 protected static int upgradeGlobals0to1(final SharedPreferences from, final SharedPreferences to) throws Exception {
michael@0 96 Map<String, String> map = new HashMap<String, String>();
michael@0 97 map.put(V0_PREF_CLUSTER_URL_IS_STALE, V1_PREF_CLUSTER_URL_IS_STALE);
michael@0 98 map.put(V0_PREF_EARLIEST_NEXT_SYNC, V1_PREF_EARLIEST_NEXT_SYNC);
michael@0 99
michael@0 100 Editor editor = to.edit();
michael@0 101 int count = copyPreferences(from, map, editor);
michael@0 102 if (count > 0) {
michael@0 103 editor.commit();
michael@0 104 }
michael@0 105 return count;
michael@0 106 }
michael@0 107
michael@0 108 /**
michael@0 109 * Extract version 1 per-Sync account shared prefs and write to version 0 preferences from per-App Android shared prefs.
michael@0 110 *
michael@0 111 * @param from per-Sync account version 1 shared prefs.
michael@0 112 * @param to per-App version 0 Android shared prefs.
michael@0 113 * @return the number of preferences migrated.
michael@0 114 * @throws Exception
michael@0 115 */
michael@0 116 protected static int downgradeGlobals1to0(final SharedPreferences from, final SharedPreferences to) throws Exception {
michael@0 117 Map<String, String> map = new HashMap<String, String>();
michael@0 118 map.put(V1_PREF_CLUSTER_URL_IS_STALE, V0_PREF_CLUSTER_URL_IS_STALE);
michael@0 119 map.put(V1_PREF_EARLIEST_NEXT_SYNC, V0_PREF_EARLIEST_NEXT_SYNC);
michael@0 120
michael@0 121 Editor editor = to.edit();
michael@0 122 int count = copyPreferences(from, map, editor);
michael@0 123 if (count > 0) {
michael@0 124 editor.commit();
michael@0 125 }
michael@0 126 return count;
michael@0 127 }
michael@0 128
michael@0 129 protected static final String V0_PREF_ACCOUNT_GUID = "account.guid";
michael@0 130 protected static final String V1_PREF_ACCOUNT_GUID = V0_PREF_ACCOUNT_GUID;
michael@0 131 protected static final String V0_PREF_CLIENT_NAME = "account.clientName";
michael@0 132 protected static final String V1_PREF_CLIENT_NAME = V0_PREF_CLIENT_NAME;
michael@0 133 protected static final String V0_PREF_NUM_CLIENTS = "account.numClients";
michael@0 134 protected static final String V1_PREF_NUM_CLIENTS = V0_PREF_NUM_CLIENTS;
michael@0 135
michael@0 136 /**
michael@0 137 * Extract version 0 per-Android account user data and write to version 1 per-Sync account shared prefs.
michael@0 138 *
michael@0 139 * @param accountManager Android account manager.
michael@0 140 * @param account Android account.
michael@0 141 * @param to per-Sync account version 1 shared prefs.
michael@0 142 * @return the number of preferences migrated.
michael@0 143 * @throws Exception
michael@0 144 */
michael@0 145 protected static int upgradeAndroidAccount0to1(final AccountManager accountManager, final Account account, final SharedPreferences to) throws Exception {
michael@0 146 final String V0_PREF_ACCOUNT_GUID = "account.guid";
michael@0 147 final String V1_PREF_ACCOUNT_GUID = V0_PREF_ACCOUNT_GUID;
michael@0 148 final String V0_PREF_CLIENT_NAME = "account.clientName";
michael@0 149 final String V1_PREF_CLIENT_NAME = V0_PREF_CLIENT_NAME;
michael@0 150 final String V0_PREF_NUM_CLIENTS = "account.numClients";
michael@0 151 final String V1_PREF_NUM_CLIENTS = V0_PREF_NUM_CLIENTS;
michael@0 152
michael@0 153 String accountGUID = null;
michael@0 154 String clientName = null;
michael@0 155 long numClients = -1;
michael@0 156 try {
michael@0 157 accountGUID = accountManager.getUserData(account, V0_PREF_ACCOUNT_GUID);
michael@0 158 } catch (Exception e) {
michael@0 159 // Do nothing.
michael@0 160 }
michael@0 161 try {
michael@0 162 clientName = accountManager.getUserData(account, V0_PREF_CLIENT_NAME);
michael@0 163 } catch (Exception e) {
michael@0 164 // Do nothing.
michael@0 165 }
michael@0 166 try {
michael@0 167 numClients = Long.parseLong(accountManager.getUserData(account, V0_PREF_NUM_CLIENTS));
michael@0 168 } catch (Exception e) {
michael@0 169 // Do nothing.
michael@0 170 }
michael@0 171
michael@0 172 final Editor editor = to.edit();
michael@0 173
michael@0 174 int count = 0;
michael@0 175 if (accountGUID != null) {
michael@0 176 final String fromKey = V0_PREF_ACCOUNT_GUID;
michael@0 177 final String toKey = V1_PREF_ACCOUNT_GUID;
michael@0 178 if (Logger.LOG_PERSONAL_INFORMATION) {
michael@0 179 Logger.debug(LOG_TAG, "Migrated '" + fromKey + "' to '" + toKey + "' (" + accountGUID + ").");
michael@0 180 } else {
michael@0 181 Logger.debug(LOG_TAG, "Migrated '" + fromKey + "' to '" + toKey + "'.");
michael@0 182 }
michael@0 183 editor.putString(toKey, accountGUID);
michael@0 184 count += 1;
michael@0 185 }
michael@0 186 if (clientName != null) {
michael@0 187 final String fromKey = V0_PREF_CLIENT_NAME;
michael@0 188 final String toKey = V1_PREF_CLIENT_NAME;
michael@0 189 if (Logger.LOG_PERSONAL_INFORMATION) {
michael@0 190 Logger.debug(LOG_TAG, "Migrated '" + fromKey + "' to '" + toKey + "' (" + clientName + ").");
michael@0 191 } else {
michael@0 192 Logger.debug(LOG_TAG, "Migrated '" + fromKey + "' to '" + toKey + "'.");
michael@0 193 }
michael@0 194 editor.putString(toKey, clientName);
michael@0 195 count += 1;
michael@0 196 }
michael@0 197 if (numClients > -1) {
michael@0 198 final String fromKey = V0_PREF_NUM_CLIENTS;
michael@0 199 final String toKey = V1_PREF_NUM_CLIENTS;
michael@0 200 if (Logger.LOG_PERSONAL_INFORMATION) {
michael@0 201 Logger.debug(LOG_TAG, "Migrated '" + fromKey + "' to '" + toKey + "' (" + numClients + ").");
michael@0 202 } else {
michael@0 203 Logger.debug(LOG_TAG, "Migrated '" + fromKey + "' to '" + toKey + "'.");
michael@0 204 }
michael@0 205 editor.putLong(toKey, numClients);
michael@0 206 count += 1;
michael@0 207 }
michael@0 208
michael@0 209 if (count > 0) {
michael@0 210 editor.commit();
michael@0 211 }
michael@0 212 return count;
michael@0 213 }
michael@0 214
michael@0 215 /**
michael@0 216 * Extract version 1 per-Sync account shared prefs and write to version 0 per-Android account user data.
michael@0 217 *
michael@0 218 * @param from per-Sync account version 1 shared prefs.
michael@0 219 * @param accountManager Android account manager.
michael@0 220 * @param account Android account.
michael@0 221 * @return the number of preferences migrated.
michael@0 222 * @throws Exception
michael@0 223 */
michael@0 224 protected static int downgradeAndroidAccount1to0(final SharedPreferences from, final AccountManager accountManager, final Account account) throws Exception {
michael@0 225 final String accountGUID = from.getString(V1_PREF_ACCOUNT_GUID, null);
michael@0 226 final String clientName = from.getString(V1_PREF_CLIENT_NAME, null);
michael@0 227 final long numClients = from.getLong(V1_PREF_NUM_CLIENTS, -1L);
michael@0 228
michael@0 229 int count = 0;
michael@0 230 if (accountGUID != null) {
michael@0 231 Logger.debug(LOG_TAG, "Migrated account GUID.");
michael@0 232 accountManager.setUserData(account, V0_PREF_ACCOUNT_GUID, accountGUID);
michael@0 233 count += 1;
michael@0 234 }
michael@0 235 if (clientName != null) {
michael@0 236 Logger.debug(LOG_TAG, "Migrated client name.");
michael@0 237 accountManager.setUserData(account, V1_PREF_CLIENT_NAME, clientName);
michael@0 238 count += 1;
michael@0 239 }
michael@0 240 if (numClients > -1) {
michael@0 241 Logger.debug(LOG_TAG, "Migrated clients count.");
michael@0 242 accountManager.setUserData(account, V1_PREF_NUM_CLIENTS, new Long(numClients).toString());
michael@0 243 count += 1;
michael@0 244 }
michael@0 245 return count;
michael@0 246 }
michael@0 247
michael@0 248 /**
michael@0 249 * Extract version 0 per-Android account user data and write to version 1 per-Sync account shared prefs.
michael@0 250 *
michael@0 251 * @param from per-Sync account version 0 shared prefs.
michael@0 252 * @param to per-Sync account version 1 shared prefs.
michael@0 253 * @return the number of preferences migrated.
michael@0 254 * @throws Exception
michael@0 255 */
michael@0 256 protected static int upgradeShared0to1(final SharedPreferences from, final SharedPreferences to) {
michael@0 257 final Map<String, String> map = new HashMap<String, String>();
michael@0 258 final String[] prefs = new String [] {
michael@0 259 "syncID",
michael@0 260 "clusterURL",
michael@0 261 "enabledEngineNames",
michael@0 262
michael@0 263 "metaGlobalLastModified", "metaGlobalServerResponseBody",
michael@0 264
michael@0 265 "crypto5KeysLastModified", "crypto5KeysServerResponseBody",
michael@0 266
michael@0 267 "serverClientsTimestamp", "serverClientRecordTimestamp",
michael@0 268
michael@0 269 "forms.remote", "forms.local", "forms.syncID",
michael@0 270 "tabs.remote", "tabs.local", "tabs.syncID",
michael@0 271 "passwords.remote", "passwords.local", "passwords.syncID",
michael@0 272 "history.remote", "history.local", "history.syncID",
michael@0 273 "bookmarks.remote", "bookmarks.local", "bookmarks.syncID",
michael@0 274 };
michael@0 275 for (String pref : prefs) {
michael@0 276 map.put(pref, pref);
michael@0 277 }
michael@0 278
michael@0 279 Editor editor = to.edit();
michael@0 280 int count = copyPreferences(from, map, editor);
michael@0 281 if (count > 0) {
michael@0 282 editor.commit();
michael@0 283 }
michael@0 284 return count;
michael@0 285 }
michael@0 286
michael@0 287 /**
michael@0 288 * Extract version 1 per-Sync account shared prefs and write to version 0 per-Android account user data.
michael@0 289 *
michael@0 290 * @param from per-Sync account version 1 shared prefs.
michael@0 291 * @param to per-Sync account version 0 shared prefs.
michael@0 292 * @return the number of preferences migrated.
michael@0 293 * @throws Exception
michael@0 294 */
michael@0 295 protected static int downgradeShared1to0(final SharedPreferences from, final SharedPreferences to) {
michael@0 296 // Strictly a copy, no re-naming, no deletions -- so just invert.
michael@0 297 return upgradeShared0to1(from, to);
michael@0 298 }
michael@0 299
michael@0 300 public static void upgrade0to1(final Context context, final AccountManager accountManager, final Account account,
michael@0 301 final String product, final String username, final String serverURL, final String profile) throws Exception {
michael@0 302
michael@0 303 final String GLOBAL_SHARED_PREFS = "sync.prefs.global";
michael@0 304
michael@0 305 final SharedPreferences globalPrefs = context.getSharedPreferences(GLOBAL_SHARED_PREFS, Utils.SHARED_PREFERENCES_MODE);
michael@0 306 final SharedPreferences accountPrefs = Utils.getSharedPreferences(context, product, username, serverURL, profile, 0);
michael@0 307 final SharedPreferences newPrefs = Utils.getSharedPreferences(context, product, username, serverURL, profile, 1);
michael@0 308
michael@0 309 upgradeGlobals0to1(globalPrefs, newPrefs);
michael@0 310 upgradeAndroidAccount0to1(accountManager, account, newPrefs);
michael@0 311 upgradeShared0to1(accountPrefs, newPrefs);
michael@0 312 }
michael@0 313
michael@0 314 public static void downgrade1to0(final Context context, final AccountManager accountManager, final Account account,
michael@0 315 final String product, final String username, final String serverURL, final String profile) throws Exception {
michael@0 316
michael@0 317 final String GLOBAL_SHARED_PREFS = "sync.prefs.global";
michael@0 318
michael@0 319 final SharedPreferences globalPrefs = context.getSharedPreferences(GLOBAL_SHARED_PREFS, Utils.SHARED_PREFERENCES_MODE);
michael@0 320 final SharedPreferences accountPrefs = Utils.getSharedPreferences(context, product, username, serverURL, profile, 0);
michael@0 321 final SharedPreferences oldPrefs = Utils.getSharedPreferences(context, product, username, serverURL, profile, 1);
michael@0 322
michael@0 323 downgradeGlobals1to0(oldPrefs, globalPrefs);
michael@0 324 downgradeAndroidAccount1to0(oldPrefs, accountManager, account);
michael@0 325 downgradeShared1to0(oldPrefs, accountPrefs);
michael@0 326 }
michael@0 327
michael@0 328 /**
michael@0 329 * Migrate, if necessary, existing prefs to a certain version.
michael@0 330 * <p>
michael@0 331 * Stores current prefs version in Android shared prefs with root
michael@0 332 * "sync.prefs.version", which corresponds to the file
michael@0 333 * "sync.prefs.version.xml".
michael@0 334 *
michael@0 335 * @param desiredVersion
michael@0 336 * version to finish it.
michael@0 337 * @param context
michael@0 338 * @param accountManager
michael@0 339 * @param account
michael@0 340 * @param product
michael@0 341 * @param username
michael@0 342 * @param serverURL
michael@0 343 * @param profile
michael@0 344 * @throws Exception
michael@0 345 */
michael@0 346 public static void ensurePrefsAreVersion(final long desiredVersion,
michael@0 347 final Context context, final AccountManager accountManager, final Account account,
michael@0 348 final String product, final String username, final String serverURL, final String profile) throws Exception {
michael@0 349 if (desiredVersion < 0 || desiredVersion > SyncConfiguration.CURRENT_PREFS_VERSION) {
michael@0 350 throw new IllegalArgumentException("Cannot migrate to unknown version " + desiredVersion + ".");
michael@0 351 }
michael@0 352
michael@0 353 SharedPreferences versionPrefs = context.getSharedPreferences("sync.prefs.version", Utils.SHARED_PREFERENCES_MODE);
michael@0 354
michael@0 355 // We default to 0 since clients getting this code for the first time will
michael@0 356 // not have "sync.prefs.version.xml" *at all*, and upgrading when all old
michael@0 357 // data is missing is expected to be safe.
michael@0 358 long currentVersion = versionPrefs.getLong(SyncConfiguration.PREF_PREFS_VERSION, 0);
michael@0 359 if (currentVersion == desiredVersion) {
michael@0 360 Logger.info(LOG_TAG, "Current version (" + currentVersion + ") is desired version; no need to migrate.");
michael@0 361 return;
michael@0 362 }
michael@0 363
michael@0 364 if (currentVersion < 0 || currentVersion > SyncConfiguration.CURRENT_PREFS_VERSION) {
michael@0 365 throw new IllegalStateException("Cannot migrate from unknown version " + currentVersion + ".");
michael@0 366 }
michael@0 367
michael@0 368 // Now we're down to either version 0 or version 1.
michael@0 369 if (currentVersion == 0 && desiredVersion == 1) {
michael@0 370 Logger.info(LOG_TAG, "Upgrading from version 0 to version 1.");
michael@0 371 upgrade0to1(context, accountManager, account, product, username, serverURL, profile);
michael@0 372 } else if (currentVersion == 1 && desiredVersion == 0) {
michael@0 373 Logger.info(LOG_TAG, "Upgrading from version 0 to version 1.");
michael@0 374 upgrade0to1(context, accountManager, account, product, username, serverURL, profile);
michael@0 375 } else {
michael@0 376 Logger.warn(LOG_TAG, "Don't know how to migrate from version " + currentVersion + " to " + desiredVersion + ".");
michael@0 377 }
michael@0 378
michael@0 379 Logger.info(LOG_TAG, "Migrated from version " + currentVersion + " to version " + desiredVersion + ".");
michael@0 380 versionPrefs.edit().putLong(SyncConfiguration.PREF_PREFS_VERSION, desiredVersion).commit();
michael@0 381 }
michael@0 382 }

mercurial