blob: 7f0030a5064eba4c7ac1dfcc609da1bdae818dc2 [file] [log] [blame]
Justin Klaassen10d07c82017-09-15 17:58:39 -04001package 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;
Justin Klaassen4d01eea2018-04-03 23:21:57 -04007
8import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT;
Justin Klaassen10d07c82017-09-15 17:58:39 -04009
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
Justin Klaassen4d01eea2018-04-03 23:21:57 -040022import com.android.internal.util.Preconditions;
Justin Klaassen93b7ee42017-10-10 15:20:13 -040023import com.android.server.backup.utils.FullBackupUtils;
24
Justin Klaassen10d07c82017-09-15 17:58:39 -040025import libcore.io.IoUtils;
26
27import java.io.File;
28import java.io.FileNotFoundException;
29import java.io.FileOutputStream;
30import java.io.IOException;
31import 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 */
41public 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;
Justin Klaassen4d01eea2018-04-03 23:21:57 -040063 private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
Justin Klaassen10d07c82017-09-15 17:58:39 -040064
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
Justin Klaassen4d01eea2018-04-03 23:21:57 -040085 mManifestFile = new File(mDataDir, BackupManagerService.BACKUP_MANIFEST_FILENAME);
86 mAgentTimeoutParameters = Preconditions.checkNotNull(
87 backupManagerService.getAgentTimeoutParameters(),
88 "Timeout parameters cannot be null");
Justin Klaassen10d07c82017-09-15 17:58:39 -040089 }
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();
Justin Klaassen4d01eea2018-04-03 23:21:57 -0400156 long kvBackupAgentTimeoutMillis = mAgentTimeoutParameters.getKvBackupAgentTimeoutMillis();
Justin Klaassen10d07c82017-09-15 17:58:39 -0400157 try {
Justin Klaassen4d01eea2018-04-03 23:21:57 -0400158 mBackupManagerService.prepareOperationTimeout(token, kvBackupAgentTimeoutMillis, null,
Justin Klaassen10d07c82017-09-15 17:58:39 -0400159 OP_TYPE_BACKUP_WAIT);
160
161 // Start backup and wait for BackupManagerService to get callback for success or timeout
Justin Klaassen4d01eea2018-04-03 23:21:57 -0400162 agent.doBackup(
163 mSavedState, mBackupData, mNewState, Long.MAX_VALUE, token,
164 mBackupManagerService.getBackupManagerBinder(), /*transportFlags=*/ 0);
Justin Klaassen10d07c82017-09-15 17:58:39 -0400165 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 }
Justin Klaassen93b7ee42017-10-10 15:20:13 -0400200 FullBackupUtils.writeAppManifest(
Justin Klaassen10d07c82017-09-15 17:58:39 -0400201 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 {
Justin Klaassen10d07c82017-09-15 17:58:39 -0400240 int token = mBackupManagerService.generateRandomIntegerToken();
Justin Klaassen4d01eea2018-04-03 23:21:57 -0400241 long kvBackupAgentTimeoutMillis = mAgentTimeoutParameters.getKvBackupAgentTimeoutMillis();
Justin Klaassen10d07c82017-09-15 17:58:39 -0400242
243 ParcelFileDescriptor[] pipes = null;
244 try {
245 pipes = ParcelFileDescriptor.createPipe();
246
Justin Klaassen4d01eea2018-04-03 23:21:57 -0400247 mBackupManagerService.prepareOperationTimeout(token, kvBackupAgentTimeoutMillis, null,
Justin Klaassen10d07c82017-09-15 17:58:39 -0400248 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
Justin Klaassen93b7ee42017-10-10 15:20:13 -0400263 FullBackupUtils.routeSocketDataToOutput(pipes[0], mOutput);
Justin Klaassen10d07c82017-09-15 17:58:39 -0400264
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}