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();
+    }
+}