Home | History | Annotate | Download | only in backup
      1 package com.android.server.backup;
      2 
      3 import static android.os.ParcelFileDescriptor.MODE_CREATE;
      4 import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
      5 import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
      6 import static android.os.ParcelFileDescriptor.MODE_TRUNCATE;
      7 
      8 import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT;
      9 
     10 import android.app.ApplicationThreadConstants;
     11 import android.app.IBackupAgent;
     12 import android.app.backup.FullBackup;
     13 import android.app.backup.FullBackupDataOutput;
     14 import android.content.pm.ApplicationInfo;
     15 import android.content.pm.PackageInfo;
     16 import android.content.pm.PackageManager;
     17 import android.os.ParcelFileDescriptor;
     18 import android.os.RemoteException;
     19 import android.os.SELinux;
     20 import android.util.Slog;
     21 
     22 import com.android.internal.util.Preconditions;
     23 import com.android.server.backup.utils.FullBackupUtils;
     24 
     25 import libcore.io.IoUtils;
     26 
     27 import java.io.File;
     28 import java.io.FileNotFoundException;
     29 import java.io.FileOutputStream;
     30 import java.io.IOException;
     31 import java.io.OutputStream;
     32 
     33 /**
     34  * Used by BackupManagerService to perform adb backup for key-value packages. At the moment this
     35  * class resembles what is done in the standard key-value code paths in BackupManagerService, and
     36  * should be unified later.
     37  *
     38  * TODO: We should create unified backup/restore engines that can be used for both transport and
     39  * adb backup/restore, and for fullbackup and key-value backup.
     40  */
     41 public class KeyValueAdbBackupEngine {
     42     private static final String TAG = "KeyValueAdbBackupEngine";
     43     private static final boolean DEBUG = false;
     44 
     45     private static final String BACKUP_KEY_VALUE_DIRECTORY_NAME = "key_value_dir";
     46     private static final String BACKUP_KEY_VALUE_BLANK_STATE_FILENAME = "blank_state";
     47     private static final String BACKUP_KEY_VALUE_BACKUP_DATA_FILENAME_SUFFIX = ".data";
     48     private static final String BACKUP_KEY_VALUE_NEW_STATE_FILENAME_SUFFIX = ".new";
     49 
     50     private BackupManagerServiceInterface mBackupManagerService;
     51     private final PackageManager mPackageManager;
     52     private final OutputStream mOutput;
     53     private final PackageInfo mCurrentPackage;
     54     private final File mDataDir;
     55     private final File mStateDir;
     56     private final File mBlankStateName;
     57     private final File mBackupDataName;
     58     private final File mNewStateName;
     59     private final File mManifestFile;
     60     private ParcelFileDescriptor mSavedState;
     61     private ParcelFileDescriptor mBackupData;
     62     private ParcelFileDescriptor mNewState;
     63     private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
     64 
     65     public KeyValueAdbBackupEngine(OutputStream output, PackageInfo packageInfo,
     66             BackupManagerServiceInterface backupManagerService, PackageManager packageManager,
     67             File baseStateDir, File dataDir) {
     68         mOutput = output;
     69         mCurrentPackage = packageInfo;
     70         mBackupManagerService = backupManagerService;
     71         mPackageManager = packageManager;
     72 
     73         mDataDir = dataDir;
     74         mStateDir = new File(baseStateDir, BACKUP_KEY_VALUE_DIRECTORY_NAME);
     75         mStateDir.mkdirs();
     76 
     77         String pkg = mCurrentPackage.packageName;
     78 
     79         mBlankStateName = new File(mStateDir, BACKUP_KEY_VALUE_BLANK_STATE_FILENAME);
     80         mBackupDataName = new File(mDataDir,
     81                 pkg + BACKUP_KEY_VALUE_BACKUP_DATA_FILENAME_SUFFIX);
     82         mNewStateName = new File(mStateDir,
     83                 pkg + BACKUP_KEY_VALUE_NEW_STATE_FILENAME_SUFFIX);
     84 
     85         mManifestFile = new File(mDataDir, BackupManagerService.BACKUP_MANIFEST_FILENAME);
     86         mAgentTimeoutParameters = Preconditions.checkNotNull(
     87                 backupManagerService.getAgentTimeoutParameters(),
     88                 "Timeout parameters cannot be null");
     89     }
     90 
     91     public void backupOnePackage() throws IOException {
     92         ApplicationInfo targetApp = mCurrentPackage.applicationInfo;
     93 
     94         try {
     95             prepareBackupFiles(mCurrentPackage.packageName);
     96 
     97             IBackupAgent agent = bindToAgent(targetApp);
     98 
     99             if (agent == null) {
    100                 // We failed binding to the agent, so ignore this package
    101                 Slog.e(TAG, "Failed binding to BackupAgent for package "
    102                         + mCurrentPackage.packageName);
    103                 return;
    104             }
    105 
    106             // We are bound to agent, initiate backup.
    107             if (!invokeAgentForAdbBackup(mCurrentPackage.packageName, agent)) {
    108                 // Backup failed, skip package.
    109                 Slog.e(TAG, "Backup Failed for package " + mCurrentPackage.packageName);
    110                 return;
    111             }
    112 
    113             // Backup finished successfully. Copy the backup data to the output stream.
    114             writeBackupData();
    115         } catch (FileNotFoundException e) {
    116             Slog.e(TAG, "Failed creating files for package " + mCurrentPackage.packageName
    117                     + " will ignore package. " + e);
    118         } finally {
    119             // We are either done, failed or have timed out, so do cleanup and kill the agent.
    120             cleanup();
    121         }
    122     }
    123 
    124     private void  prepareBackupFiles(String packageName) throws FileNotFoundException {
    125 
    126         // We pass a blank state to make sure we are getting the complete backup, not just an
    127         // increment
    128         mSavedState = ParcelFileDescriptor.open(mBlankStateName,
    129                 MODE_READ_ONLY | MODE_CREATE);  // Make an empty file if necessary
    130 
    131         mBackupData = ParcelFileDescriptor.open(mBackupDataName,
    132                 MODE_READ_WRITE | MODE_CREATE | MODE_TRUNCATE);
    133 
    134         if (!SELinux.restorecon(mBackupDataName)) {
    135             Slog.e(TAG, "SELinux restorecon failed on " + mBackupDataName);
    136         }
    137 
    138         mNewState = ParcelFileDescriptor.open(mNewStateName,
    139                 MODE_READ_WRITE | MODE_CREATE | MODE_TRUNCATE);
    140     }
    141 
    142     private IBackupAgent bindToAgent(ApplicationInfo targetApp) {
    143         try {
    144             return mBackupManagerService.bindToAgentSynchronous(targetApp,
    145                     ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL);
    146         } catch (SecurityException e) {
    147             Slog.e(TAG, "error in binding to agent for package " + targetApp.packageName
    148                     + ". " + e);
    149             return null;
    150         }
    151     }
    152 
    153     // Return true on backup success, false otherwise
    154     private boolean invokeAgentForAdbBackup(String packageName, IBackupAgent agent) {
    155         int token = mBackupManagerService.generateRandomIntegerToken();
    156         long kvBackupAgentTimeoutMillis = mAgentTimeoutParameters.getKvBackupAgentTimeoutMillis();
    157         try {
    158             mBackupManagerService.prepareOperationTimeout(token, kvBackupAgentTimeoutMillis, null,
    159                     OP_TYPE_BACKUP_WAIT);
    160 
    161             // Start backup and wait for BackupManagerService to get callback for success or timeout
    162             agent.doBackup(
    163                     mSavedState, mBackupData, mNewState, Long.MAX_VALUE, token,
    164                     mBackupManagerService.getBackupManagerBinder(), /*transportFlags=*/ 0);
    165             if (!mBackupManagerService.waitUntilOperationComplete(token)) {
    166                 Slog.e(TAG, "Key-value backup failed on package " + packageName);
    167                 return false;
    168             }
    169             if (DEBUG) {
    170                 Slog.i(TAG, "Key-value backup success for package " + packageName);
    171             }
    172             return true;
    173         } catch (RemoteException e) {
    174             Slog.e(TAG, "Error invoking agent for backup on " + packageName + ". " + e);
    175             return false;
    176         }
    177     }
    178 
    179     class KeyValueAdbBackupDataCopier implements Runnable {
    180         private final PackageInfo mPackage;
    181         private final ParcelFileDescriptor mPipe;
    182         private final int mToken;
    183 
    184         KeyValueAdbBackupDataCopier(PackageInfo pack, ParcelFileDescriptor pipe,
    185                 int token)
    186                 throws IOException {
    187             mPackage = pack;
    188             mPipe = ParcelFileDescriptor.dup(pipe.getFileDescriptor());
    189             mToken = token;
    190         }
    191 
    192         @Override
    193         public void run() {
    194             try {
    195                 FullBackupDataOutput output = new FullBackupDataOutput(mPipe);
    196 
    197                 if (DEBUG) {
    198                     Slog.d(TAG, "Writing manifest for " + mPackage.packageName);
    199                 }
    200                 FullBackupUtils.writeAppManifest(
    201                         mPackage, mPackageManager, mManifestFile, false, false);
    202                 FullBackup.backupToTar(mPackage.packageName, FullBackup.KEY_VALUE_DATA_TOKEN, null,
    203                         mDataDir.getAbsolutePath(),
    204                         mManifestFile.getAbsolutePath(),
    205                         output);
    206                 mManifestFile.delete();
    207 
    208                 if (DEBUG) {
    209                     Slog.d(TAG, "Writing key-value package payload" + mPackage.packageName);
    210                 }
    211                 FullBackup.backupToTar(mPackage.packageName, FullBackup.KEY_VALUE_DATA_TOKEN, null,
    212                         mDataDir.getAbsolutePath(),
    213                         mBackupDataName.getAbsolutePath(),
    214                         output);
    215 
    216                 // Write EOD marker
    217                 try {
    218                     FileOutputStream out = new FileOutputStream(mPipe.getFileDescriptor());
    219                     byte[] buf = new byte[4];
    220                     out.write(buf);
    221                 } catch (IOException e) {
    222                     Slog.e(TAG, "Unable to finalize backup stream!");
    223                 }
    224 
    225                 try {
    226                     mBackupManagerService.getBackupManagerBinder().opComplete(mToken, 0);
    227                 } catch (RemoteException e) {
    228                     // we'll time out anyway, so we're safe
    229                 }
    230 
    231             } catch (IOException e) {
    232                 Slog.e(TAG, "Error running full backup for " + mPackage.packageName + ". " + e);
    233             } finally {
    234                 IoUtils.closeQuietly(mPipe);
    235             }
    236         }
    237     }
    238 
    239     private void writeBackupData() throws IOException {
    240         int token = mBackupManagerService.generateRandomIntegerToken();
    241         long kvBackupAgentTimeoutMillis = mAgentTimeoutParameters.getKvBackupAgentTimeoutMillis();
    242 
    243         ParcelFileDescriptor[] pipes = null;
    244         try {
    245             pipes = ParcelFileDescriptor.createPipe();
    246 
    247             mBackupManagerService.prepareOperationTimeout(token, kvBackupAgentTimeoutMillis, null,
    248                     OP_TYPE_BACKUP_WAIT);
    249 
    250             // We will have to create a runnable that will read the manifest and backup data we
    251             // created, such that we can pipe the data into mOutput. The reason we do this is that
    252             // internally FullBackup.backupToTar is used, which will create the necessary file
    253             // header, but will also chunk the data. The method routeSocketDataToOutput in
    254             // BackupManagerService will dechunk the data, and append it to the TAR outputstream.
    255             KeyValueAdbBackupDataCopier runner = new KeyValueAdbBackupDataCopier(mCurrentPackage, pipes[1],
    256                     token);
    257             pipes[1].close();   // the runner has dup'd it
    258             pipes[1] = null;
    259             Thread t = new Thread(runner, "key-value-app-data-runner");
    260             t.start();
    261 
    262             // Now pull data from the app and stuff it into the output
    263             FullBackupUtils.routeSocketDataToOutput(pipes[0], mOutput);
    264 
    265             if (!mBackupManagerService.waitUntilOperationComplete(token)) {
    266                 Slog.e(TAG, "Full backup failed on package " + mCurrentPackage.packageName);
    267             } else {
    268                 if (DEBUG) {
    269                     Slog.d(TAG, "Full package backup success: " + mCurrentPackage.packageName);
    270                 }
    271             }
    272         } catch (IOException e) {
    273             Slog.e(TAG, "Error backing up " + mCurrentPackage.packageName + ": " + e);
    274         } finally {
    275             // flush after every package
    276             mOutput.flush();
    277             if (pipes != null) {
    278                 IoUtils.closeQuietly(pipes[0]);
    279                 IoUtils.closeQuietly(pipes[1]);
    280             }
    281         }
    282     }
    283 
    284     private void cleanup() {
    285         mBackupManagerService.tearDownAgentAndKill(mCurrentPackage.applicationInfo);
    286         mBlankStateName.delete();
    287         mNewStateName.delete();
    288         mBackupDataName.delete();
    289     }
    290 }
    291