1 package com.android.server.testing.shadows; 2 3 import android.app.backup.BackupDataOutput; 4 import android.app.backup.FullBackup; 5 import android.app.backup.FullBackupDataOutput; 6 7 import org.robolectric.annotation.Implementation; 8 import org.robolectric.annotation.Implements; 9 import org.robolectric.shadow.api.Shadow; 10 11 import java.io.IOException; 12 import java.io.ObjectOutputStream; 13 import java.nio.ByteBuffer; 14 import java.nio.file.Files; 15 import java.nio.file.Path; 16 import java.nio.file.Paths; 17 18 /** 19 * Shadow for {@link FullBackup}. Used to emulate the native method {@link 20 * FullBackup#backupToTar(String, String, String, String, String, FullBackupDataOutput)}. Relies on 21 * the shadow {@link ShadowBackupDataOutput}, which must be included in tests that use this shadow. 22 */ 23 @Implements(FullBackup.class) 24 public class ShadowFullBackup { 25 /** 26 * Reads data from the specified file at {@code path} and writes it to the {@code output}. Does 27 * not match the native implementation, and only partially simulates TAR format. Used solely for 28 * passing backup data for testing purposes. 29 * 30 * <p>Note: Only handles the {@code path} denoting a file and not a directory like the real 31 * implementation. 32 */ 33 @Implementation backupToTar( String packageName, String domain, String linkdomain, String rootpath, String path, FullBackupDataOutput output)34 protected static int backupToTar( 35 String packageName, 36 String domain, 37 String linkdomain, 38 String rootpath, 39 String path, 40 FullBackupDataOutput output) { 41 BackupDataOutput backupDataOutput = output.getData(); 42 try { 43 Path file = Paths.get(path); 44 byte[] data = Files.readAllBytes(file); 45 backupDataOutput.writeEntityHeader("key", data.length); 46 47 // Partially simulate TAR header (not all fields included). We use a 512 byte block for 48 // the header to follow the TAR convention and to have a consistent size block to help 49 // with separating the header from the data. 50 ByteBuffer tarBlock = ByteBuffer.wrap(new byte[512]); 51 String tarPath = "apps/" + packageName + (domain == null ? "" : "/" + domain) + path; 52 tarBlock.put(tarPath.getBytes()); // file path 53 tarBlock.putInt(0x1ff); // file mode 54 tarBlock.putLong(Files.size(file)); // file size 55 tarBlock.putLong(Files.getLastModifiedTime(file).toMillis()); // last modified time 56 tarBlock.putInt(0); // file type 57 58 // Write TAR header directly to the BackupDataOutput's output stream. 59 ShadowBackupDataOutput shadowBackupDataOutput = Shadow.extract(backupDataOutput); 60 ObjectOutputStream outputStream = shadowBackupDataOutput.getOutputStream(); 61 outputStream.write(tarBlock.array()); 62 outputStream.flush(); 63 64 backupDataOutput.writeEntityData(data, data.length); 65 } catch (IOException e) { 66 throw new AssertionError(e); 67 } 68 return 0; 69 } 70 } 71