Use FullRestoreEngine in adb restore.
PerformAdbRestoreTask and FullRestoreEngine has shared a lot of code, remove the copy that was in PerformAdbRestoreTask. Test: adb restore Change-Id: Ib4ce8b50eff8aed4a2c0660808c05b80f378ea98
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
package com.android.server.backup.restore;
|
||||
|
||||
import android.app.IBackupAgent;
|
||||
import android.os.RemoteException;
|
||||
|
||||
import com.android.server.backup.BackupManagerService;
|
||||
|
||||
/**
|
||||
* Runner that can be placed on a separate thread to do in-process invocation of the "restore
|
||||
* finished" API asynchronously. Used by adb restore.
|
||||
*/
|
||||
public class AdbRestoreFinishedRunnable implements Runnable {
|
||||
|
||||
private final IBackupAgent mAgent;
|
||||
private final int mToken;
|
||||
private final BackupManagerService mBackupManagerService;
|
||||
|
||||
AdbRestoreFinishedRunnable(IBackupAgent agent, int token,
|
||||
BackupManagerService backupManagerService) {
|
||||
mAgent = agent;
|
||||
mToken = token;
|
||||
mBackupManagerService = backupManagerService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
mAgent.doRestoreFinished(mToken, mBackupManagerService.getBackupManagerBinder());
|
||||
} catch (RemoteException e) {
|
||||
// never happens; this is used only for local binder calls
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -122,15 +122,17 @@ public class FullRestoreEngine extends RestoreEngine {
|
||||
|
||||
// Widget blob to be restored out-of-band
|
||||
private byte[] mWidgetData = null;
|
||||
private long mAppVersion;
|
||||
|
||||
final int mEphemeralOpToken;
|
||||
|
||||
private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
|
||||
final boolean mIsAdbRestore;
|
||||
|
||||
public FullRestoreEngine(BackupManagerService backupManagerService,
|
||||
BackupRestoreTask monitorTask, IFullBackupRestoreObserver observer,
|
||||
IBackupManagerMonitor monitor, PackageInfo onlyPackage, boolean allowApks,
|
||||
boolean allowObbs, int ephemeralOpToken) {
|
||||
boolean allowObbs, int ephemeralOpToken, boolean isAdbRestore) {
|
||||
mBackupManagerService = backupManagerService;
|
||||
mEphemeralOpToken = ephemeralOpToken;
|
||||
mMonitorTask = monitorTask;
|
||||
@@ -144,6 +146,7 @@ public class FullRestoreEngine extends RestoreEngine {
|
||||
mAgentTimeoutParameters = Preconditions.checkNotNull(
|
||||
backupManagerService.getAgentTimeoutParameters(),
|
||||
"Timeout parameters cannot be null");
|
||||
mIsAdbRestore = isAdbRestore;
|
||||
}
|
||||
|
||||
public IBackupAgent getAgent() {
|
||||
@@ -209,7 +212,7 @@ public class FullRestoreEngine extends RestoreEngine {
|
||||
}
|
||||
// Now we're really done
|
||||
tearDownPipes();
|
||||
tearDownAgent(mTargetApp);
|
||||
tearDownAgent(mTargetApp, mIsAdbRestore);
|
||||
mTargetApp = null;
|
||||
mAgentPackage = null;
|
||||
}
|
||||
@@ -218,6 +221,9 @@ public class FullRestoreEngine extends RestoreEngine {
|
||||
if (info.path.equals(BACKUP_MANIFEST_FILENAME)) {
|
||||
Signature[] signatures = tarBackupReader.readAppManifestAndReturnSignatures(
|
||||
info);
|
||||
// readAppManifestAndReturnSignatures() will have extracted the version from
|
||||
// the manifest, so we save it to use in adb key-value restore later.
|
||||
mAppVersion = info.version;
|
||||
PackageManagerInternal pmi = LocalServices.getService(
|
||||
PackageManagerInternal.class);
|
||||
RestorePolicy restorePolicy = tarBackupReader.chooseRestorePolicy(
|
||||
@@ -362,7 +368,9 @@ public class FullRestoreEngine extends RestoreEngine {
|
||||
// All set; now set up the IPC and launch the agent
|
||||
setUpPipes();
|
||||
mAgent = mBackupManagerService.bindToAgentSynchronous(mTargetApp,
|
||||
ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL);
|
||||
FullBackup.KEY_VALUE_DATA_TOKEN.equals(info.domain)
|
||||
? ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL
|
||||
: ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL);
|
||||
mAgentPackage = pkg;
|
||||
} catch (IOException e) {
|
||||
// fall through to error handling
|
||||
@@ -419,6 +427,8 @@ public class FullRestoreEngine extends RestoreEngine {
|
||||
Slog.d(TAG, "Restoring key-value file for " + pkg
|
||||
+ " : " + info.path);
|
||||
}
|
||||
// Set the version saved from manifest entry.
|
||||
info.version = mAppVersion;
|
||||
KeyValueAdbRestoreEngine restoreEngine =
|
||||
new KeyValueAdbRestoreEngine(
|
||||
mBackupManagerService,
|
||||
@@ -506,7 +516,7 @@ public class FullRestoreEngine extends RestoreEngine {
|
||||
mBackupManagerService.getBackupHandler().removeMessages(
|
||||
MSG_RESTORE_OPERATION_TIMEOUT);
|
||||
tearDownPipes();
|
||||
tearDownAgent(mTargetApp);
|
||||
tearDownAgent(mTargetApp, false);
|
||||
mAgent = null;
|
||||
mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
|
||||
|
||||
@@ -559,7 +569,7 @@ public class FullRestoreEngine extends RestoreEngine {
|
||||
tearDownPipes();
|
||||
setRunning(false);
|
||||
if (mustKillAgent) {
|
||||
tearDownAgent(mTargetApp);
|
||||
tearDownAgent(mTargetApp, mIsAdbRestore);
|
||||
}
|
||||
}
|
||||
return (info != null);
|
||||
@@ -588,9 +598,37 @@ public class FullRestoreEngine extends RestoreEngine {
|
||||
}
|
||||
}
|
||||
|
||||
private void tearDownAgent(ApplicationInfo app) {
|
||||
private void tearDownAgent(ApplicationInfo app, boolean doRestoreFinished) {
|
||||
if (mAgent != null) {
|
||||
mBackupManagerService.tearDownAgentAndKill(app);
|
||||
try {
|
||||
// In the adb restore case, we do restore-finished here
|
||||
if (doRestoreFinished) {
|
||||
final int token = mBackupManagerService.generateRandomIntegerToken();
|
||||
long fullBackupAgentTimeoutMillis =
|
||||
mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
|
||||
final AdbRestoreFinishedLatch latch = new AdbRestoreFinishedLatch(
|
||||
mBackupManagerService, token);
|
||||
mBackupManagerService.prepareOperationTimeout(
|
||||
token, fullBackupAgentTimeoutMillis, latch, OP_TYPE_RESTORE_WAIT);
|
||||
if (mTargetApp.processName.equals("system")) {
|
||||
if (MORE_DEBUG) {
|
||||
Slog.d(TAG, "system agent - restoreFinished on thread");
|
||||
}
|
||||
Runnable runner = new AdbRestoreFinishedRunnable(mAgent, token,
|
||||
mBackupManagerService);
|
||||
new Thread(runner, "restore-sys-finished-runner").start();
|
||||
} else {
|
||||
mAgent.doRestoreFinished(token,
|
||||
mBackupManagerService.getBackupManagerBinder());
|
||||
}
|
||||
|
||||
latch.await();
|
||||
}
|
||||
|
||||
mBackupManagerService.tearDownAgentAndKill(app);
|
||||
} catch (RemoteException e) {
|
||||
Slog.d(TAG, "Lost app trying to shut down");
|
||||
}
|
||||
mAgent = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.android.server.backup.restore;
|
||||
|
||||
import android.os.ParcelFileDescriptor;
|
||||
|
||||
import libcore.io.IoUtils;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
|
||||
class FullRestoreEngineThread implements Runnable {
|
||||
|
||||
FullRestoreEngine mEngine;
|
||||
InputStream mEngineStream;
|
||||
private final boolean mMustKillAgent;
|
||||
|
||||
FullRestoreEngineThread(FullRestoreEngine engine, ParcelFileDescriptor engineSocket) {
|
||||
mEngine = engine;
|
||||
engine.setRunning(true);
|
||||
// We *do* want this FileInputStream to own the underlying fd, so that
|
||||
// when we are finished with it, it closes this end of the pipe in a way
|
||||
// that signals its other end.
|
||||
mEngineStream = new FileInputStream(engineSocket.getFileDescriptor(), true);
|
||||
// Tell it to be sure to leave the agent instance up after finishing
|
||||
mMustKillAgent = false;
|
||||
}
|
||||
|
||||
//for adb restore
|
||||
FullRestoreEngineThread(FullRestoreEngine engine, InputStream inputStream) {
|
||||
mEngine = engine;
|
||||
engine.setRunning(true);
|
||||
mEngineStream = inputStream;
|
||||
// philippov: in adb agent is killed after restore.
|
||||
mMustKillAgent = true;
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
return mEngine.isRunning();
|
||||
}
|
||||
|
||||
public int waitForResult() {
|
||||
return mEngine.waitForResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
while (mEngine.isRunning()) {
|
||||
mEngine.restoreOneFile(mEngineStream, mMustKillAgent, mEngine.mBuffer,
|
||||
mEngine.mOnlyPackage, mEngine.mAllowApks, mEngine.mEphemeralOpToken,
|
||||
mEngine.mMonitor);
|
||||
}
|
||||
} finally {
|
||||
// Because mEngineStream adopted its underlying FD, this also
|
||||
// closes this end of the pipe.
|
||||
IoUtils.closeQuietly(mEngineStream);
|
||||
}
|
||||
}
|
||||
|
||||
public void handleTimeout() {
|
||||
IoUtils.closeQuietly(mEngineStream);
|
||||
mEngine.handleTimeout();
|
||||
}
|
||||
}
|
||||
@@ -18,8 +18,6 @@ package com.android.server.backup.restore;
|
||||
|
||||
import static com.android.server.backup.BackupManagerService.BACKUP_FILE_HEADER_MAGIC;
|
||||
import static com.android.server.backup.BackupManagerService.BACKUP_FILE_VERSION;
|
||||
import static com.android.server.backup.BackupManagerService.BACKUP_MANIFEST_FILENAME;
|
||||
import static com.android.server.backup.BackupManagerService.BACKUP_METADATA_FILENAME;
|
||||
import static com.android.server.backup.BackupManagerService.DEBUG;
|
||||
import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
|
||||
import static com.android.server.backup.BackupManagerService.OP_TYPE_RESTORE_WAIT;
|
||||
@@ -30,15 +28,10 @@ import static com.android.server.backup.BackupPasswordManager.PBKDF_CURRENT;
|
||||
import static com.android.server.backup.BackupPasswordManager.PBKDF_FALLBACK;
|
||||
import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT;
|
||||
|
||||
import android.app.ApplicationThreadConstants;
|
||||
|
||||
import android.app.IBackupAgent;
|
||||
import android.app.backup.FullBackup;
|
||||
import android.app.backup.IBackupManagerMonitor;
|
||||
import android.app.backup.IFullBackupRestoreObserver;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.content.pm.Signature;
|
||||
import android.os.Environment;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
@@ -50,18 +43,12 @@ import com.android.internal.util.Preconditions;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.backup.BackupAgentTimeoutParameters;
|
||||
import com.android.server.backup.BackupManagerService;
|
||||
import com.android.server.backup.FileMetadata;
|
||||
import com.android.server.backup.KeyValueAdbRestoreEngine;
|
||||
import com.android.server.backup.PackageManagerBackupAgent;
|
||||
import com.android.server.backup.fullbackup.FullBackupObbConnection;
|
||||
import com.android.server.backup.utils.BytesReadListener;
|
||||
import com.android.server.backup.utils.FullBackupRestoreObserverUtils;
|
||||
import com.android.server.backup.utils.PasswordUtils;
|
||||
import com.android.server.backup.utils.RestoreUtils;
|
||||
import com.android.server.backup.utils.TarBackupReader;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
@@ -104,31 +91,6 @@ public class PerformAdbRestoreTask implements Runnable {
|
||||
private long mBytes;
|
||||
private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
|
||||
|
||||
// Runner that can be placed on a separate thread to do in-process invocation
|
||||
// of the "restore finished" API asynchronously. Used by adb restore.
|
||||
private static class RestoreFinishedRunnable implements Runnable {
|
||||
|
||||
private final IBackupAgent mAgent;
|
||||
private final int mToken;
|
||||
private final BackupManagerService mBackupManagerService;
|
||||
|
||||
RestoreFinishedRunnable(IBackupAgent agent, int token,
|
||||
BackupManagerService backupManagerService) {
|
||||
mAgent = agent;
|
||||
mToken = token;
|
||||
mBackupManagerService = backupManagerService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
mAgent.doRestoreFinished(mToken, mBackupManagerService.getBackupManagerBinder());
|
||||
} catch (RemoteException e) {
|
||||
// never happens; this is used only for local binder calls
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// possible handling states for a given package in the restore dataset
|
||||
private final HashMap<String, RestorePolicy> mPackagePolicies
|
||||
= new HashMap<>();
|
||||
@@ -199,23 +161,18 @@ public class PerformAdbRestoreTask implements Runnable {
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] buffer = new byte[32 * 1024];
|
||||
boolean didRestore;
|
||||
do {
|
||||
didRestore = restoreOneFile(tarInputStream, false /* mustKillAgent */, buffer,
|
||||
null /* onlyPackage */, true /* allowApks */,
|
||||
mBackupManagerService.generateRandomIntegerToken(), null /* monitor */);
|
||||
} while (didRestore);
|
||||
FullRestoreEngine mEngine = new FullRestoreEngine(mBackupManagerService, null,
|
||||
mObserver, null, null, true, true/*unused*/, 0 /*unused*/, true);
|
||||
FullRestoreEngineThread mEngineThread = new FullRestoreEngineThread(mEngine,
|
||||
tarInputStream);
|
||||
mEngineThread.run();
|
||||
|
||||
if (MORE_DEBUG) {
|
||||
Slog.v(TAG, "Done consuming input tarfile, total bytes=" + mBytes);
|
||||
Slog.v(TAG, "Done consuming input tarfile.");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Slog.e(TAG, "Unable to read restore input");
|
||||
} finally {
|
||||
tearDownPipes();
|
||||
tearDownAgent(mTargetApp, true);
|
||||
|
||||
try {
|
||||
if (rawInStream != null) {
|
||||
rawInStream.close();
|
||||
@@ -433,432 +390,4 @@ public class PerformAdbRestoreTask implements Runnable {
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
boolean restoreOneFile(InputStream instream, boolean mustKillAgent, byte[] buffer,
|
||||
PackageInfo onlyPackage, boolean allowApks, int token, IBackupManagerMonitor monitor) {
|
||||
BytesReadListener bytesReadListener = new BytesReadListener() {
|
||||
@Override
|
||||
public void onBytesRead(long bytesRead) {
|
||||
mBytes += bytesRead;
|
||||
}
|
||||
};
|
||||
TarBackupReader tarBackupReader = new TarBackupReader(instream,
|
||||
bytesReadListener, monitor);
|
||||
FileMetadata info;
|
||||
try {
|
||||
info = tarBackupReader.readTarHeaders();
|
||||
if (info != null) {
|
||||
if (MORE_DEBUG) {
|
||||
info.dump();
|
||||
}
|
||||
|
||||
final String pkg = info.packageName;
|
||||
if (!pkg.equals(mAgentPackage)) {
|
||||
// okay, change in package; set up our various
|
||||
// bookkeeping if we haven't seen it yet
|
||||
if (!mPackagePolicies.containsKey(pkg)) {
|
||||
mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
|
||||
}
|
||||
|
||||
// Clean up the previous agent relationship if necessary,
|
||||
// and let the observer know we're considering a new app.
|
||||
if (mAgent != null) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Saw new package; finalizing old one");
|
||||
}
|
||||
// Now we're really done
|
||||
tearDownPipes();
|
||||
tearDownAgent(mTargetApp, true);
|
||||
mTargetApp = null;
|
||||
mAgentPackage = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (info.path.equals(BACKUP_MANIFEST_FILENAME)) {
|
||||
Signature[] signatures = tarBackupReader.readAppManifestAndReturnSignatures(
|
||||
info);
|
||||
// readAppManifestAndReturnSignatures() will have extracted the version from
|
||||
// the manifest, so we save it to use in key-value restore later.
|
||||
mAppVersion = info.version;
|
||||
PackageManagerInternal pmi = LocalServices.getService(
|
||||
PackageManagerInternal.class);
|
||||
RestorePolicy restorePolicy = tarBackupReader.chooseRestorePolicy(
|
||||
mBackupManagerService.getPackageManager(), allowApks,
|
||||
info, signatures, pmi);
|
||||
mManifestSignatures.put(info.packageName, signatures);
|
||||
mPackagePolicies.put(pkg, restorePolicy);
|
||||
mPackageInstallers.put(pkg, info.installerPackageName);
|
||||
// We've read only the manifest content itself at this point,
|
||||
// so consume the footer before looping around to the next
|
||||
// input file
|
||||
tarBackupReader.skipTarPadding(info.size);
|
||||
mObserver = FullBackupRestoreObserverUtils.sendOnRestorePackage(mObserver, pkg);
|
||||
} else if (info.path.equals(BACKUP_METADATA_FILENAME)) {
|
||||
// Metadata blobs!
|
||||
tarBackupReader.readMetadata(info);
|
||||
|
||||
// The following only exist because we want to keep refactoring as safe as
|
||||
// possible, without changing too much.
|
||||
// TODO: Refactor, so that there are no funny things like this.
|
||||
// This is read during TarBackupReader.readMetadata().
|
||||
mWidgetData = tarBackupReader.getWidgetData();
|
||||
// This can be nulled during TarBackupReader.readMetadata().
|
||||
monitor = tarBackupReader.getMonitor();
|
||||
|
||||
tarBackupReader.skipTarPadding(info.size);
|
||||
} else {
|
||||
// Non-manifest, so it's actual file data. Is this a package
|
||||
// we're ignoring?
|
||||
boolean okay = true;
|
||||
RestorePolicy policy = mPackagePolicies.get(pkg);
|
||||
switch (policy) {
|
||||
case IGNORE:
|
||||
okay = false;
|
||||
break;
|
||||
|
||||
case ACCEPT_IF_APK:
|
||||
// If we're in accept-if-apk state, then the first file we
|
||||
// see MUST be the apk.
|
||||
if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "APK file; installing");
|
||||
}
|
||||
// Try to install the app.
|
||||
String installerPackageName = mPackageInstallers.get(pkg);
|
||||
boolean isSuccessfullyInstalled = RestoreUtils.installApk(instream,
|
||||
mBackupManagerService.getContext(),
|
||||
mDeleteObserver, mManifestSignatures, mPackagePolicies,
|
||||
info, installerPackageName, bytesReadListener);
|
||||
// good to go; promote to ACCEPT
|
||||
mPackagePolicies.put(pkg, isSuccessfullyInstalled
|
||||
? RestorePolicy.ACCEPT
|
||||
: RestorePolicy.IGNORE);
|
||||
// At this point we've consumed this file entry
|
||||
// ourselves, so just strip the tar footer and
|
||||
// go on to the next file in the input stream
|
||||
tarBackupReader.skipTarPadding(info.size);
|
||||
return true;
|
||||
} else {
|
||||
// File data before (or without) the apk. We can't
|
||||
// handle it coherently in this case so ignore it.
|
||||
mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
|
||||
okay = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case ACCEPT:
|
||||
if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "apk present but ACCEPT");
|
||||
}
|
||||
// we can take the data without the apk, so we
|
||||
// *want* to do so. skip the apk by declaring this
|
||||
// one file not-okay without changing the restore
|
||||
// policy for the package.
|
||||
okay = false;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// Something has gone dreadfully wrong when determining
|
||||
// the restore policy from the manifest. Ignore the
|
||||
// rest of this package's data.
|
||||
Slog.e(TAG, "Invalid policy from manifest");
|
||||
okay = false;
|
||||
mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
|
||||
break;
|
||||
}
|
||||
|
||||
// The path needs to be canonical
|
||||
if (!isCanonicalFilePath(info.path)) {
|
||||
okay = false;
|
||||
}
|
||||
|
||||
// If the policy is satisfied, go ahead and set up to pipe the
|
||||
// data to the agent.
|
||||
if (DEBUG && okay && mAgent != null) {
|
||||
Slog.i(TAG, "Reusing existing agent instance");
|
||||
}
|
||||
if (okay && mAgent == null) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Need to launch agent for " + pkg);
|
||||
}
|
||||
|
||||
try {
|
||||
mTargetApp =
|
||||
mBackupManagerService.getPackageManager().getApplicationInfo(
|
||||
pkg, 0);
|
||||
|
||||
// If we haven't sent any data to this app yet, we probably
|
||||
// need to clear it first. Check that.
|
||||
if (!mClearedPackages.contains(pkg)) {
|
||||
// apps with their own backup agents are
|
||||
// responsible for coherently managing a full
|
||||
// restore.
|
||||
if (mTargetApp.backupAgentName == null) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG,
|
||||
"Clearing app data preparatory to full restore");
|
||||
}
|
||||
mBackupManagerService.clearApplicationDataSynchronous(pkg, true);
|
||||
} else {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "backup agent ("
|
||||
+ mTargetApp.backupAgentName + ") => no clear");
|
||||
}
|
||||
}
|
||||
mClearedPackages.add(pkg);
|
||||
} else {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "We've initialized this app already; no clear "
|
||||
+ "required");
|
||||
}
|
||||
}
|
||||
|
||||
// All set; now set up the IPC and launch the agent
|
||||
setUpPipes();
|
||||
mAgent = mBackupManagerService.bindToAgentSynchronous(mTargetApp,
|
||||
FullBackup.KEY_VALUE_DATA_TOKEN.equals(info.domain)
|
||||
? ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL
|
||||
: ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL);
|
||||
mAgentPackage = pkg;
|
||||
} catch (IOException e) {
|
||||
// fall through to error handling
|
||||
} catch (NameNotFoundException e) {
|
||||
// fall through to error handling
|
||||
}
|
||||
|
||||
if (mAgent == null) {
|
||||
Slog.e(TAG, "Unable to create agent for " + pkg);
|
||||
okay = false;
|
||||
tearDownPipes();
|
||||
mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
|
||||
}
|
||||
}
|
||||
|
||||
// Sanity check: make sure we never give data to the wrong app. This
|
||||
// should never happen but a little paranoia here won't go amiss.
|
||||
if (okay && !pkg.equals(mAgentPackage)) {
|
||||
Slog.e(TAG, "Restoring data for " + pkg
|
||||
+ " but agent is for " + mAgentPackage);
|
||||
okay = false;
|
||||
}
|
||||
|
||||
// At this point we have an agent ready to handle the full
|
||||
// restore data as well as a pipe for sending data to
|
||||
// that agent. Tell the agent to start reading from the
|
||||
// pipe.
|
||||
if (okay) {
|
||||
boolean agentSuccess = true;
|
||||
long toCopy = info.size;
|
||||
long restoreAgentTimeoutMillis =
|
||||
mAgentTimeoutParameters.getRestoreAgentTimeoutMillis();
|
||||
try {
|
||||
mBackupManagerService.prepareOperationTimeout(
|
||||
token, restoreAgentTimeoutMillis, null, OP_TYPE_RESTORE_WAIT);
|
||||
|
||||
if (FullBackup.OBB_TREE_TOKEN.equals(info.domain)) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Restoring OBB file for " + pkg
|
||||
+ " : " + info.path);
|
||||
}
|
||||
mObbConnection.restoreObbFile(pkg, mPipes[0],
|
||||
info.size, info.type, info.path, info.mode,
|
||||
info.mtime, token,
|
||||
mBackupManagerService.getBackupManagerBinder());
|
||||
} else if (FullBackup.KEY_VALUE_DATA_TOKEN.equals(info.domain)) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Restoring key-value file for " + pkg
|
||||
+ " : " + info.path);
|
||||
}
|
||||
// Set the version saved from manifest entry.
|
||||
info.version = mAppVersion;
|
||||
KeyValueAdbRestoreEngine restoreEngine =
|
||||
new KeyValueAdbRestoreEngine(
|
||||
mBackupManagerService,
|
||||
mBackupManagerService.getDataDir(), info, mPipes[0],
|
||||
mAgent, token);
|
||||
new Thread(restoreEngine, "restore-key-value-runner").start();
|
||||
} else {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Invoking agent to restore file " + info.path);
|
||||
}
|
||||
// fire up the app's agent listening on the socket. If
|
||||
// the agent is running in the system process we can't
|
||||
// just invoke it asynchronously, so we provide a thread
|
||||
// for it here.
|
||||
if (mTargetApp.processName.equals("system")) {
|
||||
Slog.d(TAG, "system process agent - spinning a thread");
|
||||
RestoreFileRunnable runner = new RestoreFileRunnable(
|
||||
mBackupManagerService, mAgent, info, mPipes[0], token);
|
||||
new Thread(runner, "restore-sys-runner").start();
|
||||
} else {
|
||||
mAgent.doRestoreFile(mPipes[0], info.size, info.type,
|
||||
info.domain, info.path, info.mode, info.mtime,
|
||||
token, mBackupManagerService.getBackupManagerBinder());
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// couldn't dup the socket for a process-local restore
|
||||
Slog.d(TAG, "Couldn't establish restore");
|
||||
agentSuccess = false;
|
||||
okay = false;
|
||||
} catch (RemoteException e) {
|
||||
// whoops, remote entity went away. We'll eat the content
|
||||
// ourselves, then, and not copy it over.
|
||||
Slog.e(TAG, "Agent crashed during full restore");
|
||||
agentSuccess = false;
|
||||
okay = false;
|
||||
}
|
||||
|
||||
// Copy over the data if the agent is still good
|
||||
if (okay) {
|
||||
boolean pipeOkay = true;
|
||||
FileOutputStream pipe = new FileOutputStream(
|
||||
mPipes[1].getFileDescriptor());
|
||||
while (toCopy > 0) {
|
||||
int toRead = (toCopy > buffer.length)
|
||||
? buffer.length : (int) toCopy;
|
||||
int nRead = instream.read(buffer, 0, toRead);
|
||||
if (nRead >= 0) {
|
||||
mBytes += nRead;
|
||||
}
|
||||
if (nRead <= 0) {
|
||||
break;
|
||||
}
|
||||
toCopy -= nRead;
|
||||
|
||||
// send it to the output pipe as long as things
|
||||
// are still good
|
||||
if (pipeOkay) {
|
||||
try {
|
||||
pipe.write(buffer, 0, nRead);
|
||||
} catch (IOException e) {
|
||||
Slog.e(TAG, "Failed to write to restore pipe", e);
|
||||
pipeOkay = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// done sending that file! Now we just need to consume
|
||||
// the delta from info.size to the end of block.
|
||||
tarBackupReader.skipTarPadding(info.size);
|
||||
|
||||
// and now that we've sent it all, wait for the remote
|
||||
// side to acknowledge receipt
|
||||
agentSuccess = mBackupManagerService.waitUntilOperationComplete(token);
|
||||
}
|
||||
|
||||
// okay, if the remote end failed at any point, deal with
|
||||
// it by ignoring the rest of the restore on it
|
||||
if (!agentSuccess) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Agent failure restoring " + pkg + "; now ignoring");
|
||||
}
|
||||
mBackupManagerService.getBackupHandler().removeMessages(
|
||||
MSG_RESTORE_OPERATION_TIMEOUT);
|
||||
tearDownPipes();
|
||||
tearDownAgent(mTargetApp, false);
|
||||
mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
|
||||
}
|
||||
}
|
||||
|
||||
// Problems setting up the agent communication, or an already-
|
||||
// ignored package: skip to the next tar stream entry by
|
||||
// reading and discarding this file.
|
||||
if (!okay) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "[discarding file content]");
|
||||
}
|
||||
long bytesToConsume = (info.size + 511) & ~511;
|
||||
while (bytesToConsume > 0) {
|
||||
int toRead = (bytesToConsume > buffer.length)
|
||||
? buffer.length : (int) bytesToConsume;
|
||||
long nRead = instream.read(buffer, 0, toRead);
|
||||
if (nRead >= 0) {
|
||||
mBytes += nRead;
|
||||
}
|
||||
if (nRead <= 0) {
|
||||
break;
|
||||
}
|
||||
bytesToConsume -= nRead;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
if (DEBUG) {
|
||||
Slog.w(TAG, "io exception on restore socket read", e);
|
||||
}
|
||||
// treat as EOF
|
||||
info = null;
|
||||
}
|
||||
|
||||
return (info != null);
|
||||
}
|
||||
|
||||
private static boolean isCanonicalFilePath(String path) {
|
||||
if (path.contains("..") || path.contains("//")) {
|
||||
if (MORE_DEBUG) {
|
||||
Slog.w(TAG, "Dropping invalid path " + path);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void setUpPipes() throws IOException {
|
||||
mPipes = ParcelFileDescriptor.createPipe();
|
||||
}
|
||||
|
||||
private void tearDownPipes() {
|
||||
if (mPipes != null) {
|
||||
try {
|
||||
mPipes[0].close();
|
||||
mPipes[0] = null;
|
||||
mPipes[1].close();
|
||||
mPipes[1] = null;
|
||||
} catch (IOException e) {
|
||||
Slog.w(TAG, "Couldn't close agent pipes", e);
|
||||
}
|
||||
mPipes = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void tearDownAgent(ApplicationInfo app, boolean doRestoreFinished) {
|
||||
if (mAgent != null) {
|
||||
try {
|
||||
// In the adb restore case, we do restore-finished here
|
||||
if (doRestoreFinished) {
|
||||
final int token = mBackupManagerService.generateRandomIntegerToken();
|
||||
long fullBackupAgentTimeoutMillis =
|
||||
mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
|
||||
final AdbRestoreFinishedLatch latch = new AdbRestoreFinishedLatch(
|
||||
mBackupManagerService, token);
|
||||
mBackupManagerService.prepareOperationTimeout(
|
||||
token, fullBackupAgentTimeoutMillis, latch, OP_TYPE_RESTORE_WAIT);
|
||||
if (mTargetApp.processName.equals("system")) {
|
||||
if (MORE_DEBUG) {
|
||||
Slog.d(TAG, "system agent - restoreFinished on thread");
|
||||
}
|
||||
Runnable runner = new RestoreFinishedRunnable(mAgent, token,
|
||||
mBackupManagerService);
|
||||
new Thread(runner, "restore-sys-finished-runner").start();
|
||||
} else {
|
||||
mAgent.doRestoreFinished(token,
|
||||
mBackupManagerService.getBackupManagerBinder());
|
||||
}
|
||||
|
||||
latch.await();
|
||||
}
|
||||
|
||||
mBackupManagerService.tearDownAgentAndKill(app);
|
||||
} catch (RemoteException e) {
|
||||
Slog.d(TAG, "Lost app trying to shut down");
|
||||
}
|
||||
mAgent = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -841,7 +841,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
|
||||
|
||||
final String TAG = "StreamFeederThread";
|
||||
FullRestoreEngine mEngine;
|
||||
EngineThread mEngineThread;
|
||||
FullRestoreEngineThread mEngineThread;
|
||||
|
||||
// pipe through which we read data from the transport. [0] read, [1] write
|
||||
ParcelFileDescriptor[] mTransportPipes;
|
||||
@@ -867,8 +867,8 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
|
||||
mCurrentPackage.packageName);
|
||||
|
||||
mEngine = new FullRestoreEngine(backupManagerService, this, null,
|
||||
mMonitor, mCurrentPackage, false, false, mEphemeralOpToken);
|
||||
mEngineThread = new EngineThread(mEngine, mEnginePipes[0]);
|
||||
mMonitor, mCurrentPackage, false, false, mEphemeralOpToken, false);
|
||||
mEngineThread = new FullRestoreEngineThread(mEngine, mEnginePipes[0]);
|
||||
|
||||
ParcelFileDescriptor eWriteEnd = mEnginePipes[1];
|
||||
ParcelFileDescriptor tReadEnd = mTransportPipes[0];
|
||||
@@ -1031,50 +1031,6 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
|
||||
}
|
||||
}
|
||||
|
||||
class EngineThread implements Runnable {
|
||||
|
||||
FullRestoreEngine mEngine;
|
||||
FileInputStream mEngineStream;
|
||||
|
||||
EngineThread(FullRestoreEngine engine, ParcelFileDescriptor engineSocket) {
|
||||
mEngine = engine;
|
||||
engine.setRunning(true);
|
||||
// We *do* want this FileInputStream to own the underlying fd, so that
|
||||
// when we are finished with it, it closes this end of the pipe in a way
|
||||
// that signals its other end.
|
||||
mEngineStream = new FileInputStream(engineSocket.getFileDescriptor(), true);
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
return mEngine.isRunning();
|
||||
}
|
||||
|
||||
public int waitForResult() {
|
||||
return mEngine.waitForResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
while (mEngine.isRunning()) {
|
||||
// Tell it to be sure to leave the agent instance up after finishing
|
||||
mEngine.restoreOneFile(mEngineStream, false, mEngine.mBuffer,
|
||||
mEngine.mOnlyPackage, mEngine.mAllowApks, mEngine.mEphemeralOpToken,
|
||||
mEngine.mMonitor);
|
||||
}
|
||||
} finally {
|
||||
// Because mEngineStream adopted its underlying FD, this also
|
||||
// closes this end of the pipe.
|
||||
IoUtils.closeQuietly(mEngineStream);
|
||||
}
|
||||
}
|
||||
|
||||
public void handleTimeout() {
|
||||
IoUtils.closeQuietly(mEngineStream);
|
||||
mEngine.handleTimeout();
|
||||
}
|
||||
}
|
||||
|
||||
// state FINAL : tear everything down and we're done.
|
||||
private void finalizeRestore() {
|
||||
if (MORE_DEBUG) {
|
||||
|
||||
Reference in New Issue
Block a user