11e64246154401f7798c9070f74e45de6891c9d62Transformer Teampackage com.google.android.libraries.backup.shadow; 21e64246154401f7798c9070f74e45de6891c9d62Transformer Team 31e64246154401f7798c9070f74e45de6891c9d62Transformer Teamimport static android.content.Context.MODE_PRIVATE; 41e64246154401f7798c9070f74e45de6891c9d62Transformer Team 51e64246154401f7798c9070f74e45de6891c9d62Transformer Teamimport android.app.backup.SharedPreferencesBackupHelper; 61e64246154401f7798c9070f74e45de6891c9d62Transformer Teamimport android.content.Context; 71e64246154401f7798c9070f74e45de6891c9d62Transformer Teamimport android.content.SharedPreferences.Editor; 81e64246154401f7798c9070f74e45de6891c9d62Transformer Teamimport android.util.Log; 91e64246154401f7798c9070f74e45de6891c9d62Transformer Teamimport com.google.android.libraries.backup.PersistentBackupAgentHelper; 101e64246154401f7798c9070f74e45de6891c9d62Transformer Teamimport com.google.common.annotations.VisibleForTesting; 111e64246154401f7798c9070f74e45de6891c9d62Transformer Teamimport com.google.common.base.Preconditions; 121e64246154401f7798c9070f74e45de6891c9d62Transformer Teamimport com.google.common.collect.ImmutableMap; 131e64246154401f7798c9070f74e45de6891c9d62Transformer Teamimport com.google.common.collect.ImmutableSet; 141e64246154401f7798c9070f74e45de6891c9d62Transformer Teamimport java.lang.reflect.Field; 151e64246154401f7798c9070f74e45de6891c9d62Transformer Teamimport java.util.Map; 161e64246154401f7798c9070f74e45de6891c9d62Transformer Teamimport java.util.Set; 171e64246154401f7798c9070f74e45de6891c9d62Transformer Team 181e64246154401f7798c9070f74e45de6891c9d62Transformer Team/** 191e64246154401f7798c9070f74e45de6891c9d62Transformer Team * Representation of {@link SharedPreferencesBackupHelper} configuration used for testing. This 201e64246154401f7798c9070f74e45de6891c9d62Transformer Team * class simulates backing up and restoring shared preferences by storing them in memory. 211e64246154401f7798c9070f74e45de6891c9d62Transformer Team * 221e64246154401f7798c9070f74e45de6891c9d62Transformer Team * <p>{@see BackupAgentHelperShadow} 231e64246154401f7798c9070f74e45de6891c9d62Transformer Team */ 241e64246154401f7798c9070f74e45de6891c9d62Transformer Teampublic class SharedPreferencesBackupHelperSimulator extends BackupHelperSimulator { 251e64246154401f7798c9070f74e45de6891c9d62Transformer Team private static final String TAG = "SharedPreferencesBackup"; 261e64246154401f7798c9070f74e45de6891c9d62Transformer Team 271e64246154401f7798c9070f74e45de6891c9d62Transformer Team /** Shared preferences file names which should be backed up/restored. */ 281e64246154401f7798c9070f74e45de6891c9d62Transformer Team private final Set<String> prefGroups; 291e64246154401f7798c9070f74e45de6891c9d62Transformer Team 301e64246154401f7798c9070f74e45de6891c9d62Transformer Team private SharedPreferencesBackupHelperSimulator(String keyPrefix, Set<String> prefGroups) { 311e64246154401f7798c9070f74e45de6891c9d62Transformer Team super(keyPrefix); 321e64246154401f7798c9070f74e45de6891c9d62Transformer Team this.prefGroups = Preconditions.checkNotNull(prefGroups); 331e64246154401f7798c9070f74e45de6891c9d62Transformer Team } 341e64246154401f7798c9070f74e45de6891c9d62Transformer Team 351e64246154401f7798c9070f74e45de6891c9d62Transformer Team public static SharedPreferencesBackupHelperSimulator fromPreferenceGroups( 361e64246154401f7798c9070f74e45de6891c9d62Transformer Team String keyPrefix, Set<String> prefGroups) { 371e64246154401f7798c9070f74e45de6891c9d62Transformer Team return new SharedPreferencesBackupHelperSimulator(keyPrefix, prefGroups); 381e64246154401f7798c9070f74e45de6891c9d62Transformer Team } 391e64246154401f7798c9070f74e45de6891c9d62Transformer Team 401e64246154401f7798c9070f74e45de6891c9d62Transformer Team public static SharedPreferencesBackupHelperSimulator fromHelper( 411e64246154401f7798c9070f74e45de6891c9d62Transformer Team String keyPrefix, SharedPreferencesBackupHelper helper) { 421e64246154401f7798c9070f74e45de6891c9d62Transformer Team return new SharedPreferencesBackupHelperSimulator( 431e64246154401f7798c9070f74e45de6891c9d62Transformer Team keyPrefix, extractPreferenceGroupsFromHelper(helper)); 441e64246154401f7798c9070f74e45de6891c9d62Transformer Team } 451e64246154401f7798c9070f74e45de6891c9d62Transformer Team 461e64246154401f7798c9070f74e45de6891c9d62Transformer Team @VisibleForTesting 471e64246154401f7798c9070f74e45de6891c9d62Transformer Team static Set<String> extractPreferenceGroupsFromHelper(SharedPreferencesBackupHelper helper) { 481e64246154401f7798c9070f74e45de6891c9d62Transformer Team try { 491e64246154401f7798c9070f74e45de6891c9d62Transformer Team Field prefGroupsField = SharedPreferencesBackupHelper.class.getDeclaredField("mPrefGroups"); 501e64246154401f7798c9070f74e45de6891c9d62Transformer Team prefGroupsField.setAccessible(true); 511e64246154401f7798c9070f74e45de6891c9d62Transformer Team return ImmutableSet.copyOf((String[]) prefGroupsField.get(helper)); 521e64246154401f7798c9070f74e45de6891c9d62Transformer Team } catch (ReflectiveOperationException e) { 531e64246154401f7798c9070f74e45de6891c9d62Transformer Team throw new IllegalStateException( 541e64246154401f7798c9070f74e45de6891c9d62Transformer Team "Failed to construct SharedPreferencesBackupHelperSimulator", e); 551e64246154401f7798c9070f74e45de6891c9d62Transformer Team } 561e64246154401f7798c9070f74e45de6891c9d62Transformer Team } 571e64246154401f7798c9070f74e45de6891c9d62Transformer Team 581e64246154401f7798c9070f74e45de6891c9d62Transformer Team /** Collection of backed up shared preferences. */ 591e64246154401f7798c9070f74e45de6891c9d62Transformer Team public static class SharedPreferencesBackupData { 601e64246154401f7798c9070f74e45de6891c9d62Transformer Team /** Map from shared preferences file names to key-value preference maps. */ 611e64246154401f7798c9070f74e45de6891c9d62Transformer Team private final Map<String, Map<String, ?>> preferences; 621e64246154401f7798c9070f74e45de6891c9d62Transformer Team 631e64246154401f7798c9070f74e45de6891c9d62Transformer Team public SharedPreferencesBackupData(Map<String, Map<String, ?>> data) { 641e64246154401f7798c9070f74e45de6891c9d62Transformer Team this.preferences = Preconditions.checkNotNull(data); 651e64246154401f7798c9070f74e45de6891c9d62Transformer Team } 661e64246154401f7798c9070f74e45de6891c9d62Transformer Team 671e64246154401f7798c9070f74e45de6891c9d62Transformer Team @Override 681e64246154401f7798c9070f74e45de6891c9d62Transformer Team public boolean equals(Object obj) { 691e64246154401f7798c9070f74e45de6891c9d62Transformer Team return obj instanceof SharedPreferencesBackupData 701e64246154401f7798c9070f74e45de6891c9d62Transformer Team && preferences.equals(((SharedPreferencesBackupData) obj).preferences); 711e64246154401f7798c9070f74e45de6891c9d62Transformer Team } 721e64246154401f7798c9070f74e45de6891c9d62Transformer Team 731e64246154401f7798c9070f74e45de6891c9d62Transformer Team @Override 741e64246154401f7798c9070f74e45de6891c9d62Transformer Team public int hashCode() { 751e64246154401f7798c9070f74e45de6891c9d62Transformer Team return preferences.hashCode(); 761e64246154401f7798c9070f74e45de6891c9d62Transformer Team } 771e64246154401f7798c9070f74e45de6891c9d62Transformer Team 781e64246154401f7798c9070f74e45de6891c9d62Transformer Team public Map<String, Map<String, ?>> getPreferences() { 791e64246154401f7798c9070f74e45de6891c9d62Transformer Team return preferences; 801e64246154401f7798c9070f74e45de6891c9d62Transformer Team } 811e64246154401f7798c9070f74e45de6891c9d62Transformer Team } 821e64246154401f7798c9070f74e45de6891c9d62Transformer Team 831e64246154401f7798c9070f74e45de6891c9d62Transformer Team @Override 841e64246154401f7798c9070f74e45de6891c9d62Transformer Team public Object backup(Context context) { 851e64246154401f7798c9070f74e45de6891c9d62Transformer Team ImmutableMap.Builder<String, Map<String, ?>> dataToBackupBuilder = ImmutableMap.builder(); 861e64246154401f7798c9070f74e45de6891c9d62Transformer Team for (String prefGroup : prefGroups) { 871e64246154401f7798c9070f74e45de6891c9d62Transformer Team Map<String, ?> prefs = context.getSharedPreferences(prefGroup, MODE_PRIVATE).getAll(); 881e64246154401f7798c9070f74e45de6891c9d62Transformer Team if (prefs.isEmpty()) { 891e64246154401f7798c9070f74e45de6891c9d62Transformer Team Log.w(TAG, "Shared prefs \"" + prefGroup + "\" are empty. The helper \"" + keyPrefix 901e64246154401f7798c9070f74e45de6891c9d62Transformer Team + "\" assumes this is due to a missing (rather than empty) shared preferences file."); 911e64246154401f7798c9070f74e45de6891c9d62Transformer Team continue; 921e64246154401f7798c9070f74e45de6891c9d62Transformer Team } 931e64246154401f7798c9070f74e45de6891c9d62Transformer Team ImmutableMap.Builder<String, Object> prefsData = ImmutableMap.builder(); 941e64246154401f7798c9070f74e45de6891c9d62Transformer Team for (Map.Entry<String, ?> prefEntry : prefs.entrySet()) { 951e64246154401f7798c9070f74e45de6891c9d62Transformer Team String key = prefEntry.getKey(); 961e64246154401f7798c9070f74e45de6891c9d62Transformer Team Object value = prefEntry.getValue(); 971e64246154401f7798c9070f74e45de6891c9d62Transformer Team if (value instanceof Set) { 981e64246154401f7798c9070f74e45de6891c9d62Transformer Team value = ImmutableSet.copyOf((Set<?>) value); 991e64246154401f7798c9070f74e45de6891c9d62Transformer Team } 1001e64246154401f7798c9070f74e45de6891c9d62Transformer Team prefsData.put(key, value); 1011e64246154401f7798c9070f74e45de6891c9d62Transformer Team } 1021e64246154401f7798c9070f74e45de6891c9d62Transformer Team dataToBackupBuilder.put(prefGroup, prefsData.build()); 1031e64246154401f7798c9070f74e45de6891c9d62Transformer Team } 1041e64246154401f7798c9070f74e45de6891c9d62Transformer Team return new SharedPreferencesBackupData(dataToBackupBuilder.build()); 1051e64246154401f7798c9070f74e45de6891c9d62Transformer Team } 1061e64246154401f7798c9070f74e45de6891c9d62Transformer Team 1071e64246154401f7798c9070f74e45de6891c9d62Transformer Team @Override 1081e64246154401f7798c9070f74e45de6891c9d62Transformer Team public void restore(Context context, Object data) { 1091e64246154401f7798c9070f74e45de6891c9d62Transformer Team if (!(data instanceof SharedPreferencesBackupData)) { 1101e64246154401f7798c9070f74e45de6891c9d62Transformer Team throw new IllegalArgumentException("Invalid type of files to restore in helper \"" 1111e64246154401f7798c9070f74e45de6891c9d62Transformer Team + keyPrefix + "\": " + data.getClass()); 1121e64246154401f7798c9070f74e45de6891c9d62Transformer Team } 1131e64246154401f7798c9070f74e45de6891c9d62Transformer Team 1141e64246154401f7798c9070f74e45de6891c9d62Transformer Team Map<String, Map<String, ?>> prefsToRestore = 1151e64246154401f7798c9070f74e45de6891c9d62Transformer Team ((SharedPreferencesBackupData) data).getPreferences(); 1161e64246154401f7798c9070f74e45de6891c9d62Transformer Team 1171e64246154401f7798c9070f74e45de6891c9d62Transformer Team // Display a warning when missing/empty preferences are restored onto non-empty preferences. 1181e64246154401f7798c9070f74e45de6891c9d62Transformer Team for (String prefGroup : prefGroups) { 1191e64246154401f7798c9070f74e45de6891c9d62Transformer Team if (context.getSharedPreferences(prefGroup, MODE_PRIVATE).getAll().isEmpty()) { 1201e64246154401f7798c9070f74e45de6891c9d62Transformer Team continue; 1211e64246154401f7798c9070f74e45de6891c9d62Transformer Team } 1221e64246154401f7798c9070f74e45de6891c9d62Transformer Team Map<String, ?> prefsData = prefsToRestore.get(prefGroup); 1231e64246154401f7798c9070f74e45de6891c9d62Transformer Team if (prefsData == null) { 1241e64246154401f7798c9070f74e45de6891c9d62Transformer Team Log.w(TAG, "Non-empty shared prefs \"" + prefGroup + "\" will NOT be cleared by helper \"" 1251e64246154401f7798c9070f74e45de6891c9d62Transformer Team + keyPrefix + "\" because the corresponding file is missing in the restored data."); 1261e64246154401f7798c9070f74e45de6891c9d62Transformer Team } else if (prefsData.isEmpty()) { 1271e64246154401f7798c9070f74e45de6891c9d62Transformer Team Log.w(TAG, "Non-empty shared prefs \"" + prefGroup + "\" will be cleared by helper \"" 1281e64246154401f7798c9070f74e45de6891c9d62Transformer Team + keyPrefix + "\" because the corresponding file is empty in the restored data."); 1291e64246154401f7798c9070f74e45de6891c9d62Transformer Team } 1301e64246154401f7798c9070f74e45de6891c9d62Transformer Team } 1311e64246154401f7798c9070f74e45de6891c9d62Transformer Team 1321e64246154401f7798c9070f74e45de6891c9d62Transformer Team for (Map.Entry<String, Map<String, ?>> restoreEntry : prefsToRestore.entrySet()) { 1331e64246154401f7798c9070f74e45de6891c9d62Transformer Team String prefGroup = restoreEntry.getKey(); 1341e64246154401f7798c9070f74e45de6891c9d62Transformer Team if (!prefGroups.contains(prefGroup)) { 1351e64246154401f7798c9070f74e45de6891c9d62Transformer Team Log.w(TAG, "Shared prefs \"" + prefGroup + "\" ignored by helper \"" + keyPrefix + "\"."); 1361e64246154401f7798c9070f74e45de6891c9d62Transformer Team continue; 1371e64246154401f7798c9070f74e45de6891c9d62Transformer Team } 1381e64246154401f7798c9070f74e45de6891c9d62Transformer Team Map<String, ?> prefsData = restoreEntry.getValue(); 1391e64246154401f7798c9070f74e45de6891c9d62Transformer Team Editor editor = context.getSharedPreferences(prefGroup, MODE_PRIVATE).edit().clear(); 1401e64246154401f7798c9070f74e45de6891c9d62Transformer Team for (Map.Entry<String, ?> prefEntry : prefsData.entrySet()) { 1411e64246154401f7798c9070f74e45de6891c9d62Transformer Team PersistentBackupAgentHelper.putSharedPreference( 1421e64246154401f7798c9070f74e45de6891c9d62Transformer Team editor, prefEntry.getKey(), prefEntry.getValue()); 1431e64246154401f7798c9070f74e45de6891c9d62Transformer Team } 1441e64246154401f7798c9070f74e45de6891c9d62Transformer Team editor.apply(); 1451e64246154401f7798c9070f74e45de6891c9d62Transformer Team } 1461e64246154401f7798c9070f74e45de6891c9d62Transformer Team } 1471e64246154401f7798c9070f74e45de6891c9d62Transformer Team} 148