From: Johan Toras Halseth Date: Fri, 3 Mar 2017 15:37:43 +0000 (+0000) Subject: Add support for key-value packages to adb backup/restore. X-Git-Tag: android-x86-8.1-r1~2182^2 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=b59a4b85ade3f1f408def6a0dd3dbb146225bdd7;p=android-x86%2Fframeworks-base.git Add support for key-value packages to adb backup/restore. For adding CTS tests for packages having key-value backup agents, we add support for key-value backups to the adb backup/restore command. Previously, packages not supporting fullbackup would just be skipped on this command. Now, by adding the -keyvalue flag to the adb backup command, packages supporting key-value will also be added to the resulting tarball. Similarly, if the tarball being supplied to adb restore contains data from key-value packages, it will be restored. This will later be utilized for writing CTS tests for such packages. Test: adb backup -includekeyvalue -all && adb restore backup.ab Change-Id: I7b4ccfb7072d01d29a888952145d7cce90a4f59e --- diff --git a/cmds/bu/src/com/android/commands/bu/Backup.java b/cmds/bu/src/com/android/commands/bu/Backup.java index ffc0f875c79c..db17b28b8182 100644 --- a/cmds/bu/src/com/android/commands/bu/Backup.java +++ b/cmds/bu/src/com/android/commands/bu/Backup.java @@ -53,15 +53,15 @@ public final class Backup { String arg = nextArg(); if (arg.equals("backup")) { - doFullBackup(OsConstants.STDOUT_FILENO); + doBackup(OsConstants.STDOUT_FILENO); } else if (arg.equals("restore")) { - doFullRestore(OsConstants.STDIN_FILENO); + doRestore(OsConstants.STDIN_FILENO); } else { Log.e(TAG, "Invalid operation '" + arg + "'"); } } - private void doFullBackup(int socketFd) { + private void doBackup(int socketFd) { ArrayList packages = new ArrayList(); boolean saveApks = false; boolean saveObbs = false; @@ -70,6 +70,7 @@ public final class Backup { boolean doWidgets = false; boolean allIncludesSystem = true; boolean doCompress = true; + boolean doKeyValue = false; String arg; while ((arg = nextArg()) != null) { @@ -100,6 +101,8 @@ public final class Backup { doCompress = true; } else if ("-nocompress".equals(arg)) { doCompress = false; + } else if ("-includekeyvalue".equals(arg)) { + doKeyValue = true; } else { Log.w(TAG, "Unknown backup flag " + arg); continue; @@ -123,8 +126,8 @@ public final class Backup { try { fd = ParcelFileDescriptor.adoptFd(socketFd); String[] packArray = new String[packages.size()]; - mBackupManager.fullBackup(fd, saveApks, saveObbs, saveShared, doWidgets, - doEverything, allIncludesSystem, doCompress, packages.toArray(packArray)); + mBackupManager.adbBackup(fd, saveApks, saveObbs, saveShared, doWidgets, doEverything, + allIncludesSystem, doCompress, doKeyValue, packages.toArray(packArray)); } catch (RemoteException e) { Log.e(TAG, "Unable to invoke backup manager for backup"); } finally { @@ -136,12 +139,12 @@ public final class Backup { } } - private void doFullRestore(int socketFd) { + private void doRestore(int socketFd) { // No arguments to restore ParcelFileDescriptor fd = null; try { fd = ParcelFileDescriptor.adoptFd(socketFd); - mBackupManager.fullRestore(fd); + mBackupManager.adbRestore(fd); } catch (RemoteException e) { Log.e(TAG, "Unable to invoke backup manager for restore"); } finally { diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java index 76828eeba785..a5dd5bd30d63 100644 --- a/core/java/android/app/backup/FullBackup.java +++ b/core/java/android/app/backup/FullBackup.java @@ -56,6 +56,7 @@ public class FullBackup { public static final String APK_TREE_TOKEN = "a"; public static final String OBB_TREE_TOKEN = "obb"; + public static final String KEY_VALUE_DATA_TOKEN = "k"; public static final String ROOT_TREE_TOKEN = "r"; public static final String FILES_TREE_TOKEN = "f"; diff --git a/core/java/android/app/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl index 59a941ac46c8..9c3b11009243 100644 --- a/core/java/android/app/backup/IBackupManager.aidl +++ b/core/java/android/app/backup/IBackupManager.aidl @@ -144,9 +144,10 @@ interface IBackupManager { void backupNow(); /** - * Write a full backup of the given package to the supplied file descriptor. + * Write a backup of the given package to the supplied file descriptor. * The fd may be a socket or other non-seekable destination. If no package names * are supplied, then every application on the device will be backed up to the output. + * Currently only used by the 'adb backup' command. * *

This method is synchronous -- it does not return until the backup has * completed. @@ -167,12 +168,14 @@ interface IBackupManager { * as including packages pre-installed as part of the system. If {@code false}, * then setting {@code allApps} to {@code true} will mean only that all 3rd-party * applications will be included in the dataset. + * @param doKeyValue If {@code true}, also packages supporting key-value backup will be backed + * up. If {@code false}, key-value packages will be skipped. * @param packageNames The package names of the apps whose data (and optionally .apk files) * are to be backed up. The allApps parameter supersedes this. */ - void fullBackup(in ParcelFileDescriptor fd, boolean includeApks, boolean includeObbs, + void adbBackup(in ParcelFileDescriptor fd, boolean includeApks, boolean includeObbs, boolean includeShared, boolean doWidgets, boolean allApps, boolean allIncludesSystem, - boolean doCompress, in String[] packageNames); + boolean doCompress, boolean doKeyValue, in String[] packageNames); /** * Perform a full-dataset backup of the given applications via the currently active @@ -184,11 +187,12 @@ interface IBackupManager { /** * Restore device content from the data stream passed through the given socket. The - * data stream must be in the format emitted by fullBackup(). + * data stream must be in the format emitted by adbBackup(). + * Currently only used by the 'adb restore' command. * *

Callers must hold the android.permission.BACKUP permission to use this method. */ - void fullRestore(in ParcelFileDescriptor fd); + void adbRestore(in ParcelFileDescriptor fd); /** * Confirm that the requested full backup/restore operation can proceed. The system will diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java index 30d06db93330..037804e0c5e9 100644 --- a/services/backup/java/com/android/server/backup/BackupManagerService.java +++ b/services/backup/java/com/android/server/backup/BackupManagerService.java @@ -222,12 +222,27 @@ public class BackupManagerService { // 2 : no format change per se; version bump to facilitate PBKDF2 version skew detection // 3 : introduced "_meta" metadata file; no other format change per se // 4 : added support for new device-encrypted storage locations - static final int BACKUP_FILE_VERSION = 4; + // 5 : added support for key-value packages + static final int BACKUP_FILE_VERSION = 5; static final String BACKUP_FILE_HEADER_MAGIC = "ANDROID BACKUP\n"; static final int BACKUP_PW_FILE_VERSION = 2; static final String BACKUP_METADATA_FILENAME = "_meta"; static final int BACKUP_METADATA_VERSION = 1; static final int BACKUP_WIDGET_METADATA_TOKEN = 0x01FFED01; + + static final int TAR_HEADER_LONG_RADIX = 8; + static final int TAR_HEADER_OFFSET_FILESIZE = 124; + static final int TAR_HEADER_LENGTH_FILESIZE = 12; + static final int TAR_HEADER_OFFSET_MODTIME = 136; + static final int TAR_HEADER_LENGTH_MODTIME = 12; + static final int TAR_HEADER_OFFSET_MODE = 100; + static final int TAR_HEADER_LENGTH_MODE = 8; + static final int TAR_HEADER_OFFSET_PATH_PREFIX = 345; + static final int TAR_HEADER_LENGTH_PATH_PREFIX = 155; + static final int TAR_HEADER_OFFSET_PATH = 0; + static final int TAR_HEADER_LENGTH_PATH = 100; + static final int TAR_HEADER_OFFSET_TYPE_CHAR = 156; + static final boolean COMPRESS_FULL_BACKUPS = true; // should be true in production static final String SETTINGS_PACKAGE = "com.android.providers.settings"; @@ -553,19 +568,20 @@ public class BackupManagerService { } } - class FullParams { + // Parameters used by adbBackup() and adbRestore() + class AdbParams { public ParcelFileDescriptor fd; public final AtomicBoolean latch; public IFullBackupRestoreObserver observer; public String curPassword; // filled in by the confirmation step public String encryptPassword; - FullParams() { + AdbParams() { latch = new AtomicBoolean(false); } } - class FullBackupParams extends FullParams { + class AdbBackupParams extends AdbParams { public boolean includeApks; public boolean includeObbs; public boolean includeShared; @@ -573,11 +589,12 @@ public class BackupManagerService { public boolean allApps; public boolean includeSystem; public boolean doCompress; + public boolean includeKeyValue; public String[] packages; - FullBackupParams(ParcelFileDescriptor output, boolean saveApks, boolean saveObbs, + AdbBackupParams(ParcelFileDescriptor output, boolean saveApks, boolean saveObbs, boolean saveShared, boolean alsoWidgets, boolean doAllApps, boolean doSystem, - boolean compress, String[] pkgList) { + boolean compress, boolean doKeyValue, String[] pkgList) { fd = output; includeApks = saveApks; includeObbs = saveObbs; @@ -586,12 +603,13 @@ public class BackupManagerService { allApps = doAllApps; includeSystem = doSystem; doCompress = compress; + includeKeyValue = doKeyValue; packages = pkgList; } } - class FullRestoreParams extends FullParams { - FullRestoreParams(ParcelFileDescriptor input) { + class AdbRestoreParams extends AdbParams { + AdbRestoreParams(ParcelFileDescriptor input) { fd = input; } } @@ -627,10 +645,10 @@ public class BackupManagerService { static final int OP_TIMEOUT = -1; // Waiting for backup agent to respond during backup operation. - private static final int OP_TYPE_BACKUP_WAIT = 0; + static final int OP_TYPE_BACKUP_WAIT = 0; // Waiting for backup agent to respond during restore operation. - private static final int OP_TYPE_RESTORE_WAIT = 1; + static final int OP_TYPE_RESTORE_WAIT = 1; // An entire backup operation spanning multiple packages. private static final int OP_TYPE_BACKUP = 2; @@ -672,7 +690,7 @@ public class BackupManagerService { final Object mCurrentOpLock = new Object(); final Random mTokenGenerator = new Random(); - final SparseArray mFullConfirmations = new SparseArray(); + final SparseArray mAdbBackupRestoreConfirmations = new SparseArray(); // Where we keep our journal files and other bookkeeping File mBaseStateDir; @@ -791,15 +809,9 @@ public class BackupManagerService { } /* adb backup: is this app only capable of doing key/value? We say otherwise if - * the app has a backup agent and does not say fullBackupOnly, *unless* it - * is a package that we know _a priori_ explicitly supports both key/value and - * full-data backup. + * the app has a backup agent and does not say fullBackupOnly, */ private static boolean appIsKeyValueOnly(PackageInfo pkg) { - if ("com.android.providers.settings".equals(pkg.packageName)) { - return false; - } - return !appGetsFullBackup(pkg); } @@ -912,13 +924,12 @@ public class BackupManagerService { { // TODO: refactor full backup to be a looper-based state machine // similar to normal backup/restore. - FullBackupParams params = (FullBackupParams)msg.obj; + AdbBackupParams params = (AdbBackupParams)msg.obj; PerformAdbBackupTask task = new PerformAdbBackupTask(params.fd, params.observer, params.includeApks, params.includeObbs, - params.includeShared, params.doWidgets, - params.curPassword, params.encryptPassword, - params.allApps, params.includeSystem, params.doCompress, - params.packages, params.latch); + params.includeShared, params.doWidgets, params.curPassword, + params.encryptPassword, params.allApps, params.includeSystem, + params.doCompress, params.includeKeyValue, params.packages, params.latch); (new Thread(task, "adb-backup")).start(); break; } @@ -963,7 +974,7 @@ public class BackupManagerService { { // TODO: refactor full restore to be a looper-based state machine // similar to normal backup/restore. - FullRestoreParams params = (FullRestoreParams)msg.obj; + AdbRestoreParams params = (AdbRestoreParams)msg.obj; PerformAdbRestoreTask task = new PerformAdbRestoreTask(params.fd, params.curPassword, params.encryptPassword, params.observer, params.latch); @@ -1071,16 +1082,16 @@ public class BackupManagerService { case MSG_FULL_CONFIRMATION_TIMEOUT: { - synchronized (mFullConfirmations) { - FullParams params = mFullConfirmations.get(msg.arg1); + synchronized (mAdbBackupRestoreConfirmations) { + AdbParams params = mAdbBackupRestoreConfirmations.get(msg.arg1); if (params != null) { Slog.i(TAG, "Full backup/restore timed out waiting for user confirmation"); // Release the waiter; timeout == completion - signalFullBackupRestoreCompletion(params); + signalAdbBackupRestoreCompletion(params); // Remove the token from the set - mFullConfirmations.delete(msg.arg1); + mAdbBackupRestoreConfirmations.delete(msg.arg1); // Report a timeout to the observer, if any if (params.observer != null) { @@ -3719,7 +3730,7 @@ public class BackupManagerService { } - private void routeSocketDataToOutput(ParcelFileDescriptor inPipe, OutputStream out) + static void routeSocketDataToOutput(ParcelFileDescriptor inPipe, OutputStream out) throws IOException { // We do not take close() responsibility for the pipe FD FileInputStream raw = new FileInputStream(inPipe.getFileDescriptor()); @@ -3822,7 +3833,7 @@ public class BackupManagerService { if (mWriteManifest) { final boolean writeWidgetData = mWidgetData != null; if (MORE_DEBUG) Slog.d(TAG, "Writing manifest for " + mPackage.packageName); - writeAppManifest(mPackage, mManifestFile, mSendApk, writeWidgetData); + writeAppManifest(mPackage, mPackageManager, mManifestFile, mSendApk, writeWidgetData); FullBackup.backupToTar(mPackage.packageName, null, null, mFilesDir.getAbsolutePath(), mManifestFile.getAbsolutePath(), @@ -4006,52 +4017,6 @@ public class BackupManagerService { } } - private void writeAppManifest(PackageInfo pkg, File manifestFile, - boolean withApk, boolean withWidgets) throws IOException { - // Manifest format. All data are strings ending in LF: - // BACKUP_MANIFEST_VERSION, currently 1 - // - // Version 1: - // package name - // package's versionCode - // platform versionCode - // getInstallerPackageName() for this package (maybe empty) - // boolean: "1" if archive includes .apk; any other string means not - // number of signatures == N - // N*: signature byte array in ascii format per Signature.toCharsString() - StringBuilder builder = new StringBuilder(4096); - StringBuilderPrinter printer = new StringBuilderPrinter(builder); - - printer.println(Integer.toString(BACKUP_MANIFEST_VERSION)); - printer.println(pkg.packageName); - printer.println(Integer.toString(pkg.versionCode)); - printer.println(Integer.toString(Build.VERSION.SDK_INT)); - - String installerName = mPackageManager.getInstallerPackageName(pkg.packageName); - printer.println((installerName != null) ? installerName : ""); - - printer.println(withApk ? "1" : "0"); - if (pkg.signatures == null) { - printer.println("0"); - } else { - printer.println(Integer.toString(pkg.signatures.length)); - for (Signature sig : pkg.signatures) { - printer.println(sig.toCharsString()); - } - } - - FileOutputStream outstream = new FileOutputStream(manifestFile); - outstream.write(builder.toString().getBytes()); - outstream.close(); - - // We want the manifest block in the archive stream to be idempotent: - // each time we generate a backup stream for the app, we want the manifest - // block to be identical. The underlying tar mechanism sees it as a file, - // though, and will propagate its mtime, causing the tar header to vary. - // Avoid this problem by pinning the mtime to zero. - manifestFile.setLastModified(0); - } - // Widget metadata format. All header entries are strings ending in LF: // // Version 1 header: @@ -4100,6 +4065,52 @@ public class BackupManagerService { } } + static void writeAppManifest(PackageInfo pkg, PackageManager packageManager, File manifestFile, + boolean withApk, boolean withWidgets) throws IOException { + // Manifest format. All data are strings ending in LF: + // BACKUP_MANIFEST_VERSION, currently 1 + // + // Version 1: + // package name + // package's versionCode + // platform versionCode + // getInstallerPackageName() for this package (maybe empty) + // boolean: "1" if archive includes .apk; any other string means not + // number of signatures == N + // N*: signature byte array in ascii format per Signature.toCharsString() + StringBuilder builder = new StringBuilder(4096); + StringBuilderPrinter printer = new StringBuilderPrinter(builder); + + printer.println(Integer.toString(BACKUP_MANIFEST_VERSION)); + printer.println(pkg.packageName); + printer.println(Integer.toString(pkg.versionCode)); + printer.println(Integer.toString(Build.VERSION.SDK_INT)); + + String installerName = packageManager.getInstallerPackageName(pkg.packageName); + printer.println((installerName != null) ? installerName : ""); + + printer.println(withApk ? "1" : "0"); + if (pkg.signatures == null) { + printer.println("0"); + } else { + printer.println(Integer.toString(pkg.signatures.length)); + for (Signature sig : pkg.signatures) { + printer.println(sig.toCharsString()); + } + } + + FileOutputStream outstream = new FileOutputStream(manifestFile); + outstream.write(builder.toString().getBytes()); + outstream.close(); + + // We want the manifest block in the archive stream to be idempotent: + // each time we generate a backup stream for the app, we want the manifest + // block to be identical. The underlying tar mechanism sees it as a file, + // though, and will propagate its mtime, causing the tar header to vary. + // Avoid this problem by pinning the mtime to zero. + manifestFile.setLastModified(0); + } + // Generic driver skeleton for full backup operations abstract class FullBackupTask implements Runnable { IFullBackupRestoreObserver mObserver; @@ -4172,6 +4183,7 @@ public class BackupManagerService { boolean mAllApps; boolean mIncludeSystem; boolean mCompress; + boolean mKeyValue; ArrayList mPackages; PackageInfo mCurrentTarget; String mCurrentPassword; @@ -4179,9 +4191,9 @@ public class BackupManagerService { private final int mCurrentOpToken; PerformAdbBackupTask(ParcelFileDescriptor fd, IFullBackupRestoreObserver observer, - boolean includeApks, boolean includeObbs, boolean includeShared, - boolean doWidgets, String curPassword, String encryptPassword, boolean doAllApps, - boolean doSystem, boolean doCompress, String[] packages, AtomicBoolean latch) { + boolean includeApks, boolean includeObbs, boolean includeShared, boolean doWidgets, + String curPassword, String encryptPassword, boolean doAllApps, boolean doSystem, + boolean doCompress, boolean doKeyValue, String[] packages, AtomicBoolean latch) { super(observer); mCurrentOpToken = generateToken(); mLatch = latch; @@ -4210,6 +4222,7 @@ public class BackupManagerService { Slog.w(TAG, "Encrypting backup with passphrase=" + mEncryptPassword); } mCompress = doCompress; + mKeyValue = doKeyValue; } void addPackagesToSet(TreeMap set, List pkgNames) { @@ -4309,7 +4322,8 @@ public class BackupManagerService { @Override public void run() { - Slog.i(TAG, "--- Performing full-dataset adb backup ---"); + String includeKeyValue = mKeyValue ? ", including key-value backups" : ""; + Slog.i(TAG, "--- Performing adb backup" + includeKeyValue + " ---"); TreeMap packagesToBackup = new TreeMap(); FullBackupObbConnection obbConnection = new FullBackupObbConnection(); @@ -4361,14 +4375,26 @@ public class BackupManagerService { // Now we cull any inapplicable / inappropriate packages from the set. This // includes the special shared-storage agent package; we handle that one - // explicitly at the end of the backup pass. + // explicitly at the end of the backup pass. Packages supporting key-value backup are + // added to their own queue, and handled after packages supporting fullbackup. + ArrayList keyValueBackupQueue = new ArrayList<>(); Iterator> iter = packagesToBackup.entrySet().iterator(); while (iter.hasNext()) { PackageInfo pkg = iter.next().getValue(); if (!appIsEligibleForBackup(pkg.applicationInfo) - || appIsStopped(pkg.applicationInfo) - || appIsKeyValueOnly(pkg)) { + || appIsStopped(pkg.applicationInfo)) { + iter.remove(); + if (DEBUG) { + Slog.i(TAG, "Package " + pkg.packageName + + " is not eligible for backup, removing."); + } + } else if (appIsKeyValueOnly(pkg)) { iter.remove(); + if (DEBUG) { + Slog.i(TAG, "Package " + pkg.packageName + + " is key-value."); + } + keyValueBackupQueue.add(pkg); } } @@ -4402,7 +4428,7 @@ public class BackupManagerService { // final '\n'. // // line 1: "ANDROID BACKUP" - // line 2: backup file format version, currently "2" + // line 2: backup file format version, currently "5" // line 3: compressed? "0" if not compressed, "1" if compressed. // line 4: name of encryption algorithm [currently only "none" or "AES-256"] // @@ -4462,10 +4488,14 @@ public class BackupManagerService { } } - // Now actually run the constructed backup sequence + // Now actually run the constructed backup sequence for full backup int N = backupQueue.size(); for (int i = 0; i < N; i++) { pkg = backupQueue.get(i); + if (DEBUG) { + Slog.i(TAG,"--- Performing full backup for package " + pkg.packageName + + " ---"); + } final boolean isSharedStorage = pkg.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE); @@ -4485,6 +4515,21 @@ public class BackupManagerService { } } } + // And for key-value backup if enabled + if (mKeyValue) { + for (PackageInfo keyValuePackage : keyValueBackupQueue) { + if (DEBUG) { + Slog.i(TAG, "--- Performing key-value backup for package " + + keyValuePackage.packageName + " ---"); + } + KeyValueAdbBackupEngine kvBackupEngine = + new KeyValueAdbBackupEngine(out, keyValuePackage, + BackupManagerService.this, + mPackageManager, mBaseStateDir, mDataDir); + sendOnBackupPackage(keyValuePackage.packageName); + kvBackupEngine.backupOnePackage(); + } + } // Done! finalizeBackup(out); @@ -6693,19 +6738,24 @@ public class BackupManagerService { try { // okay, presume we're okay, and extract the various metadata info = new FileMetadata(); - info.size = extractRadix(block, 124, 12, 8); - info.mtime = extractRadix(block, 136, 12, 8); - info.mode = extractRadix(block, 100, 8, 8); - - info.path = extractString(block, 345, 155); // prefix - String path = extractString(block, 0, 100); + info.size = extractRadix(block, TAR_HEADER_OFFSET_FILESIZE, + TAR_HEADER_LENGTH_FILESIZE, TAR_HEADER_LONG_RADIX); + info.mtime = extractRadix(block, TAR_HEADER_OFFSET_MODTIME, + TAR_HEADER_LENGTH_MODTIME, TAR_HEADER_LONG_RADIX); + info.mode = extractRadix(block, TAR_HEADER_OFFSET_MODE, + TAR_HEADER_LENGTH_MODE, TAR_HEADER_LONG_RADIX); + + info.path = extractString(block, TAR_HEADER_OFFSET_PATH_PREFIX, + TAR_HEADER_LENGTH_PATH_PREFIX); + String path = extractString(block, TAR_HEADER_OFFSET_PATH, + TAR_HEADER_LENGTH_PATH); if (path.length() > 0) { if (info.path.length() > 0) info.path += '/'; info.path += path; } // tar link indicator field: 1 byte at offset 156 in the header. - int typeChar = block[156]; + int typeChar = block[TAR_HEADER_OFFSET_TYPE_CHAR]; if (typeChar == 'x') { // pax extended header, so we need to read that gotHeader = readPaxExtendedHeader(instream, info); @@ -6716,7 +6766,7 @@ public class BackupManagerService { } if (!gotHeader) throw new IOException("Bad or missing pax header"); - typeChar = block[156]; + typeChar = block[TAR_HEADER_OFFSET_TYPE_CHAR]; } switch (typeChar) { @@ -7037,6 +7087,7 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF IFullBackupRestoreObserver mObserver; AtomicBoolean mLatchObject; IBackupAgent mAgent; + PackageManagerBackupAgent mPackageManagerBackupAgent; String mAgentPackage; ApplicationInfo mTargetApp; FullBackupObbConnection mObbConnection = null; @@ -7088,6 +7139,7 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF mObserver = observer; mLatchObject = latch; mAgent = null; + mPackageManagerBackupAgent = new PackageManagerBackupAgent(mPackageManager); mAgentPackage = null; mTargetApp = null; mObbConnection = new FullBackupObbConnection(); @@ -7505,14 +7557,21 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF long toCopy = info.size; final int token = generateToken(); try { - prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, null, + prepareOperationTimeout(token, TIMEOUT_RESTORE_INTERVAL, null, OP_TYPE_RESTORE_WAIT); - if (info.domain.equals(FullBackup.OBB_TREE_TOKEN)) { + if (FullBackup.OBB_TREE_TOKEN.equals(info.domain)) { if (DEBUG) Slog.d(TAG, "Restoring OBB file for " + pkg + " : " + info.path); mObbConnection.restoreObbFile(pkg, mPipes[0], info.size, info.type, info.path, info.mode, info.mtime, token, mBackupManagerBinder); + } else if (FullBackup.KEY_VALUE_DATA_TOKEN.equals(info.domain)) { + if (DEBUG) Slog.d(TAG, "Restoring key-value file for " + pkg + + " : " + info.path); + KeyValueAdbRestoreEngine restoreEngine = + new KeyValueAdbRestoreEngine(BackupManagerService.this, + mDataDir, info, mPipes[0], mAgent, token); + new Thread(restoreEngine, "restore-key-value-runner").start(); } else { if (DEBUG) Slog.d(TAG, "Invoking agent to restore file " + info.path); @@ -8100,6 +8159,7 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF Slog.i(TAG, b.toString()); } } + // Consume a tar file header block [sequence] and accumulate the relevant metadata FileMetadata readTarHeaders(InputStream instream) throws IOException { byte[] block = new byte[512]; @@ -9920,16 +9980,16 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF return (Settings.Global.getInt(resolver, Settings.Global.DEVICE_PROVISIONED, 0) != 0); } - // Run a *full* backup pass for the given packages, writing the resulting data stream + // Run a backup pass for the given packages, writing the resulting data stream // to the supplied file descriptor. This method is synchronous and does not return // to the caller until the backup has been completed. // // This is the variant used by 'adb backup'; it requires on-screen confirmation // by the user because it can be used to offload data over untrusted USB. - public void fullBackup(ParcelFileDescriptor fd, boolean includeApks, - boolean includeObbs, boolean includeShared, boolean doWidgets, - boolean doAllApps, boolean includeSystem, boolean compress, String[] pkgList) { - mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup"); + public void adbBackup(ParcelFileDescriptor fd, boolean includeApks, boolean includeObbs, + boolean includeShared, boolean doWidgets, boolean doAllApps, boolean includeSystem, + boolean compress, boolean doKeyValue, String[] pkgList) { + mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "adbBackup"); final int callingUserHandle = UserHandle.getCallingUserId(); // TODO: http://b/22388012 @@ -9954,27 +10014,28 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF try { // Doesn't make sense to do a full backup prior to setup if (!deviceIsProvisioned()) { - Slog.i(TAG, "Full backup not supported before setup"); + Slog.i(TAG, "Backup not supported before setup"); return; } - if (DEBUG) Slog.v(TAG, "Requesting full backup: apks=" + includeApks - + " obb=" + includeObbs + " shared=" + includeShared + " all=" + doAllApps - + " system=" + includeSystem + " pkgs=" + pkgList); - Slog.i(TAG, "Beginning full backup..."); + if (DEBUG) Slog.v(TAG, "Requesting backup: apks=" + includeApks + " obb=" + includeObbs + + " shared=" + includeShared + " all=" + doAllApps + " system=" + + includeSystem + " includekeyvalue=" + doKeyValue + " pkgs=" + pkgList); + Slog.i(TAG, "Beginning adb backup..."); - FullBackupParams params = new FullBackupParams(fd, includeApks, includeObbs, - includeShared, doWidgets, doAllApps, includeSystem, compress, pkgList); + AdbBackupParams params = new AdbBackupParams(fd, includeApks, includeObbs, + includeShared, doWidgets, doAllApps, includeSystem, compress, doKeyValue, + pkgList); final int token = generateToken(); - synchronized (mFullConfirmations) { - mFullConfirmations.put(token, params); + synchronized (mAdbBackupRestoreConfirmations) { + mAdbBackupRestoreConfirmations.put(token, params); } // start up the confirmation UI if (DEBUG) Slog.d(TAG, "Starting backup confirmation UI, token=" + token); if (!startConfirmationUi(token, FullBackup.FULL_BACKUP_INTENT_ACTION)) { - Slog.e(TAG, "Unable to launch full backup confirmation"); - mFullConfirmations.delete(token); + Slog.e(TAG, "Unable to launch backup confirmation UI"); + mAdbBackupRestoreConfirmations.delete(token); return; } @@ -9987,7 +10048,7 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF startConfirmationTimeout(token, params); // wait for the backup to be performed - if (DEBUG) Slog.d(TAG, "Waiting for full backup completion..."); + if (DEBUG) Slog.d(TAG, "Waiting for backup completion..."); waitForCompletion(params); } finally { try { @@ -9996,7 +10057,7 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF // just eat it } Binder.restoreCallingIdentity(oldId); - Slog.d(TAG, "Full backup processing complete."); + Slog.d(TAG, "Adb backup processing complete."); } } @@ -10049,8 +10110,8 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF } } - public void fullRestore(ParcelFileDescriptor fd) { - mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullRestore"); + public void adbRestore(ParcelFileDescriptor fd) { + mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "adbRestore"); final int callingUserHandle = UserHandle.getCallingUserId(); // TODO: http://b/22388012 @@ -10068,19 +10129,19 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF return; } - Slog.i(TAG, "Beginning full restore..."); + Slog.i(TAG, "Beginning restore..."); - FullRestoreParams params = new FullRestoreParams(fd); + AdbRestoreParams params = new AdbRestoreParams(fd); final int token = generateToken(); - synchronized (mFullConfirmations) { - mFullConfirmations.put(token, params); + synchronized (mAdbBackupRestoreConfirmations) { + mAdbBackupRestoreConfirmations.put(token, params); } // start up the confirmation UI if (DEBUG) Slog.d(TAG, "Starting restore confirmation UI, token=" + token); if (!startConfirmationUi(token, FullBackup.FULL_RESTORE_INTENT_ACTION)) { - Slog.e(TAG, "Unable to launch full restore confirmation"); - mFullConfirmations.delete(token); + Slog.e(TAG, "Unable to launch restore confirmation"); + mAdbBackupRestoreConfirmations.delete(token); return; } @@ -10093,16 +10154,16 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF startConfirmationTimeout(token, params); // wait for the restore to be performed - if (DEBUG) Slog.d(TAG, "Waiting for full restore completion..."); + if (DEBUG) Slog.d(TAG, "Waiting for restore completion..."); waitForCompletion(params); } finally { try { fd.close(); } catch (IOException e) { - Slog.w(TAG, "Error trying to close fd after full restore: " + e); + Slog.w(TAG, "Error trying to close fd after adb restore: " + e); } Binder.restoreCallingIdentity(oldId); - Slog.i(TAG, "Full restore processing complete."); + Slog.i(TAG, "adb restore processing complete."); } } @@ -10120,7 +10181,7 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF return true; } - void startConfirmationTimeout(int token, FullParams params) { + void startConfirmationTimeout(int token, AdbParams params) { if (MORE_DEBUG) Slog.d(TAG, "Posting conf timeout msg after " + TIMEOUT_FULL_CONFIRMATION + " millis"); Message msg = mBackupHandler.obtainMessage(MSG_FULL_CONFIRMATION_TIMEOUT, @@ -10128,7 +10189,7 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF mBackupHandler.sendMessageDelayed(msg, TIMEOUT_FULL_CONFIRMATION); } - void waitForCompletion(FullParams params) { + void waitForCompletion(AdbParams params) { synchronized (params.latch) { while (params.latch.get() == false) { try { @@ -10138,7 +10199,7 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF } } - void signalFullBackupRestoreCompletion(FullParams params) { + void signalAdbBackupRestoreCompletion(AdbParams params) { synchronized (params.latch) { params.latch.set(true); params.latch.notifyAll(); @@ -10147,27 +10208,27 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF // Confirm that the previously-requested full backup/restore operation can proceed. This // is used to require a user-facing disclosure about the operation. - public void acknowledgeFullBackupOrRestore(int token, boolean allow, + public void acknowledgeAdbBackupOrRestore(int token, boolean allow, String curPassword, String encPpassword, IFullBackupRestoreObserver observer) { - if (DEBUG) Slog.d(TAG, "acknowledgeFullBackupOrRestore : token=" + token + if (DEBUG) Slog.d(TAG, "acknowledgeAdbBackupOrRestore : token=" + token + " allow=" + allow); // TODO: possibly require not just this signature-only permission, but even // require that the specific designated confirmation-UI app uid is the caller? - mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "acknowledgeFullBackupOrRestore"); + mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "acknowledgeAdbBackupOrRestore"); long oldId = Binder.clearCallingIdentity(); try { - FullParams params; - synchronized (mFullConfirmations) { - params = mFullConfirmations.get(token); + AdbParams params; + synchronized (mAdbBackupRestoreConfirmations) { + params = mAdbBackupRestoreConfirmations.get(token); if (params != null) { mBackupHandler.removeMessages(MSG_FULL_CONFIRMATION_TIMEOUT, params); - mFullConfirmations.delete(token); + mAdbBackupRestoreConfirmations.delete(token); if (allow) { - final int verb = params instanceof FullBackupParams + final int verb = params instanceof AdbBackupParams ? MSG_RUN_ADB_BACKUP : MSG_RUN_ADB_RESTORE; @@ -10183,7 +10244,7 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF } else { Slog.w(TAG, "User rejected full backup/restore operation"); // indicate completion without having actually transferred any data - signalFullBackupRestoreCompletion(params); + signalAdbBackupRestoreCompletion(params); } } else { Slog.w(TAG, "Attempted to ack full backup/restore with invalid token"); diff --git a/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java new file mode 100644 index 000000000000..cd137603b350 --- /dev/null +++ b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java @@ -0,0 +1,281 @@ +package com.android.server.backup; + +import static android.os.ParcelFileDescriptor.MODE_CREATE; +import static android.os.ParcelFileDescriptor.MODE_READ_ONLY; +import static android.os.ParcelFileDescriptor.MODE_READ_WRITE; +import static android.os.ParcelFileDescriptor.MODE_TRUNCATE; +import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT; +import static com.android.server.backup.BackupManagerService.TIMEOUT_BACKUP_INTERVAL; + +import android.app.ApplicationThreadConstants; +import android.app.IBackupAgent; +import android.app.backup.FullBackup; +import android.app.backup.FullBackupDataOutput; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.os.ParcelFileDescriptor; +import android.os.RemoteException; +import android.os.SELinux; +import android.util.Slog; + +import libcore.io.IoUtils; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +/** + * Used by BackupManagerService to perform adb backup for key-value packages. At the moment this + * class resembles what is done in the standard key-value code paths in BackupManagerService, and + * should be unified later. + * + * TODO: We should create unified backup/restore engines that can be used for both transport and + * adb backup/restore, and for fullbackup and key-value backup. + */ +class KeyValueAdbBackupEngine { + private static final String TAG = "KeyValueAdbBackupEngine"; + private static final boolean DEBUG = false; + + private static final String BACKUP_KEY_VALUE_DIRECTORY_NAME = "key_value_dir"; + private static final String BACKUP_KEY_VALUE_BLANK_STATE_FILENAME = "blank_state"; + private static final String BACKUP_KEY_VALUE_BACKUP_DATA_FILENAME_SUFFIX = ".data"; + private static final String BACKUP_KEY_VALUE_NEW_STATE_FILENAME_SUFFIX = ".new"; + + private BackupManagerService mBackupManagerService; + private final PackageManager mPackageManager; + private final OutputStream mOutput; + private final PackageInfo mCurrentPackage; + private final File mDataDir; + private final File mStateDir; + private final File mBlankStateName; + private final File mBackupDataName; + private final File mNewStateName; + private final File mManifestFile; + private ParcelFileDescriptor mSavedState; + private ParcelFileDescriptor mBackupData; + private ParcelFileDescriptor mNewState; + + KeyValueAdbBackupEngine(OutputStream output, PackageInfo packageInfo, + BackupManagerService backupManagerService, PackageManager packageManager, + File baseStateDir, File dataDir) { + mOutput = output; + mCurrentPackage = packageInfo; + mBackupManagerService = backupManagerService; + mPackageManager = packageManager; + + mDataDir = dataDir; + mStateDir = new File(baseStateDir, BACKUP_KEY_VALUE_DIRECTORY_NAME); + mStateDir.mkdirs(); + + String pkg = mCurrentPackage.packageName; + + mBlankStateName = new File(mStateDir, BACKUP_KEY_VALUE_BLANK_STATE_FILENAME); + mBackupDataName = new File(mDataDir, + pkg + BACKUP_KEY_VALUE_BACKUP_DATA_FILENAME_SUFFIX); + mNewStateName = new File(mStateDir, + pkg + BACKUP_KEY_VALUE_NEW_STATE_FILENAME_SUFFIX); + + mManifestFile = new File(mDataDir, BackupManagerService.BACKUP_MANIFEST_FILENAME); + } + + void backupOnePackage() throws IOException { + ApplicationInfo targetApp = mCurrentPackage.applicationInfo; + + try { + prepareBackupFiles(mCurrentPackage.packageName); + + IBackupAgent agent = bindToAgent(targetApp); + + if (agent == null) { + // We failed binding to the agent, so ignore this package + Slog.e(TAG, "Failed binding to BackupAgent for package " + + mCurrentPackage.packageName); + return; + } + + // We are bound to agent, initiate backup. + if (!invokeAgentForAdbBackup(mCurrentPackage.packageName, agent)) { + // Backup failed, skip package. + Slog.e(TAG, "Backup Failed for package " + mCurrentPackage.packageName); + return; + } + + // Backup finished successfully. Copy the backup data to the output stream. + writeBackupData(); + } catch (FileNotFoundException e) { + Slog.e(TAG, "Failed creating files for package " + mCurrentPackage.packageName + + " will ignore package. " + e); + } finally { + // We are either done, failed or have timed out, so do cleanup and kill the agent. + cleanup(); + } + } + + private void prepareBackupFiles(String packageName) throws FileNotFoundException { + + // We pass a blank state to make sure we are getting the complete backup, not just an + // increment + mSavedState = ParcelFileDescriptor.open(mBlankStateName, + MODE_READ_ONLY | MODE_CREATE); // Make an empty file if necessary + + mBackupData = ParcelFileDescriptor.open(mBackupDataName, + MODE_READ_WRITE | MODE_CREATE | MODE_TRUNCATE); + + if (!SELinux.restorecon(mBackupDataName)) { + Slog.e(TAG, "SELinux restorecon failed on " + mBackupDataName); + } + + mNewState = ParcelFileDescriptor.open(mNewStateName, + MODE_READ_WRITE | MODE_CREATE | MODE_TRUNCATE); + } + + private IBackupAgent bindToAgent(ApplicationInfo targetApp) { + try { + return mBackupManagerService.bindToAgentSynchronous(targetApp, + ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL); + } catch (SecurityException e) { + Slog.e(TAG, "error in binding to agent for package " + targetApp.packageName + + ". " + e); + return null; + } + } + + // Return true on backup success, false otherwise + private boolean invokeAgentForAdbBackup(String packageName, IBackupAgent agent) { + int token = mBackupManagerService.generateToken(); + try { + mBackupManagerService.prepareOperationTimeout(token, TIMEOUT_BACKUP_INTERVAL, null, + OP_TYPE_BACKUP_WAIT); + + // Start backup and wait for BackupManagerService to get callback for success or timeout + agent.doBackup(mSavedState, mBackupData, mNewState, Long.MAX_VALUE, token, + mBackupManagerService.mBackupManagerBinder); + if (!mBackupManagerService.waitUntilOperationComplete(token)) { + Slog.e(TAG, "Key-value backup failed on package " + packageName); + return false; + } + if (DEBUG) { + Slog.i(TAG, "Key-value backup success for package " + packageName); + } + return true; + } catch (RemoteException e) { + Slog.e(TAG, "Error invoking agent for backup on " + packageName + ". " + e); + return false; + } + } + + class KeyValueAdbBackupDataCopier implements Runnable { + private final PackageInfo mPackage; + private final ParcelFileDescriptor mPipe; + private final int mToken; + + KeyValueAdbBackupDataCopier(PackageInfo pack, ParcelFileDescriptor pipe, + int token) + throws IOException { + mPackage = pack; + mPipe = ParcelFileDescriptor.dup(pipe.getFileDescriptor()); + mToken = token; + } + + @Override + public void run() { + try { + FullBackupDataOutput output = new FullBackupDataOutput(mPipe); + + if (DEBUG) { + Slog.d(TAG, "Writing manifest for " + mPackage.packageName); + } + BackupManagerService.writeAppManifest( + mPackage, mPackageManager, mManifestFile, false, false); + FullBackup.backupToTar(mPackage.packageName, FullBackup.KEY_VALUE_DATA_TOKEN, null, + mDataDir.getAbsolutePath(), + mManifestFile.getAbsolutePath(), + output); + mManifestFile.delete(); + + if (DEBUG) { + Slog.d(TAG, "Writing key-value package payload" + mPackage.packageName); + } + FullBackup.backupToTar(mPackage.packageName, FullBackup.KEY_VALUE_DATA_TOKEN, null, + mDataDir.getAbsolutePath(), + mBackupDataName.getAbsolutePath(), + output); + + // Write EOD marker + try { + FileOutputStream out = new FileOutputStream(mPipe.getFileDescriptor()); + byte[] buf = new byte[4]; + out.write(buf); + } catch (IOException e) { + Slog.e(TAG, "Unable to finalize backup stream!"); + } + + try { + mBackupManagerService.mBackupManagerBinder.opComplete(mToken, 0); + } catch (RemoteException e) { + // we'll time out anyway, so we're safe + } + + } catch (IOException e) { + Slog.e(TAG, "Error running full backup for " + mPackage.packageName + ". " + e); + } finally { + IoUtils.closeQuietly(mPipe); + } + } + } + + private void writeBackupData() throws IOException { + + int token = mBackupManagerService.generateToken(); + + ParcelFileDescriptor[] pipes = null; + try { + pipes = ParcelFileDescriptor.createPipe(); + + mBackupManagerService.prepareOperationTimeout(token, TIMEOUT_BACKUP_INTERVAL, null, + OP_TYPE_BACKUP_WAIT); + + // We will have to create a runnable that will read the manifest and backup data we + // created, such that we can pipe the data into mOutput. The reason we do this is that + // internally FullBackup.backupToTar is used, which will create the necessary file + // header, but will also chunk the data. The method routeSocketDataToOutput in + // BackupManagerService will dechunk the data, and append it to the TAR outputstream. + KeyValueAdbBackupDataCopier runner = new KeyValueAdbBackupDataCopier(mCurrentPackage, pipes[1], + token); + pipes[1].close(); // the runner has dup'd it + pipes[1] = null; + Thread t = new Thread(runner, "key-value-app-data-runner"); + t.start(); + + // Now pull data from the app and stuff it into the output + BackupManagerService.routeSocketDataToOutput(pipes[0], mOutput); + + if (!mBackupManagerService.waitUntilOperationComplete(token)) { + Slog.e(TAG, "Full backup failed on package " + mCurrentPackage.packageName); + } else { + if (DEBUG) { + Slog.d(TAG, "Full package backup success: " + mCurrentPackage.packageName); + } + } + } catch (IOException e) { + Slog.e(TAG, "Error backing up " + mCurrentPackage.packageName + ": " + e); + } finally { + // flush after every package + mOutput.flush(); + if (pipes != null) { + IoUtils.closeQuietly(pipes[0]); + IoUtils.closeQuietly(pipes[1]); + } + } + } + + private void cleanup() { + mBackupManagerService.tearDownAgentAndKill(mCurrentPackage.applicationInfo); + mBlankStateName.delete(); + mNewStateName.delete(); + mBackupDataName.delete(); + } +} diff --git a/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java b/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java new file mode 100644 index 000000000000..6fb935569928 --- /dev/null +++ b/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java @@ -0,0 +1,148 @@ +package com.android.server.backup; + +import static android.os.ParcelFileDescriptor.MODE_CREATE; +import static android.os.ParcelFileDescriptor.MODE_READ_ONLY; +import static android.os.ParcelFileDescriptor.MODE_READ_WRITE; +import static android.os.ParcelFileDescriptor.MODE_TRUNCATE; + +import android.app.IBackupAgent; +import android.app.backup.BackupDataInput; +import android.app.backup.BackupDataOutput; +import android.app.backup.FullBackup; +import android.os.ParcelFileDescriptor; +import android.os.RemoteException; +import android.util.Slog; + +import com.android.server.backup.BackupManagerService.FileMetadata; +import libcore.io.IoUtils; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Used by BackupManagerService to perform adb restore for key-value packages. At the moment this + * class resembles what is done in the standard key-value code paths in BackupManagerService, and + * should be unified later. + * + * TODO: We should create unified backup/restore engines that can be used for both transport and + * adb backup/restore, and for fullbackup and key-value backup. + */ +class KeyValueAdbRestoreEngine implements Runnable { + private static final String TAG = "KeyValueAdbRestoreEngine"; + private static final boolean DEBUG = false; + + private final BackupManagerService mBackupManagerService; + private final File mDataDir; + + FileMetadata mInfo; + BackupManagerService.PerformAdbRestoreTask mRestoreTask; + ParcelFileDescriptor mInFD; + IBackupAgent mAgent; + int mToken; + + KeyValueAdbRestoreEngine(BackupManagerService backupManagerService, File dataDir, + FileMetadata info, ParcelFileDescriptor inFD, IBackupAgent agent, int token) { + mBackupManagerService = backupManagerService; + mDataDir = dataDir; + mInfo = info; + mInFD = inFD; + mAgent = agent; + mToken = token; + } + + @Override + public void run() { + try { + File restoreData = prepareRestoreData(mInfo, mInFD); + + // TODO: version ? + invokeAgentForAdbRestore(mAgent, mInfo, restoreData, 0); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private File prepareRestoreData(FileMetadata info, ParcelFileDescriptor inFD) throws IOException { + String pkg = info.packageName; + File restoreDataName = new File(mDataDir, pkg + ".restore"); + File sortedDataName = new File(mDataDir, pkg + ".sorted"); + + FullBackup.restoreFile(inFD, info.size, info.type, info.mode, info.mtime, restoreDataName); + + // Sort the keys, as the BackupAgent expect them to come in lexicographical order + sortKeyValueData(restoreDataName, sortedDataName); + return sortedDataName; + } + + private void invokeAgentForAdbRestore(IBackupAgent agent, FileMetadata info, File restoreData, + int versionCode) throws IOException { + String pkg = info.packageName; + File newStateName = new File(mDataDir, pkg + ".new"); + try { + ParcelFileDescriptor backupData = + ParcelFileDescriptor.open(restoreData, MODE_READ_ONLY); + ParcelFileDescriptor newState = ParcelFileDescriptor.open(newStateName, + MODE_READ_WRITE | MODE_CREATE | MODE_TRUNCATE); + + if (DEBUG) { + Slog.i(TAG, "Starting restore of package " + pkg + " for version code " + + versionCode); + } + agent.doRestore(backupData, versionCode, newState, mToken, + mBackupManagerService.mBackupManagerBinder); + } catch (IOException e) { + Slog.e(TAG, "Exception opening file. " + e); + } catch (RemoteException e) { + Slog.e(TAG, "Exception calling doRestore on agent: " + e); + } + } + + private void sortKeyValueData (File restoreData, File sortedData) throws IOException { + FileInputStream inputStream = null; + FileOutputStream outputStream = null; + try { + inputStream = new FileInputStream(restoreData); + outputStream = new FileOutputStream(sortedData); + BackupDataInput reader = new BackupDataInput(inputStream.getFD()); + BackupDataOutput writer = new BackupDataOutput(outputStream.getFD()); + copyKeysInLexicalOrder(reader, writer); + } finally { + if (inputStream != null) { + IoUtils.closeQuietly(inputStream); + } + if (outputStream != null) { + IoUtils.closeQuietly(outputStream); + } + } + } + + private void copyKeysInLexicalOrder(BackupDataInput in, BackupDataOutput out) + throws IOException { + Map data = new HashMap<>(); + while (in.readNextHeader()) { + String key = in.getKey(); + int size = in.getDataSize(); + if (size < 0) { + in.skipEntityData(); + continue; + } + byte[] value = new byte[size]; + in.readEntityData(value, 0, size); + data.put(key, value); + } + List keys = new ArrayList<>(data.keySet()); + Collections.sort(keys); + for (String key : keys) { + byte[] value = data.get(key); + out.writeEntityHeader(key, value.length); + out.writeEntityData(value, value.length); + } + } +} diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java index 8855661b954f..c40f2ca0b5ac 100644 --- a/services/backup/java/com/android/server/backup/Trampoline.java +++ b/services/backup/java/com/android/server/backup/Trampoline.java @@ -227,14 +227,14 @@ public class Trampoline extends IBackupManager.Stub { } @Override - public void fullBackup(ParcelFileDescriptor fd, boolean includeApks, boolean includeObbs, + public void adbBackup(ParcelFileDescriptor fd, boolean includeApks, boolean includeObbs, boolean includeShared, boolean doWidgets, boolean allApps, - boolean allIncludesSystem, boolean doCompress, String[] packageNames) + boolean allIncludesSystem, boolean doCompress, boolean doKeyValue, String[] packageNames) throws RemoteException { BackupManagerService svc = mService; if (svc != null) { - svc.fullBackup(fd, includeApks, includeObbs, includeShared, doWidgets, - allApps, allIncludesSystem, doCompress, packageNames); + svc.adbBackup(fd, includeApks, includeObbs, includeShared, doWidgets, + allApps, allIncludesSystem, doCompress, doKeyValue, packageNames); } } @@ -247,10 +247,10 @@ public class Trampoline extends IBackupManager.Stub { } @Override - public void fullRestore(ParcelFileDescriptor fd) throws RemoteException { + public void adbRestore(ParcelFileDescriptor fd) throws RemoteException { BackupManagerService svc = mService; if (svc != null) { - svc.fullRestore(fd); + svc.adbRestore(fd); } } @@ -260,7 +260,7 @@ public class Trampoline extends IBackupManager.Stub { throws RemoteException { BackupManagerService svc = mService; if (svc != null) { - svc.acknowledgeFullBackupOrRestore(token, allow, + svc.acknowledgeAdbBackupOrRestore(token, allow, curPassword, encryptionPassword, observer); } }