backupDataMap;
attachBaseContextToAgentIfNecessary(agent);
agent.onCreate();
try {
agent.onBackup(null, null, null);
backupDataMap = backupDataMapToBackup.getAndSet(null);
} catch (IOException e) {
backupDataMapToBackup.set(null);
throw new IllegalStateException(e);
}
agent.onDestroy();
return backupDataMap;
}
/**
* Simulates key-value restore for the provided agent all the way from {@link
* BackupAgentHelper#onCreate} to {@link BackupAgentHelper#onDestroy} (both inclusive).
*
* Note: To make end-to-end tests more realistic, different {@link BackupAgentHelper}
* instances should be used in {@link #simulateBackup} and {@link #simulateRestore}.
*/
public static void simulateRestore(
BackupAgentHelper agent, Map backupDataMap, int appVersionCode) {
attachBaseContextToAgentIfNecessary(agent);
agent.onCreate();
assertTrue(backupDataMapToRestore.compareAndSet(null, backupDataMap));
try {
agent.onRestore(null, appVersionCode, null);
} catch (IOException e) {
throw new IllegalStateException(e);
} finally {
backupDataMapToRestore.set(null);
}
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
agent.onRestoreFinished();
}
agent.onDestroy();
}
private static void attachBaseContextToAgentIfNecessary(BackupAgentHelper agent) {
if (agent.getBaseContext() != null) {
return;
}
try {
// {@link BackupAgent#attach} is a hidden method, so we need to call it via reflection.
Method method = BackupAgent.class.getMethod("attach", Context.class);
method.invoke(agent, RuntimeEnvironment.application);
} catch (ReflectiveOperationException e) {
throw new IllegalStateException(e);
}
}
private final Map helperSimulators;
public BackupAgentHelperShadow() {
// Use a {@link TreeMap} to mirror the internal implementation of {@link BackupHelperDispatcher}
// as closely as possible.
helperSimulators = new TreeMap<>();
}
@RealObject private BackupAgentHelper realHelper;
@Implementation
public void addHelper(String keyPrefix, BackupHelper helper) {
Class extends BackupHelper> helperClass = helper.getClass();
final BackupHelperSimulator simulator;
if (helperClass == SharedPreferencesBackupHelper.class) {
simulator = SharedPreferencesBackupHelperSimulator.fromHelper(
keyPrefix, (SharedPreferencesBackupHelper) helper);
} else if (helperClass == FileBackupHelper.class) {
simulator = FileBackupHelperSimulator.fromHelper(keyPrefix, (FileBackupHelper) helper);
} else {
throw new UnsupportedOperationException(
"Unknown backup helper class for key prefix \"" + keyPrefix + "\": " + helperClass);
}
helperSimulators.put(keyPrefix, simulator);
}
@Implementation
public void onBackup(
ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState)
throws IOException {
ImmutableMap.Builder backupDataMapBuilder = ImmutableMap.builder();
for (Map.Entry simulatorEntry : helperSimulators.entrySet()) {
String keyPrefix = simulatorEntry.getKey();
BackupHelperSimulator simulator = simulatorEntry.getValue();
backupDataMapBuilder.put(keyPrefix, simulator.backup(realHelper));
}
assertTrue(backupDataMapToBackup.compareAndSet(null, backupDataMapBuilder.build()));
}
@Implementation
public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState)
throws IOException {
Map backupDataMap = backupDataMapToRestore.getAndSet(null);
assertNotNull(backupDataMap);
for (Map.Entry simulatorEntry : helperSimulators.entrySet()) {
String keyPrefix = simulatorEntry.getKey();
Object dataToRestore = backupDataMap.get(keyPrefix);
if (dataToRestore == null) {
Log.w(TAG, "No data to restore for key prefix: \"" + keyPrefix + "\".");
continue;
}
BackupHelperSimulator simulator = simulatorEntry.getValue();
simulator.restore(realHelper, dataToRestore);
}
}
}