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