FullBackupUtils.java revision 2c2c856b3a60e96e09261d2513b43acb7ff4b070
1package com.android.server.backup.utils;
2
3import android.content.pm.PackageInfo;
4import android.content.pm.PackageManager;
5import android.content.pm.Signature;
6import android.os.Build;
7import android.os.ParcelFileDescriptor;
8import android.util.StringBuilderPrinter;
9
10import com.android.server.backup.RefactoredBackupManagerService;
11
12import java.io.DataInputStream;
13import java.io.File;
14import java.io.FileInputStream;
15import java.io.FileOutputStream;
16import java.io.IOException;
17import java.io.OutputStream;
18
19/**
20 * Low-level utility methods for full backup.
21 */
22public class FullBackupUtils {
23    /**
24     * Reads data from pipe and writes it to the stream in chunks of up to 32KB.
25     *
26     * @param inPipe - pipe to read the data from.
27     * @param out - stream to write the data to.
28     * @throws IOException - in case of an error.
29     */
30    public static void routeSocketDataToOutput(ParcelFileDescriptor inPipe, OutputStream out)
31            throws IOException {
32        // We do not take close() responsibility for the pipe FD
33        FileInputStream raw = new FileInputStream(inPipe.getFileDescriptor());
34        DataInputStream in = new DataInputStream(raw);
35
36        byte[] buffer = new byte[32 * 1024];
37        int chunkTotal;
38        while ((chunkTotal = in.readInt()) > 0) {
39            while (chunkTotal > 0) {
40                int toRead = (chunkTotal > buffer.length) ? buffer.length : chunkTotal;
41                int nRead = in.read(buffer, 0, toRead);
42                out.write(buffer, 0, nRead);
43                chunkTotal -= nRead;
44            }
45        }
46    }
47
48    /**
49     * Writes app manifest to the given manifest file.
50     *
51     * @param pkg - app package, which manifest to write.
52     * @param packageManager - {@link PackageManager} instance.
53     * @param manifestFile - target manifest file.
54     * @param withApk - whether include apk or not.
55     * @param withWidgets - whether to write widgets data.
56     * @throws IOException - in case of an error.
57     */
58    // TODO: withWidgets is not used, decide whether it is needed.
59    public static void writeAppManifest(PackageInfo pkg, PackageManager packageManager,
60            File manifestFile, boolean withApk, boolean withWidgets) throws IOException {
61        // Manifest format. All data are strings ending in LF:
62        //     BACKUP_MANIFEST_VERSION, currently 1
63        //
64        // Version 1:
65        //     package name
66        //     package's versionCode
67        //     platform versionCode
68        //     getInstallerPackageName() for this package (maybe empty)
69        //     boolean: "1" if archive includes .apk; any other string means not
70        //     number of signatures == N
71        // N*:    signature byte array in ascii format per Signature.toCharsString()
72        StringBuilder builder = new StringBuilder(4096);
73        StringBuilderPrinter printer = new StringBuilderPrinter(builder);
74
75        printer.println(Integer.toString(RefactoredBackupManagerService.BACKUP_MANIFEST_VERSION));
76        printer.println(pkg.packageName);
77        printer.println(Integer.toString(pkg.versionCode));
78        printer.println(Integer.toString(Build.VERSION.SDK_INT));
79
80        String installerName = packageManager.getInstallerPackageName(pkg.packageName);
81        printer.println((installerName != null) ? installerName : "");
82
83        printer.println(withApk ? "1" : "0");
84        if (pkg.signatures == null) {
85            printer.println("0");
86        } else {
87            printer.println(Integer.toString(pkg.signatures.length));
88            for (Signature sig : pkg.signatures) {
89                printer.println(sig.toCharsString());
90            }
91        }
92
93        FileOutputStream outstream = new FileOutputStream(manifestFile);
94        outstream.write(builder.toString().getBytes());
95        outstream.close();
96
97        // We want the manifest block in the archive stream to be idempotent:
98        // each time we generate a backup stream for the app, we want the manifest
99        // block to be identical.  The underlying tar mechanism sees it as a file,
100        // though, and will propagate its mtime, causing the tar header to vary.
101        // Avoid this problem by pinning the mtime to zero.
102        manifestFile.setLastModified(0);
103    }
104}
105