xref: /aosp_15_r20/frameworks/base/services/robotests/src/com/android/server/testing/shadows/ShadowFullBackup.java (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
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