Import Android SDK Platform PI [4335822]
/google/data/ro/projects/android/fetch_artifact \
--bid 4335822 \
--target sdk_phone_armv7-win_sdk \
sdk-repo-linux-sources-4335822.zip
AndroidVersion.ApiLevel has been modified to appear as 28
Change-Id: Ic8f04be005a71c2b9abeaac754d8da8d6f9a2c32
diff --git a/com/android/server/backup/KeyValueAdbBackupEngine.java b/com/android/server/backup/KeyValueAdbBackupEngine.java
new file mode 100644
index 0000000..279c828
--- /dev/null
+++ b/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.
+ */
+public 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 BackupManagerServiceInterface 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;
+
+ public KeyValueAdbBackupEngine(OutputStream output, PackageInfo packageInfo,
+ BackupManagerServiceInterface 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);
+ }
+
+ public 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.generateRandomIntegerToken();
+ 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.getBackupManagerBinder());
+ 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.getBackupManagerBinder().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.generateRandomIntegerToken();
+
+ 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();
+ }
+}