package com.google.android.libraries.backup.shadow; import android.app.backup.FileBackupHelper; import android.content.Context; import android.util.Log; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.io.Files; import java.io.File; import java.io.IOException; import java.lang.reflect.Field; import java.util.Arrays; import java.util.Map; import java.util.Set; /** * Representation of {@link FileBackupHelper} configuration used for testing. This class simulates * backing up and restoring files by storing their contents in memory. * *

{@see BackupAgentHelperShadow} */ public class FileBackupHelperSimulator extends BackupHelperSimulator { private static final String TAG = "FileBackupHelperSimulat"; /** Filenames which should be backed up/restored. */ private final Set fileNames; private FileBackupHelperSimulator(String keyPrefix, Set fileNames) { super(keyPrefix); this.fileNames = Preconditions.checkNotNull(fileNames); } public static FileBackupHelperSimulator fromFileNames(String keyPrefix, Set fileNames) { return new FileBackupHelperSimulator(keyPrefix, fileNames); } public static FileBackupHelperSimulator fromHelper(String keyPrefix, FileBackupHelper helper) { return new FileBackupHelperSimulator(keyPrefix, extractFileNamesFromHelper(helper)); } @VisibleForTesting static Set extractFileNamesFromHelper(FileBackupHelper helper) { try { Field filesField = FileBackupHelper.class.getDeclaredField("mFiles"); filesField.setAccessible(true); return ImmutableSet.copyOf((String[]) filesField.get(helper)); } catch (ReflectiveOperationException e) { throw new IllegalStateException(e); } } /** Collection of backed up files. */ public static class FilesBackupData { /** Map from file names to their backed up contents. */ private final Map files; public FilesBackupData(Map files) { this.files = Preconditions.checkNotNull(files); } @Override public boolean equals(Object obj) { return obj instanceof FilesBackupData && files.equals(((FilesBackupData) obj).files); } @Override public int hashCode() { return files.hashCode(); } public Map getFiles() { return files; } } /** Single backed up file. */ public static class FileBackupContents { private final byte[] bytes; public FileBackupContents(byte[] bytes) { this.bytes = Preconditions.checkNotNull(bytes); } @Override public boolean equals(Object obj) { return obj instanceof FileBackupContents && Arrays.equals(bytes, ((FileBackupContents) obj).bytes); } @Override public int hashCode() { return Arrays.hashCode(bytes); } public static FileBackupContents readFromFile(File file) { return new FileBackupContents(readBytesFromFile(file)); } public void writeToFile(File file) { writeBytesToFile(file, bytes); } public static byte[] readBytesFromFile(File file) { try { return Files.toByteArray(file); } catch (IOException e) { throw new IllegalStateException(e); } } public static void writeBytesToFile(File file, byte[] bytes) { try { Files.write(bytes, file); } catch (IOException e) { throw new IllegalStateException(e); } } } @Override public Object backup(Context context) { File base = context.getFilesDir(); ImmutableMap.Builder dataToBackupBuilder = ImmutableMap.builder(); for (String fileName : fileNames) { File file = new File(base, fileName); if (!file.exists()) { Log.w(TAG, "File \"" + fileName + "\" not found by helper \"" + keyPrefix + "\"."); continue; } dataToBackupBuilder.put(fileName, FileBackupContents.readFromFile(file)); } return new FilesBackupData(dataToBackupBuilder.build()); } @Override public void restore(Context context, Object data) { if (!(data instanceof FilesBackupData)) { throw new IllegalArgumentException("Invalid type of files to restore in helper \"" + keyPrefix + "\": " + data.getClass()); } File base = context.getFilesDir(); Map dataToRestore = ((FilesBackupData) data).getFiles(); for (Map.Entry restoreEntry : dataToRestore.entrySet()) { String fileName = restoreEntry.getKey(); if (!fileNames.contains(fileName)) { Log.w(TAG, "File \"" + fileName + "\" ignored by helper \"" + keyPrefix + "\"."); continue; } FileBackupContents contents = restoreEntry.getValue(); File file = new File(base, fileName); contents.writeToFile(file); } } }