FullBackupUtils.java revision fe4ae0c5b1bc3b31adc4cc2c5a0197e29e97b6bc
1/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
17package com.android.server.backup.utils;
18
19import static com.android.server.backup.BackupManagerService.BACKUP_MANIFEST_VERSION;
20import static com.android.server.backup.BackupManagerService.TAG;
21
22import android.content.pm.PackageInfo;
23import android.content.pm.PackageManager;
24import android.content.pm.Signature;
25import android.os.Build;
26import android.os.ParcelFileDescriptor;
27import android.util.Slog;
28import android.util.StringBuilderPrinter;
29
30import java.io.DataInputStream;
31import java.io.EOFException;
32import java.io.File;
33import java.io.FileInputStream;
34import java.io.FileOutputStream;
35import java.io.IOException;
36import java.io.OutputStream;
37
38/**
39 * Low-level utility methods for full backup.
40 */
41public class FullBackupUtils {
42    /**
43     * Reads data from pipe and writes it to the stream in chunks of up to 32KB.
44     *
45     * @param inPipe - pipe to read the data from.
46     * @param out - stream to write the data to.
47     * @throws IOException - in case of an error.
48     */
49    public static void routeSocketDataToOutput(ParcelFileDescriptor inPipe, OutputStream out)
50            throws IOException {
51        // We do not take close() responsibility for the pipe FD
52        FileInputStream raw = new FileInputStream(inPipe.getFileDescriptor());
53        DataInputStream in = new DataInputStream(raw);
54
55        byte[] buffer = new byte[32 * 1024];
56        int chunkTotal;
57        while ((chunkTotal = in.readInt()) > 0) {
58            while (chunkTotal > 0) {
59                int toRead = (chunkTotal > buffer.length) ? buffer.length : chunkTotal;
60                int nRead = in.read(buffer, 0, toRead);
61                if (nRead < 0) {
62                    Slog.e(TAG, "Unexpectedly reached end of file while reading data");
63                    throw new EOFException();
64                }
65                out.write(buffer, 0, nRead);
66                chunkTotal -= nRead;
67            }
68        }
69    }
70
71    /**
72     * Writes app manifest to the given manifest file.
73     *
74     * @param pkg - app package, which manifest to write.
75     * @param packageManager - {@link PackageManager} instance.
76     * @param manifestFile - target manifest file.
77     * @param withApk - whether include apk or not.
78     * @param withWidgets - whether to write widgets data.
79     * @throws IOException - in case of an error.
80     */
81    // TODO: withWidgets is not used, decide whether it is needed.
82    public static void writeAppManifest(PackageInfo pkg, PackageManager packageManager,
83            File manifestFile, boolean withApk, boolean withWidgets) throws IOException {
84        // Manifest format. All data are strings ending in LF:
85        //     BACKUP_MANIFEST_VERSION, currently 1
86        //
87        // Version 1:
88        //     package name
89        //     package's versionCode
90        //     platform versionCode
91        //     getInstallerPackageName() for this package (maybe empty)
92        //     boolean: "1" if archive includes .apk; any other string means not
93        //     number of signatures == N
94        // N*:    signature byte array in ascii format per Signature.toCharsString()
95        StringBuilder builder = new StringBuilder(4096);
96        StringBuilderPrinter printer = new StringBuilderPrinter(builder);
97
98        printer.println(Integer.toString(BACKUP_MANIFEST_VERSION));
99        printer.println(pkg.packageName);
100        printer.println(Long.toString(pkg.getLongVersionCode()));
101        printer.println(Integer.toString(Build.VERSION.SDK_INT));
102
103        String installerName = packageManager.getInstallerPackageName(pkg.packageName);
104        printer.println((installerName != null) ? installerName : "");
105
106        printer.println(withApk ? "1" : "0");
107        if (pkg.signatures == null) {
108            printer.println("0");
109        } else {
110            printer.println(Integer.toString(pkg.signatures.length));
111            for (Signature sig : pkg.signatures) {
112                printer.println(sig.toCharsString());
113            }
114        }
115
116        FileOutputStream outstream = new FileOutputStream(manifestFile);
117        outstream.write(builder.toString().getBytes());
118        outstream.close();
119
120        // We want the manifest block in the archive stream to be idempotent:
121        // each time we generate a backup stream for the app, we want the manifest
122        // block to be identical.  The underlying tar mechanism sees it as a file,
123        // though, and will propagate its mtime, causing the tar header to vary.
124        // Avoid this problem by pinning the mtime to zero.
125        manifestFile.setLastModified(0);
126    }
127}
128