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 17 package com.android.server.backup.utils; 18 19 import static com.android.server.backup.BackupManagerService.BACKUP_MANIFEST_VERSION; 20 import static com.android.server.backup.BackupManagerService.TAG; 21 22 import android.content.pm.PackageInfo; 23 import android.content.pm.PackageManager; 24 import android.content.pm.Signature; 25 import android.content.pm.SigningInfo; 26 import android.os.Build; 27 import android.os.ParcelFileDescriptor; 28 import android.util.Slog; 29 import android.util.StringBuilderPrinter; 30 31 import java.io.DataInputStream; 32 import java.io.EOFException; 33 import java.io.File; 34 import java.io.FileInputStream; 35 import java.io.FileOutputStream; 36 import java.io.IOException; 37 import java.io.OutputStream; 38 39 /** 40 * Low-level utility methods for full backup. 41 */ 42 public class FullBackupUtils { 43 /** 44 * Reads data from pipe and writes it to the stream in chunks of up to 32KB. 45 * 46 * @param inPipe - pipe to read the data from. 47 * @param out - stream to write the data to. 48 * @throws IOException - in case of an error. 49 */ routeSocketDataToOutput(ParcelFileDescriptor inPipe, OutputStream out)50 public static void routeSocketDataToOutput(ParcelFileDescriptor inPipe, OutputStream out) 51 throws IOException { 52 // We do not take close() responsibility for the pipe FD 53 FileInputStream raw = new FileInputStream(inPipe.getFileDescriptor()); 54 DataInputStream in = new DataInputStream(raw); 55 56 byte[] buffer = new byte[32 * 1024]; 57 int chunkTotal; 58 while ((chunkTotal = in.readInt()) > 0) { 59 while (chunkTotal > 0) { 60 int toRead = (chunkTotal > buffer.length) ? buffer.length : chunkTotal; 61 int nRead = in.read(buffer, 0, toRead); 62 if (nRead < 0) { 63 Slog.e(TAG, "Unexpectedly reached end of file while reading data"); 64 throw new EOFException(); 65 } 66 out.write(buffer, 0, nRead); 67 chunkTotal -= nRead; 68 } 69 } 70 } 71 72 /** 73 * Writes app manifest to the given manifest file. 74 * 75 * @param pkg - app package, which manifest to write. 76 * @param packageManager - {@link PackageManager} instance. 77 * @param manifestFile - target manifest file. 78 * @param withApk - whether include apk or not. 79 * @param withWidgets - whether to write widgets data. 80 * @throws IOException - in case of an error. 81 */ 82 // TODO: withWidgets is not used, decide whether it is needed. writeAppManifest(PackageInfo pkg, PackageManager packageManager, File manifestFile, boolean withApk, boolean withWidgets)83 public static void writeAppManifest(PackageInfo pkg, PackageManager packageManager, 84 File manifestFile, boolean withApk, boolean withWidgets) throws IOException { 85 // Manifest format. All data are strings ending in LF: 86 // BACKUP_MANIFEST_VERSION, currently 1 87 // 88 // Version 1: 89 // package name 90 // package's versionCode 91 // platform versionCode 92 // getInstallerPackageName() for this package (maybe empty) 93 // boolean: "1" if archive includes .apk; any other string means not 94 // number of signatures == N 95 // N*: signature byte array in ascii format per Signature.toCharsString() 96 StringBuilder builder = new StringBuilder(4096); 97 StringBuilderPrinter printer = new StringBuilderPrinter(builder); 98 99 printer.println(Integer.toString(BACKUP_MANIFEST_VERSION)); 100 printer.println(pkg.packageName); 101 printer.println(Long.toString(pkg.getLongVersionCode())); 102 printer.println(Integer.toString(Build.VERSION.SDK_INT)); 103 104 String installerName = packageManager.getInstallerPackageName(pkg.packageName); 105 printer.println((installerName != null) ? installerName : ""); 106 107 printer.println(withApk ? "1" : "0"); 108 109 // write the signature block 110 SigningInfo signingInfo = pkg.signingInfo; 111 if (signingInfo == null) { 112 printer.println("0"); 113 } else { 114 // retrieve the newest sigs to write 115 // TODO (b/73988180) use entire signing history in case of rollbacks 116 Signature[] signatures = signingInfo.getApkContentsSigners(); 117 printer.println(Integer.toString(signatures.length)); 118 for (Signature sig : signatures) { 119 printer.println(sig.toCharsString()); 120 } 121 } 122 123 FileOutputStream outstream = new FileOutputStream(manifestFile); 124 outstream.write(builder.toString().getBytes()); 125 outstream.close(); 126 127 // We want the manifest block in the archive stream to be idempotent: 128 // each time we generate a backup stream for the app, we want the manifest 129 // block to be identical. The underlying tar mechanism sees it as a file, 130 // though, and will propagate its mtime, causing the tar header to vary. 131 // Avoid this problem by pinning the mtime to zero. 132 manifestFile.setLastModified(0); 133 } 134 } 135