2 * Copyright (C) 2017 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
17 package com.android.server.backup.restore;
19 import static com.android.server.backup.BackupManagerService.BACKUP_MANIFEST_FILENAME;
20 import static com.android.server.backup.BackupManagerService.BACKUP_METADATA_FILENAME;
21 import static com.android.server.backup.BackupManagerService.DEBUG;
22 import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
23 import static com.android.server.backup.BackupManagerService.OP_TYPE_RESTORE_WAIT;
24 import static com.android.server.backup.BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
25 import static com.android.server.backup.BackupManagerService.TAG;
26 import static com.android.server.backup.BackupManagerService.TIMEOUT_RESTORE_INTERVAL;
27 import static com.android.server.backup.BackupManagerService
28 .TIMEOUT_SHARED_BACKUP_INTERVAL;
29 import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT;
31 import android.app.ApplicationThreadConstants;
32 import android.app.IBackupAgent;
33 import android.app.backup.FullBackup;
34 import android.app.backup.IBackupManagerMonitor;
35 import android.app.backup.IFullBackupRestoreObserver;
36 import android.content.pm.ApplicationInfo;
37 import android.content.pm.PackageInfo;
38 import android.content.pm.PackageManager.NameNotFoundException;
39 import android.content.pm.PackageManagerInternal;
40 import android.content.pm.Signature;
41 import android.os.ParcelFileDescriptor;
42 import android.os.RemoteException;
43 import android.util.Slog;
45 import com.android.server.LocalServices;
46 import com.android.server.backup.BackupRestoreTask;
47 import com.android.server.backup.FileMetadata;
48 import com.android.server.backup.KeyValueAdbRestoreEngine;
49 import com.android.server.backup.BackupManagerService;
50 import com.android.server.backup.fullbackup.FullBackupObbConnection;
51 import com.android.server.backup.utils.BytesReadListener;
52 import com.android.server.backup.utils.FullBackupRestoreObserverUtils;
53 import com.android.server.backup.utils.RestoreUtils;
54 import com.android.server.backup.utils.TarBackupReader;
56 import java.io.FileOutputStream;
57 import java.io.IOException;
58 import java.io.InputStream;
59 import java.util.HashMap;
60 import java.util.HashSet;
63 * Full restore engine, used by both adb restore and transport-based full restore.
65 public class FullRestoreEngine extends RestoreEngine {
67 private final BackupManagerService mBackupManagerService;
68 // Task in charge of monitoring timeouts
69 private final BackupRestoreTask mMonitorTask;
71 private final RestoreDeleteObserver mDeleteObserver = new RestoreDeleteObserver();
73 // Dedicated observer, if any
74 private IFullBackupRestoreObserver mObserver;
76 final IBackupManagerMonitor mMonitor;
78 // Where we're delivering the file data as we go
79 private IBackupAgent mAgent;
81 // Are we permitted to only deliver a specific package's metadata?
82 final PackageInfo mOnlyPackage;
84 final boolean mAllowApks;
85 private final boolean mAllowObbs;
87 // Which package are we currently handling data for?
88 private String mAgentPackage;
90 // Info for working with the target app process
91 private ApplicationInfo mTargetApp;
93 // Machinery for restoring OBBs
94 private FullBackupObbConnection mObbConnection = null;
96 // possible handling states for a given package in the restore dataset
97 private final HashMap<String, RestorePolicy> mPackagePolicies
100 // installer package names for each encountered app, derived from the manifests
101 private final HashMap<String, String> mPackageInstallers = new HashMap<>();
103 // Signatures for a given package found in its manifest file
104 private final HashMap<String, Signature[]> mManifestSignatures
107 // Packages we've already wiped data on when restoring their first file
108 private final HashSet<String> mClearedPackages = new HashSet<>();
110 // How much data have we moved?
114 final byte[] mBuffer;
116 // Pipes for moving data
117 private ParcelFileDescriptor[] mPipes = null;
119 // Widget blob to be restored out-of-band
120 private byte[] mWidgetData = null;
122 final int mEphemeralOpToken;
124 public FullRestoreEngine(BackupManagerService backupManagerService,
125 BackupRestoreTask monitorTask, IFullBackupRestoreObserver observer,
126 IBackupManagerMonitor monitor, PackageInfo onlyPackage, boolean allowApks,
127 boolean allowObbs, int ephemeralOpToken) {
128 mBackupManagerService = backupManagerService;
129 mEphemeralOpToken = ephemeralOpToken;
130 mMonitorTask = monitorTask;
131 mObserver = observer;
133 mOnlyPackage = onlyPackage;
134 mAllowApks = allowApks;
135 mAllowObbs = allowObbs;
136 mBuffer = new byte[32 * 1024];
140 public IBackupAgent getAgent() {
144 public byte[] getWidgetData() {
148 public boolean restoreOneFile(InputStream instream, boolean mustKillAgent, byte[] buffer,
149 PackageInfo onlyPackage, boolean allowApks, int token, IBackupManagerMonitor monitor) {
151 Slog.w(TAG, "Restore engine used after halting");
155 BytesReadListener bytesReadListener = new BytesReadListener() {
157 public void onBytesRead(long bytesRead) {
162 TarBackupReader tarBackupReader = new TarBackupReader(instream,
163 bytesReadListener, monitor);
168 Slog.v(TAG, "Reading tar header for restoring file");
170 info = tarBackupReader.readTarHeaders();
176 final String pkg = info.packageName;
177 if (!pkg.equals(mAgentPackage)) {
178 // In the single-package case, it's a semantic error to expect
179 // one app's data but see a different app's on the wire
180 if (onlyPackage != null) {
181 if (!pkg.equals(onlyPackage.packageName)) {
182 Slog.w(TAG, "Expected data for " + onlyPackage + " but saw " + pkg);
183 setResult(RestoreEngine.TRANSPORT_FAILURE);
189 // okay, change in package; set up our various
190 // bookkeeping if we haven't seen it yet
191 if (!mPackagePolicies.containsKey(pkg)) {
192 mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
195 // Clean up the previous agent relationship if necessary,
196 // and let the observer know we're considering a new app.
197 if (mAgent != null) {
199 Slog.d(TAG, "Saw new package; finalizing old one");
201 // Now we're really done
203 tearDownAgent(mTargetApp);
205 mAgentPackage = null;
209 if (info.path.equals(BACKUP_MANIFEST_FILENAME)) {
210 Signature[] signatures = tarBackupReader.readAppManifestAndReturnSignatures(
212 PackageManagerInternal pmi = LocalServices.getService(
213 PackageManagerInternal.class);
214 RestorePolicy restorePolicy = tarBackupReader.chooseRestorePolicy(
215 mBackupManagerService.getPackageManager(), allowApks, info, signatures,
217 mManifestSignatures.put(info.packageName, signatures);
218 mPackagePolicies.put(pkg, restorePolicy);
219 mPackageInstallers.put(pkg, info.installerPackageName);
220 // We've read only the manifest content itself at this point,
221 // so consume the footer before looping around to the next
223 tarBackupReader.skipTarPadding(info.size);
224 mObserver = FullBackupRestoreObserverUtils.sendOnRestorePackage(mObserver, pkg);
225 } else if (info.path.equals(BACKUP_METADATA_FILENAME)) {
227 tarBackupReader.readMetadata(info);
229 // The following only exist because we want to keep refactoring as safe as
230 // possible, without changing too much.
231 // TODO: Refactor, so that there are no funny things like this.
232 // This is read during TarBackupReader.readMetadata().
233 mWidgetData = tarBackupReader.getWidgetData();
234 // This can be nulled during TarBackupReader.readMetadata().
235 monitor = tarBackupReader.getMonitor();
237 tarBackupReader.skipTarPadding(info.size);
239 // Non-manifest, so it's actual file data. Is this a package
242 RestorePolicy policy = mPackagePolicies.get(pkg);
249 // If we're in accept-if-apk state, then the first file we
250 // see MUST be the apk.
251 if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
253 Slog.d(TAG, "APK file; installing");
255 // Try to install the app.
256 String installerPackageName = mPackageInstallers.get(pkg);
257 boolean isSuccessfullyInstalled = RestoreUtils.installApk(
258 instream, mBackupManagerService.getContext(),
259 mDeleteObserver, mManifestSignatures,
260 mPackagePolicies, info, installerPackageName,
262 // good to go; promote to ACCEPT
263 mPackagePolicies.put(pkg, isSuccessfullyInstalled
264 ? RestorePolicy.ACCEPT
265 : RestorePolicy.IGNORE);
266 // At this point we've consumed this file entry
267 // ourselves, so just strip the tar footer and
268 // go on to the next file in the input stream
269 tarBackupReader.skipTarPadding(info.size);
272 // File data before (or without) the apk. We can't
273 // handle it coherently in this case so ignore it.
274 mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
280 if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
282 Slog.d(TAG, "apk present but ACCEPT");
284 // we can take the data without the apk, so we
285 // *want* to do so. skip the apk by declaring this
286 // one file not-okay without changing the restore
287 // policy for the package.
293 // Something has gone dreadfully wrong when determining
294 // the restore policy from the manifest. Ignore the
295 // rest of this package's data.
296 Slog.e(TAG, "Invalid policy from manifest");
298 mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
302 // Is it a *file* we need to drop or is it not a canonical path?
303 if (!isRestorableFile(info) || !isCanonicalFilePath(info.path)) {
307 // If the policy is satisfied, go ahead and set up to pipe the
308 // data to the agent.
309 if (MORE_DEBUG && okay && mAgent != null) {
310 Slog.i(TAG, "Reusing existing agent instance");
312 if (okay && mAgent == null) {
314 Slog.d(TAG, "Need to launch agent for " + pkg);
319 mBackupManagerService.getPackageManager().getApplicationInfo(
322 // If we haven't sent any data to this app yet, we probably
323 // need to clear it first. Check that.
324 if (!mClearedPackages.contains(pkg)) {
325 // apps with their own backup agents are
326 // responsible for coherently managing a full
328 if (mTargetApp.backupAgentName == null) {
331 "Clearing app data preparatory to full restore");
333 mBackupManagerService.clearApplicationDataSynchronous(pkg, true);
336 Slog.d(TAG, "backup agent ("
337 + mTargetApp.backupAgentName + ") => no clear");
340 mClearedPackages.add(pkg);
343 Slog.d(TAG, "We've initialized this app already; no clear "
348 // All set; now set up the IPC and launch the agent
350 mAgent = mBackupManagerService.bindToAgentSynchronous(mTargetApp,
351 ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL);
353 } catch (IOException e) {
354 // fall through to error handling
355 } catch (NameNotFoundException e) {
356 // fall through to error handling
359 if (mAgent == null) {
360 Slog.e(TAG, "Unable to create agent for " + pkg);
363 mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
367 // Sanity check: make sure we never give data to the wrong app. This
368 // should never happen but a little paranoia here won't go amiss.
369 if (okay && !pkg.equals(mAgentPackage)) {
370 Slog.e(TAG, "Restoring data for " + pkg
371 + " but agent is for " + mAgentPackage);
375 // At this point we have an agent ready to handle the full
376 // restore data as well as a pipe for sending data to
377 // that agent. Tell the agent to start reading from the
380 boolean agentSuccess = true;
381 long toCopy = info.size;
382 final boolean isSharedStorage = pkg.equals(SHARED_BACKUP_AGENT_PACKAGE);
383 final long timeout = isSharedStorage ?
384 TIMEOUT_SHARED_BACKUP_INTERVAL :
385 TIMEOUT_RESTORE_INTERVAL;
387 mBackupManagerService.prepareOperationTimeout(token,
390 OP_TYPE_RESTORE_WAIT);
392 if (FullBackup.OBB_TREE_TOKEN.equals(info.domain)) {
394 Slog.d(TAG, "Restoring OBB file for " + pkg
395 + " : " + info.path);
397 mObbConnection.restoreObbFile(pkg, mPipes[0],
398 info.size, info.type, info.path, info.mode,
400 mBackupManagerService.getBackupManagerBinder());
401 } else if (FullBackup.KEY_VALUE_DATA_TOKEN.equals(info.domain)) {
402 // This is only possible during adb restore.
403 // TODO: Refactor to clearly separate the flows.
405 Slog.d(TAG, "Restoring key-value file for " + pkg
406 + " : " + info.path);
408 KeyValueAdbRestoreEngine restoreEngine =
409 new KeyValueAdbRestoreEngine(
410 mBackupManagerService,
411 mBackupManagerService.getDataDir(), info, mPipes[0],
413 new Thread(restoreEngine, "restore-key-value-runner").start();
416 Slog.d(TAG, "Invoking agent to restore file " + info.path);
418 // fire up the app's agent listening on the socket. If
419 // the agent is running in the system process we can't
420 // just invoke it asynchronously, so we provide a thread
422 if (mTargetApp.processName.equals("system")) {
423 Slog.d(TAG, "system process agent - spinning a thread");
424 RestoreFileRunnable runner = new RestoreFileRunnable(
425 mBackupManagerService, mAgent, info, mPipes[0], token);
426 new Thread(runner, "restore-sys-runner").start();
428 mAgent.doRestoreFile(mPipes[0], info.size, info.type,
429 info.domain, info.path, info.mode, info.mtime,
430 token, mBackupManagerService.getBackupManagerBinder());
433 } catch (IOException e) {
434 // couldn't dup the socket for a process-local restore
435 Slog.d(TAG, "Couldn't establish restore");
436 agentSuccess = false;
438 } catch (RemoteException e) {
439 // whoops, remote entity went away. We'll eat the content
440 // ourselves, then, and not copy it over.
441 Slog.e(TAG, "Agent crashed during full restore");
442 agentSuccess = false;
446 // Copy over the data if the agent is still good
449 Slog.v(TAG, " copying to restore agent: " + toCopy + " bytes");
451 boolean pipeOkay = true;
452 FileOutputStream pipe = new FileOutputStream(
453 mPipes[1].getFileDescriptor());
455 int toRead = (toCopy > buffer.length)
456 ? buffer.length : (int) toCopy;
457 int nRead = instream.read(buffer, 0, toRead);
466 // send it to the output pipe as long as things
470 pipe.write(buffer, 0, nRead);
471 } catch (IOException e) {
472 Slog.e(TAG, "Failed to write to restore pipe: "
479 // done sending that file! Now we just need to consume
480 // the delta from info.size to the end of block.
481 tarBackupReader.skipTarPadding(info.size);
483 // and now that we've sent it all, wait for the remote
484 // side to acknowledge receipt
485 agentSuccess = mBackupManagerService.waitUntilOperationComplete(token);
488 // okay, if the remote end failed at any point, deal with
489 // it by ignoring the rest of the restore on it
491 Slog.w(TAG, "Agent failure restoring " + pkg + "; ending restore");
492 mBackupManagerService.getBackupHandler().removeMessages(
493 MSG_RESTORE_OPERATION_TIMEOUT);
495 tearDownAgent(mTargetApp);
497 mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
499 // If this was a single-package restore, we halt immediately
500 // with an agent error under these circumstances
501 if (onlyPackage != null) {
502 setResult(RestoreEngine.TARGET_FAILURE);
509 // Problems setting up the agent communication, an explicitly
510 // dropped file, or an already-ignored package: skip to the
511 // next stream entry by reading and discarding this file.
514 Slog.d(TAG, "[discarding file content]");
516 long bytesToConsume = (info.size + 511) & ~511;
517 while (bytesToConsume > 0) {
518 int toRead = (bytesToConsume > buffer.length)
519 ? buffer.length : (int) bytesToConsume;
520 long nRead = instream.read(buffer, 0, toRead);
527 bytesToConsume -= nRead;
532 } catch (IOException e) {
534 Slog.w(TAG, "io exception on restore socket read: " + e.getMessage());
536 setResult(RestoreEngine.TRANSPORT_FAILURE);
540 // If we got here we're either running smoothly or we've finished
543 Slog.i(TAG, "No [more] data for this package; tearing down");
548 tearDownAgent(mTargetApp);
551 return (info != null);
554 private void setUpPipes() throws IOException {
555 mPipes = ParcelFileDescriptor.createPipe();
558 private void tearDownPipes() {
559 // Teardown might arise from the inline restore processing or from the asynchronous
560 // timeout mechanism, and these might race. Make sure we don't try to close and
561 // null out the pipes twice.
562 synchronized (this) {
563 if (mPipes != null) {
569 } catch (IOException e) {
570 Slog.w(TAG, "Couldn't close agent pipes", e);
577 private void tearDownAgent(ApplicationInfo app) {
578 if (mAgent != null) {
579 mBackupManagerService.tearDownAgentAndKill(app);
584 void handleTimeout() {
586 setResult(RestoreEngine.TARGET_FAILURE);
590 private static boolean isRestorableFile(FileMetadata info) {
591 if (FullBackup.CACHE_TREE_TOKEN.equals(info.domain)) {
593 Slog.i(TAG, "Dropping cache file path " + info.path);
598 if (FullBackup.ROOT_TREE_TOKEN.equals(info.domain)) {
599 // It's possible this is "no-backup" dir contents in an archive stream
600 // produced on a device running a version of the OS that predates that
601 // API. Respect the no-backup intention and don't let the data get to
603 if (info.path.startsWith("no_backup/")) {
605 Slog.i(TAG, "Dropping no_backup file path " + info.path);
611 // Otherwise we think this file is good to go
615 private static boolean isCanonicalFilePath(String path) {
616 if (path.contains("..") || path.contains("//")) {
618 Slog.w(TAG, "Dropping invalid path " + path);
626 void sendOnRestorePackage(String name) {
627 if (mObserver != null) {
629 // TODO: use a more user-friendly name string
630 mObserver.onRestorePackage(name);
631 } catch (RemoteException e) {
632 Slog.w(TAG, "full restore observer went away: restorePackage");