Loading cmds/bu/src/com/android/commands/bu/Backup.java +10 −7 Original line number Diff line number Diff line Loading @@ -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<String> packages = new ArrayList<String>(); boolean saveApks = false; boolean saveObbs = false; Loading @@ -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) { Loading Loading @@ -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; Loading @@ -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 { Loading @@ -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 { Loading core/java/android/app/backup/FullBackup.java +1 −0 Original line number Diff line number Diff line Loading @@ -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"; Loading core/java/android/app/backup/IBackupManager.aidl +9 −5 Original line number Diff line number Diff line Loading @@ -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. * * <p>This method is <i>synchronous</i> -- it does not return until the backup has * completed. Loading @@ -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 <code>allApps</code> 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 Loading @@ -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. * * <p>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 Loading services/backup/java/com/android/server/backup/BackupManagerService.java +197 −136 File changed.Preview size limit exceeded, changes collapsed. Show changes services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java 0 → 100644 +281 −0 Original line number Diff line number Diff line 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(); } } Loading
cmds/bu/src/com/android/commands/bu/Backup.java +10 −7 Original line number Diff line number Diff line Loading @@ -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<String> packages = new ArrayList<String>(); boolean saveApks = false; boolean saveObbs = false; Loading @@ -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) { Loading Loading @@ -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; Loading @@ -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 { Loading @@ -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 { Loading
core/java/android/app/backup/FullBackup.java +1 −0 Original line number Diff line number Diff line Loading @@ -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"; Loading
core/java/android/app/backup/IBackupManager.aidl +9 −5 Original line number Diff line number Diff line Loading @@ -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. * * <p>This method is <i>synchronous</i> -- it does not return until the backup has * completed. Loading @@ -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 <code>allApps</code> 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 Loading @@ -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. * * <p>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 Loading
services/backup/java/com/android/server/backup/BackupManagerService.java +197 −136 File changed.Preview size limit exceeded, changes collapsed. Show changes
services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java 0 → 100644 +281 −0 Original line number Diff line number Diff line 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(); } }