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