package com.android.server.testing.shadows; import android.app.backup.BackupDataOutput; import android.app.backup.FullBackup; import android.app.backup.FullBackupDataOutput; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; import org.robolectric.shadow.api.Shadow; import java.io.IOException; import java.io.ObjectOutputStream; import java.nio.ByteBuffer; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; /** * Shadow for {@link FullBackup}. Used to emulate the native method {@link * FullBackup#backupToTar(String, String, String, String, String, FullBackupDataOutput)}. Relies on * the shadow {@link ShadowBackupDataOutput}, which must be included in tests that use this shadow. */ @Implements(FullBackup.class) public class ShadowFullBackup { /** * Reads data from the specified file at {@code path} and writes it to the {@code output}. Does * not match the native implementation, and only partially simulates TAR format. Used solely for * passing backup data for testing purposes. * *
Note: Only handles the {@code path} denoting a file and not a directory like the real * implementation. */ @Implementation protected static int backupToTar( String packageName, String domain, String linkdomain, String rootpath, String path, FullBackupDataOutput output) { BackupDataOutput backupDataOutput = output.getData(); try { Path file = Paths.get(path); byte[] data = Files.readAllBytes(file); backupDataOutput.writeEntityHeader("key", data.length); // Partially simulate TAR header (not all fields included). We use a 512 byte block for // the header to follow the TAR convention and to have a consistent size block to help // with separating the header from the data. ByteBuffer tarBlock = ByteBuffer.wrap(new byte[512]); String tarPath = "apps/" + packageName + (domain == null ? "" : "/" + domain) + path; tarBlock.put(tarPath.getBytes()); // file path tarBlock.putInt(0x1ff); // file mode tarBlock.putLong(Files.size(file)); // file size tarBlock.putLong(Files.getLastModifiedTime(file).toMillis()); // last modified time tarBlock.putInt(0); // file type // Write TAR header directly to the BackupDataOutput's output stream. ShadowBackupDataOutput shadowBackupDataOutput = Shadow.extract(backupDataOutput); ObjectOutputStream outputStream = shadowBackupDataOutput.getOutputStream(); outputStream.write(tarBlock.array()); outputStream.flush(); backupDataOutput.writeEntityData(data, data.length); } catch (IOException e) { throw new AssertionError(e); } return 0; } }