summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmds/bu/src/com/android/commands/bu/Backup.java17
-rw-r--r--core/java/android/app/backup/FullBackup.java1
-rw-r--r--core/java/android/app/backup/IBackupManager.aidl14
-rw-r--r--services/backup/java/com/android/server/backup/BackupManagerService.java333
-rw-r--r--services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java281
-rw-r--r--services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java148
-rw-r--r--services/backup/java/com/android/server/backup/Trampoline.java14
7 files changed, 653 insertions, 155 deletions
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<String> packages = new ArrayList<String>();
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.
*
* <p>This method is <i>synchronous</i> -- 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 <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
@@ -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
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<FullParams> mFullConfirmations = new SparseArray<FullParams>();
+ final SparseArray<AdbParams> mAdbBackupRestoreConfirmations = new SparseArray<AdbParams>();
// 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<String> 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<String, PackageInfo> set, List<String> 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<String, PackageInfo> packagesToBackup = new TreeMap<String, PackageInfo>();
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<PackageInfo> keyValueBackupQueue = new ArrayList<>();
Iterator<Entry<String, PackageInfo>> 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<String, byte[]> 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<String> 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);
}
}