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