Add some tests for TarBackupReader
... and PerformAdbRestoreTask. Involves some refactoring, splitting readAppManifest() into two methods. Also a bit of cleanup: make private field actually private and use static imports for some constants. Bug: 38090803 Bug: 37619463 Test: runtest -p com.android.server.backup frameworks-services Change-Id: Ic30a6c5a515da1efb67caaae6eb75f4313797d5c
This commit is contained in:
@@ -34,6 +34,8 @@ public class FileMetadata {
|
||||
public long mode; // e.g. 0666 (actually int)
|
||||
public long mtime; // last mod time, UTC time_t (actually int)
|
||||
public long size; // bytes of content
|
||||
public int version; // App version.
|
||||
public boolean hasApk; // Whether backup file contains apk.
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
@@ -16,6 +16,16 @@
|
||||
|
||||
package com.android.server.backup.restore;
|
||||
|
||||
import static com.android.server.backup.RefactoredBackupManagerService.BACKUP_MANIFEST_FILENAME;
|
||||
import static com.android.server.backup.RefactoredBackupManagerService.BACKUP_METADATA_FILENAME;
|
||||
import static com.android.server.backup.RefactoredBackupManagerService.DEBUG;
|
||||
import static com.android.server.backup.RefactoredBackupManagerService.MORE_DEBUG;
|
||||
import static com.android.server.backup.RefactoredBackupManagerService.OP_TYPE_RESTORE_WAIT;
|
||||
import static com.android.server.backup.RefactoredBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
|
||||
import static com.android.server.backup.RefactoredBackupManagerService.TAG;
|
||||
import static com.android.server.backup.RefactoredBackupManagerService.TIMEOUT_RESTORE_INTERVAL;
|
||||
import static com.android.server.backup.RefactoredBackupManagerService
|
||||
.TIMEOUT_SHARED_BACKUP_INTERVAL;
|
||||
import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT;
|
||||
|
||||
import android.app.ApplicationThreadConstants;
|
||||
@@ -52,58 +62,61 @@ import java.util.HashSet;
|
||||
*/
|
||||
public class FullRestoreEngine extends RestoreEngine {
|
||||
|
||||
private RefactoredBackupManagerService backupManagerService;
|
||||
private final RefactoredBackupManagerService mBackupManagerService;
|
||||
// Task in charge of monitoring timeouts
|
||||
BackupRestoreTask mMonitorTask;
|
||||
private final BackupRestoreTask mMonitorTask;
|
||||
|
||||
private final RestoreInstallObserver mInstallObserver = new RestoreInstallObserver();
|
||||
private final RestoreDeleteObserver mDeleteObserver = new RestoreDeleteObserver();
|
||||
|
||||
// Dedicated observer, if any
|
||||
IFullBackupRestoreObserver mObserver;
|
||||
private IFullBackupRestoreObserver mObserver;
|
||||
|
||||
IBackupManagerMonitor mMonitor;
|
||||
final IBackupManagerMonitor mMonitor;
|
||||
|
||||
// Where we're delivering the file data as we go
|
||||
IBackupAgent mAgent;
|
||||
private IBackupAgent mAgent;
|
||||
|
||||
// Are we permitted to only deliver a specific package's metadata?
|
||||
PackageInfo mOnlyPackage;
|
||||
final PackageInfo mOnlyPackage;
|
||||
|
||||
boolean mAllowApks;
|
||||
boolean mAllowObbs;
|
||||
final boolean mAllowApks;
|
||||
private final boolean mAllowObbs;
|
||||
|
||||
// Which package are we currently handling data for?
|
||||
String mAgentPackage;
|
||||
private String mAgentPackage;
|
||||
|
||||
// Info for working with the target app process
|
||||
ApplicationInfo mTargetApp;
|
||||
private ApplicationInfo mTargetApp;
|
||||
|
||||
// Machinery for restoring OBBs
|
||||
FullBackupObbConnection mObbConnection = null;
|
||||
private FullBackupObbConnection mObbConnection = null;
|
||||
|
||||
// possible handling states for a given package in the restore dataset
|
||||
final HashMap<String, RestorePolicy> mPackagePolicies
|
||||
private final HashMap<String, RestorePolicy> mPackagePolicies
|
||||
= new HashMap<>();
|
||||
|
||||
// installer package names for each encountered app, derived from the manifests
|
||||
final HashMap<String, String> mPackageInstallers = new HashMap<>();
|
||||
private final HashMap<String, String> mPackageInstallers = new HashMap<>();
|
||||
|
||||
// Signatures for a given package found in its manifest file
|
||||
final HashMap<String, Signature[]> mManifestSignatures
|
||||
private final HashMap<String, Signature[]> mManifestSignatures
|
||||
= new HashMap<>();
|
||||
|
||||
// Packages we've already wiped data on when restoring their first file
|
||||
final HashSet<String> mClearedPackages = new HashSet<>();
|
||||
private final HashSet<String> mClearedPackages = new HashSet<>();
|
||||
|
||||
// How much data have we moved?
|
||||
long mBytes;
|
||||
private long mBytes;
|
||||
|
||||
// Working buffer
|
||||
byte[] mBuffer;
|
||||
final byte[] mBuffer;
|
||||
|
||||
// Pipes for moving data
|
||||
ParcelFileDescriptor[] mPipes = null;
|
||||
private ParcelFileDescriptor[] mPipes = null;
|
||||
|
||||
// Widget blob to be restored out-of-band
|
||||
byte[] mWidgetData = null;
|
||||
private byte[] mWidgetData = null;
|
||||
|
||||
final int mEphemeralOpToken;
|
||||
|
||||
@@ -111,7 +124,7 @@ public class FullRestoreEngine extends RestoreEngine {
|
||||
BackupRestoreTask monitorTask, IFullBackupRestoreObserver observer,
|
||||
IBackupManagerMonitor monitor, PackageInfo onlyPackage, boolean allowApks,
|
||||
boolean allowObbs, int ephemeralOpToken) {
|
||||
this.backupManagerService = backupManagerService;
|
||||
mBackupManagerService = backupManagerService;
|
||||
mEphemeralOpToken = ephemeralOpToken;
|
||||
mMonitorTask = monitorTask;
|
||||
mObserver = observer;
|
||||
@@ -134,7 +147,7 @@ public class FullRestoreEngine extends RestoreEngine {
|
||||
public boolean restoreOneFile(InputStream instream, boolean mustKillAgent, byte[] buffer,
|
||||
PackageInfo onlyPackage, boolean allowApks, int token, IBackupManagerMonitor monitor) {
|
||||
if (!isRunning()) {
|
||||
Slog.w(RefactoredBackupManagerService.TAG, "Restore engine used after halting");
|
||||
Slog.w(TAG, "Restore engine used after halting");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -150,12 +163,12 @@ public class FullRestoreEngine extends RestoreEngine {
|
||||
|
||||
FileMetadata info;
|
||||
try {
|
||||
if (RefactoredBackupManagerService.MORE_DEBUG) {
|
||||
Slog.v(RefactoredBackupManagerService.TAG, "Reading tar header for restoring file");
|
||||
if (MORE_DEBUG) {
|
||||
Slog.v(TAG, "Reading tar header for restoring file");
|
||||
}
|
||||
info = tarBackupReader.readTarHeaders();
|
||||
if (info != null) {
|
||||
if (RefactoredBackupManagerService.MORE_DEBUG) {
|
||||
if (MORE_DEBUG) {
|
||||
info.dump();
|
||||
}
|
||||
|
||||
@@ -165,9 +178,7 @@ public class FullRestoreEngine extends RestoreEngine {
|
||||
// one app's data but see a different app's on the wire
|
||||
if (onlyPackage != null) {
|
||||
if (!pkg.equals(onlyPackage.packageName)) {
|
||||
Slog.w(RefactoredBackupManagerService.TAG,
|
||||
"Expected data for " + onlyPackage
|
||||
+ " but saw " + pkg);
|
||||
Slog.w(TAG, "Expected data for " + onlyPackage + " but saw " + pkg);
|
||||
setResult(RestoreEngine.TRANSPORT_FAILURE);
|
||||
setRunning(false);
|
||||
return false;
|
||||
@@ -183,9 +194,8 @@ public class FullRestoreEngine extends RestoreEngine {
|
||||
// Clean up the previous agent relationship if necessary,
|
||||
// and let the observer know we're considering a new app.
|
||||
if (mAgent != null) {
|
||||
if (RefactoredBackupManagerService.DEBUG) {
|
||||
Slog.d(RefactoredBackupManagerService.TAG,
|
||||
"Saw new package; finalizing old one");
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Saw new package; finalizing old one");
|
||||
}
|
||||
// Now we're really done
|
||||
tearDownPipes();
|
||||
@@ -195,19 +205,20 @@ public class FullRestoreEngine extends RestoreEngine {
|
||||
}
|
||||
}
|
||||
|
||||
if (info.path.equals(RefactoredBackupManagerService.BACKUP_MANIFEST_FILENAME)) {
|
||||
RestorePolicy appManifest = tarBackupReader.readAppManifest(
|
||||
backupManagerService.getPackageManager(), allowApks,
|
||||
mManifestSignatures, info);
|
||||
mPackagePolicies.put(pkg, appManifest);
|
||||
if (info.path.equals(BACKUP_MANIFEST_FILENAME)) {
|
||||
Signature[] signatures = tarBackupReader.readAppManifestAndReturnSignatures(
|
||||
info);
|
||||
RestorePolicy restorePolicy = tarBackupReader.chooseRestorePolicy(
|
||||
mBackupManagerService.getPackageManager(), allowApks, info, signatures);
|
||||
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(
|
||||
RefactoredBackupManagerService.BACKUP_METADATA_FILENAME)) {
|
||||
} else if (info.path.equals(BACKUP_METADATA_FILENAME)) {
|
||||
// Metadata blobs!
|
||||
tarBackupReader.readMetadata(info);
|
||||
|
||||
@@ -234,18 +245,17 @@ public class FullRestoreEngine extends RestoreEngine {
|
||||
// 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 (RefactoredBackupManagerService.DEBUG) {
|
||||
Slog.d(RefactoredBackupManagerService.TAG,
|
||||
"APK file; installing");
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "APK file; installing");
|
||||
}
|
||||
// Try to install the app.
|
||||
String installerName = mPackageInstallers.get(pkg);
|
||||
boolean isSuccessfullyInstalled = RestoreUtils.installApk(
|
||||
instream, backupManagerService.getPackageManager(),
|
||||
instream, mBackupManagerService.getPackageManager(),
|
||||
mInstallObserver, mDeleteObserver, mManifestSignatures,
|
||||
mPackagePolicies, info, installerName,
|
||||
bytesReadListener, backupManagerService.getDataDir()
|
||||
);
|
||||
bytesReadListener, mBackupManagerService.getDataDir()
|
||||
);
|
||||
// good to go; promote to ACCEPT
|
||||
mPackagePolicies.put(pkg, isSuccessfullyInstalled
|
||||
? RestorePolicy.ACCEPT
|
||||
@@ -265,9 +275,8 @@ public class FullRestoreEngine extends RestoreEngine {
|
||||
|
||||
case ACCEPT:
|
||||
if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
|
||||
if (RefactoredBackupManagerService.DEBUG) {
|
||||
Slog.d(RefactoredBackupManagerService.TAG,
|
||||
"apk present but ACCEPT");
|
||||
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
|
||||
@@ -281,8 +290,7 @@ public class FullRestoreEngine extends RestoreEngine {
|
||||
// Something has gone dreadfully wrong when determining
|
||||
// the restore policy from the manifest. Ignore the
|
||||
// rest of this package's data.
|
||||
Slog.e(RefactoredBackupManagerService.TAG,
|
||||
"Invalid policy from manifest");
|
||||
Slog.e(TAG, "Invalid policy from manifest");
|
||||
okay = false;
|
||||
mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
|
||||
break;
|
||||
@@ -295,19 +303,17 @@ public class FullRestoreEngine extends RestoreEngine {
|
||||
|
||||
// If the policy is satisfied, go ahead and set up to pipe the
|
||||
// data to the agent.
|
||||
if (RefactoredBackupManagerService.MORE_DEBUG && okay && mAgent != null) {
|
||||
Slog.i(RefactoredBackupManagerService.TAG,
|
||||
"Reusing existing agent instance");
|
||||
if (MORE_DEBUG && okay && mAgent != null) {
|
||||
Slog.i(TAG, "Reusing existing agent instance");
|
||||
}
|
||||
if (okay && mAgent == null) {
|
||||
if (RefactoredBackupManagerService.MORE_DEBUG) {
|
||||
Slog.d(RefactoredBackupManagerService.TAG,
|
||||
"Need to launch agent for " + pkg);
|
||||
if (MORE_DEBUG) {
|
||||
Slog.d(TAG, "Need to launch agent for " + pkg);
|
||||
}
|
||||
|
||||
try {
|
||||
mTargetApp =
|
||||
backupManagerService.getPackageManager().getApplicationInfo(
|
||||
mBackupManagerService.getPackageManager().getApplicationInfo(
|
||||
pkg, 0);
|
||||
|
||||
// If we haven't sent any data to this app yet, we probably
|
||||
@@ -317,29 +323,28 @@ public class FullRestoreEngine extends RestoreEngine {
|
||||
// responsible for coherently managing a full
|
||||
// restore.
|
||||
if (mTargetApp.backupAgentName == null) {
|
||||
if (RefactoredBackupManagerService.DEBUG) {
|
||||
Slog.d(RefactoredBackupManagerService.TAG,
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG,
|
||||
"Clearing app data preparatory to full restore");
|
||||
}
|
||||
backupManagerService.clearApplicationDataSynchronous(pkg);
|
||||
mBackupManagerService.clearApplicationDataSynchronous(pkg);
|
||||
} else {
|
||||
if (RefactoredBackupManagerService.MORE_DEBUG) {
|
||||
Slog.d(RefactoredBackupManagerService.TAG, "backup agent ("
|
||||
if (MORE_DEBUG) {
|
||||
Slog.d(TAG, "backup agent ("
|
||||
+ mTargetApp.backupAgentName + ") => no clear");
|
||||
}
|
||||
}
|
||||
mClearedPackages.add(pkg);
|
||||
} else {
|
||||
if (RefactoredBackupManagerService.MORE_DEBUG) {
|
||||
Slog.d(RefactoredBackupManagerService.TAG,
|
||||
"We've initialized this app already; no clear "
|
||||
+ "required");
|
||||
if (MORE_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 = backupManagerService.bindToAgentSynchronous(mTargetApp,
|
||||
mAgent = mBackupManagerService.bindToAgentSynchronous(mTargetApp,
|
||||
ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL);
|
||||
mAgentPackage = pkg;
|
||||
} catch (IOException e) {
|
||||
@@ -349,9 +354,7 @@ public class FullRestoreEngine extends RestoreEngine {
|
||||
}
|
||||
|
||||
if (mAgent == null) {
|
||||
Slog.e(
|
||||
RefactoredBackupManagerService.TAG,
|
||||
"Unable to create agent for " + pkg);
|
||||
Slog.e(TAG, "Unable to create agent for " + pkg);
|
||||
okay = false;
|
||||
tearDownPipes();
|
||||
mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
|
||||
@@ -361,7 +364,7 @@ public class FullRestoreEngine extends RestoreEngine {
|
||||
// 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(RefactoredBackupManagerService.TAG, "Restoring data for " + pkg
|
||||
Slog.e(TAG, "Restoring data for " + pkg
|
||||
+ " but agent is for " + mAgentPackage);
|
||||
okay = false;
|
||||
}
|
||||
@@ -373,84 +376,74 @@ public class FullRestoreEngine extends RestoreEngine {
|
||||
if (okay) {
|
||||
boolean agentSuccess = true;
|
||||
long toCopy = info.size;
|
||||
final boolean isSharedStorage = pkg.equals(
|
||||
RefactoredBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE);
|
||||
final boolean isSharedStorage = pkg.equals(SHARED_BACKUP_AGENT_PACKAGE);
|
||||
final long timeout = isSharedStorage ?
|
||||
RefactoredBackupManagerService.TIMEOUT_SHARED_BACKUP_INTERVAL :
|
||||
RefactoredBackupManagerService.TIMEOUT_RESTORE_INTERVAL;
|
||||
TIMEOUT_SHARED_BACKUP_INTERVAL :
|
||||
TIMEOUT_RESTORE_INTERVAL;
|
||||
try {
|
||||
backupManagerService.prepareOperationTimeout(token,
|
||||
mBackupManagerService.prepareOperationTimeout(token,
|
||||
timeout,
|
||||
mMonitorTask,
|
||||
RefactoredBackupManagerService.OP_TYPE_RESTORE_WAIT);
|
||||
OP_TYPE_RESTORE_WAIT);
|
||||
|
||||
if (FullBackup.OBB_TREE_TOKEN.equals(info.domain)) {
|
||||
if (RefactoredBackupManagerService.DEBUG) {
|
||||
Slog.d(RefactoredBackupManagerService.TAG,
|
||||
"Restoring OBB file for " + pkg
|
||||
+ " : " + info.path);
|
||||
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,
|
||||
backupManagerService.getBackupManagerBinder());
|
||||
mBackupManagerService.getBackupManagerBinder());
|
||||
} else if (FullBackup.KEY_VALUE_DATA_TOKEN.equals(info.domain)) {
|
||||
// This is only possible during adb restore.
|
||||
// TODO: Refactor to clearly separate the flows.
|
||||
if (RefactoredBackupManagerService.DEBUG) {
|
||||
Slog.d(RefactoredBackupManagerService.TAG,
|
||||
"Restoring key-value file for " + pkg
|
||||
+ " : " + info.path);
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Restoring key-value file for " + pkg
|
||||
+ " : " + info.path);
|
||||
}
|
||||
KeyValueAdbRestoreEngine restoreEngine =
|
||||
new KeyValueAdbRestoreEngine(
|
||||
backupManagerService,
|
||||
backupManagerService.getDataDir(), info, mPipes[0],
|
||||
mBackupManagerService,
|
||||
mBackupManagerService.getDataDir(), info, mPipes[0],
|
||||
mAgent, token);
|
||||
new Thread(restoreEngine, "restore-key-value-runner").start();
|
||||
} else {
|
||||
if (RefactoredBackupManagerService.MORE_DEBUG) {
|
||||
Slog.d(RefactoredBackupManagerService.TAG,
|
||||
"Invoking agent to restore file "
|
||||
+ info.path);
|
||||
if (MORE_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(RefactoredBackupManagerService.TAG,
|
||||
"system process agent - spinning a thread");
|
||||
Slog.d(TAG, "system process agent - spinning a thread");
|
||||
RestoreFileRunnable runner = new RestoreFileRunnable(
|
||||
backupManagerService, mAgent, info, mPipes[0], token);
|
||||
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, backupManagerService.getBackupManagerBinder());
|
||||
token, mBackupManagerService.getBackupManagerBinder());
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// couldn't dup the socket for a process-local restore
|
||||
Slog.d(RefactoredBackupManagerService.TAG,
|
||||
"Couldn't establish 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(RefactoredBackupManagerService.TAG,
|
||||
"Agent crashed during full restore");
|
||||
Slog.e(TAG, "Agent crashed during full restore");
|
||||
agentSuccess = false;
|
||||
okay = false;
|
||||
}
|
||||
|
||||
// Copy over the data if the agent is still good
|
||||
if (okay) {
|
||||
if (RefactoredBackupManagerService.MORE_DEBUG) {
|
||||
Slog.v(RefactoredBackupManagerService.TAG,
|
||||
" copying to restore agent: "
|
||||
+ toCopy + " bytes");
|
||||
if (MORE_DEBUG) {
|
||||
Slog.v(TAG, " copying to restore agent: " + toCopy + " bytes");
|
||||
}
|
||||
boolean pipeOkay = true;
|
||||
FileOutputStream pipe = new FileOutputStream(
|
||||
@@ -473,9 +466,8 @@ public class FullRestoreEngine extends RestoreEngine {
|
||||
try {
|
||||
pipe.write(buffer, 0, nRead);
|
||||
} catch (IOException e) {
|
||||
Slog.e(RefactoredBackupManagerService.TAG,
|
||||
"Failed to write to restore pipe: "
|
||||
+ e.getMessage());
|
||||
Slog.e(TAG, "Failed to write to restore pipe: "
|
||||
+ e.getMessage());
|
||||
pipeOkay = false;
|
||||
}
|
||||
}
|
||||
@@ -487,15 +479,14 @@ public class FullRestoreEngine extends RestoreEngine {
|
||||
|
||||
// and now that we've sent it all, wait for the remote
|
||||
// side to acknowledge receipt
|
||||
agentSuccess = backupManagerService.waitUntilOperationComplete(token);
|
||||
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) {
|
||||
Slog.w(RefactoredBackupManagerService.TAG,
|
||||
"Agent failure restoring " + pkg + "; ending restore");
|
||||
backupManagerService.getBackupHandler().removeMessages(
|
||||
Slog.w(TAG, "Agent failure restoring " + pkg + "; ending restore");
|
||||
mBackupManagerService.getBackupHandler().removeMessages(
|
||||
MSG_RESTORE_OPERATION_TIMEOUT);
|
||||
tearDownPipes();
|
||||
tearDownAgent(mTargetApp);
|
||||
@@ -516,8 +507,8 @@ public class FullRestoreEngine extends RestoreEngine {
|
||||
// dropped file, or an already-ignored package: skip to the
|
||||
// next stream entry by reading and discarding this file.
|
||||
if (!okay) {
|
||||
if (RefactoredBackupManagerService.MORE_DEBUG) {
|
||||
Slog.d(RefactoredBackupManagerService.TAG, "[discarding file content]");
|
||||
if (MORE_DEBUG) {
|
||||
Slog.d(TAG, "[discarding file content]");
|
||||
}
|
||||
long bytesToConsume = (info.size + 511) & ~511;
|
||||
while (bytesToConsume > 0) {
|
||||
@@ -536,9 +527,8 @@ public class FullRestoreEngine extends RestoreEngine {
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
if (RefactoredBackupManagerService.DEBUG) {
|
||||
Slog.w(RefactoredBackupManagerService.TAG,
|
||||
"io exception on restore socket read: " + e.getMessage());
|
||||
if (DEBUG) {
|
||||
Slog.w(TAG, "io exception on restore socket read: " + e.getMessage());
|
||||
}
|
||||
setResult(RestoreEngine.TRANSPORT_FAILURE);
|
||||
info = null;
|
||||
@@ -546,9 +536,8 @@ public class FullRestoreEngine extends RestoreEngine {
|
||||
|
||||
// If we got here we're either running smoothly or we've finished
|
||||
if (info == null) {
|
||||
if (RefactoredBackupManagerService.MORE_DEBUG) {
|
||||
Slog.i(RefactoredBackupManagerService.TAG,
|
||||
"No [more] data for this package; tearing down");
|
||||
if (MORE_DEBUG) {
|
||||
Slog.i(TAG, "No [more] data for this package; tearing down");
|
||||
}
|
||||
tearDownPipes();
|
||||
setRunning(false);
|
||||
@@ -559,11 +548,11 @@ public class FullRestoreEngine extends RestoreEngine {
|
||||
return (info != null);
|
||||
}
|
||||
|
||||
void setUpPipes() throws IOException {
|
||||
private void setUpPipes() throws IOException {
|
||||
mPipes = ParcelFileDescriptor.createPipe();
|
||||
}
|
||||
|
||||
void tearDownPipes() {
|
||||
private void tearDownPipes() {
|
||||
// Teardown might arise from the inline restore processing or from the asynchronous
|
||||
// timeout mechanism, and these might race. Make sure we don't try to close and
|
||||
// null out the pipes twice.
|
||||
@@ -575,16 +564,16 @@ public class FullRestoreEngine extends RestoreEngine {
|
||||
mPipes[1].close();
|
||||
mPipes[1] = null;
|
||||
} catch (IOException e) {
|
||||
Slog.w(RefactoredBackupManagerService.TAG, "Couldn't close agent pipes", e);
|
||||
Slog.w(TAG, "Couldn't close agent pipes", e);
|
||||
}
|
||||
mPipes = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void tearDownAgent(ApplicationInfo app) {
|
||||
private void tearDownAgent(ApplicationInfo app) {
|
||||
if (mAgent != null) {
|
||||
backupManagerService.tearDownAgentAndKill(app);
|
||||
mBackupManagerService.tearDownAgentAndKill(app);
|
||||
mAgent = null;
|
||||
}
|
||||
}
|
||||
@@ -595,13 +584,10 @@ public class FullRestoreEngine extends RestoreEngine {
|
||||
setRunning(false);
|
||||
}
|
||||
|
||||
final RestoreInstallObserver mInstallObserver = new RestoreInstallObserver();
|
||||
final RestoreDeleteObserver mDeleteObserver = new RestoreDeleteObserver();
|
||||
|
||||
private static boolean isRestorableFile(FileMetadata info) {
|
||||
if (FullBackup.CACHE_TREE_TOKEN.equals(info.domain)) {
|
||||
if (RefactoredBackupManagerService.MORE_DEBUG) {
|
||||
Slog.i(RefactoredBackupManagerService.TAG, "Dropping cache file path " + info.path);
|
||||
if (MORE_DEBUG) {
|
||||
Slog.i(TAG, "Dropping cache file path " + info.path);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -612,9 +598,8 @@ public class FullRestoreEngine extends RestoreEngine {
|
||||
// API. Respect the no-backup intention and don't let the data get to
|
||||
// the app.
|
||||
if (info.path.startsWith("no_backup/")) {
|
||||
if (RefactoredBackupManagerService.MORE_DEBUG) {
|
||||
Slog.i(RefactoredBackupManagerService.TAG,
|
||||
"Dropping no_backup file path " + info.path);
|
||||
if (MORE_DEBUG) {
|
||||
Slog.i(TAG, "Dropping no_backup file path " + info.path);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -626,8 +611,8 @@ public class FullRestoreEngine extends RestoreEngine {
|
||||
|
||||
private static boolean isCanonicalFilePath(String path) {
|
||||
if (path.contains("..") || path.contains("//")) {
|
||||
if (RefactoredBackupManagerService.MORE_DEBUG) {
|
||||
Slog.w(RefactoredBackupManagerService.TAG, "Dropping invalid path " + path);
|
||||
if (MORE_DEBUG) {
|
||||
Slog.w(TAG, "Dropping invalid path " + path);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -641,8 +626,7 @@ public class FullRestoreEngine extends RestoreEngine {
|
||||
// TODO: use a more user-friendly name string
|
||||
mObserver.onRestorePackage(name);
|
||||
} catch (RemoteException e) {
|
||||
Slog.w(RefactoredBackupManagerService.TAG,
|
||||
"full restore observer went away: restorePackage");
|
||||
Slog.w(TAG, "full restore observer went away: restorePackage");
|
||||
mObserver = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,20 @@
|
||||
|
||||
package com.android.server.backup.restore;
|
||||
|
||||
import static com.android.server.backup.RefactoredBackupManagerService.BACKUP_FILE_HEADER_MAGIC;
|
||||
import static com.android.server.backup.RefactoredBackupManagerService.BACKUP_FILE_VERSION;
|
||||
import static com.android.server.backup.RefactoredBackupManagerService.BACKUP_MANIFEST_FILENAME;
|
||||
import static com.android.server.backup.RefactoredBackupManagerService.BACKUP_METADATA_FILENAME;
|
||||
import static com.android.server.backup.RefactoredBackupManagerService.DEBUG;
|
||||
import static com.android.server.backup.RefactoredBackupManagerService.MORE_DEBUG;
|
||||
import static com.android.server.backup.RefactoredBackupManagerService.OP_TYPE_RESTORE_WAIT;
|
||||
import static com.android.server.backup.RefactoredBackupManagerService.PBKDF_CURRENT;
|
||||
import static com.android.server.backup.RefactoredBackupManagerService.PBKDF_FALLBACK;
|
||||
import static com.android.server.backup.RefactoredBackupManagerService.SETTINGS_PACKAGE;
|
||||
import static com.android.server.backup.RefactoredBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
|
||||
import static com.android.server.backup.RefactoredBackupManagerService.TAG;
|
||||
import static com.android.server.backup.RefactoredBackupManagerService.TIMEOUT_FULL_BACKUP_INTERVAL;
|
||||
import static com.android.server.backup.RefactoredBackupManagerService.TIMEOUT_RESTORE_INTERVAL;
|
||||
import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT;
|
||||
|
||||
import android.app.ApplicationThreadConstants;
|
||||
@@ -32,6 +46,7 @@ import android.os.ParcelFileDescriptor;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.server.backup.FileMetadata;
|
||||
import com.android.server.backup.KeyValueAdbRestoreEngine;
|
||||
import com.android.server.backup.PackageManagerBackupAgent;
|
||||
@@ -43,7 +58,6 @@ import com.android.server.backup.utils.PasswordUtils;
|
||||
import com.android.server.backup.utils.RestoreUtils;
|
||||
import com.android.server.backup.utils.TarBackupReader;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
@@ -68,38 +82,44 @@ import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
public class PerformAdbRestoreTask implements Runnable {
|
||||
|
||||
private RefactoredBackupManagerService backupManagerService;
|
||||
ParcelFileDescriptor mInputFile;
|
||||
String mCurrentPassword;
|
||||
String mDecryptPassword;
|
||||
IFullBackupRestoreObserver mObserver;
|
||||
AtomicBoolean mLatchObject;
|
||||
IBackupAgent mAgent;
|
||||
PackageManagerBackupAgent mPackageManagerBackupAgent;
|
||||
String mAgentPackage;
|
||||
ApplicationInfo mTargetApp;
|
||||
FullBackupObbConnection mObbConnection = null;
|
||||
ParcelFileDescriptor[] mPipes = null;
|
||||
byte[] mWidgetData = null;
|
||||
private final RefactoredBackupManagerService mBackupManagerService;
|
||||
private final ParcelFileDescriptor mInputFile;
|
||||
private final String mCurrentPassword;
|
||||
private final String mDecryptPassword;
|
||||
private final AtomicBoolean mLatchObject;
|
||||
private final PackageManagerBackupAgent mPackageManagerBackupAgent;
|
||||
private final RestoreInstallObserver mInstallObserver = new RestoreInstallObserver();
|
||||
private final RestoreDeleteObserver mDeleteObserver = new RestoreDeleteObserver();
|
||||
|
||||
long mBytes;
|
||||
private IFullBackupRestoreObserver mObserver;
|
||||
private IBackupAgent mAgent;
|
||||
private String mAgentPackage;
|
||||
private ApplicationInfo mTargetApp;
|
||||
private FullBackupObbConnection mObbConnection = null;
|
||||
private ParcelFileDescriptor[] mPipes = null;
|
||||
private byte[] mWidgetData = null;
|
||||
|
||||
private long mBytes;
|
||||
|
||||
// Runner that can be placed on a separate thread to do in-process invocation
|
||||
// of the "restore finished" API asynchronously. Used by adb restore.
|
||||
class RestoreFinishedRunnable implements Runnable {
|
||||
private static class RestoreFinishedRunnable implements Runnable {
|
||||
|
||||
final IBackupAgent mAgent;
|
||||
final int mToken;
|
||||
private final IBackupAgent mAgent;
|
||||
private final int mToken;
|
||||
private final RefactoredBackupManagerService mBackupManagerService;
|
||||
|
||||
RestoreFinishedRunnable(IBackupAgent agent, int token) {
|
||||
RestoreFinishedRunnable(IBackupAgent agent, int token,
|
||||
RefactoredBackupManagerService backupManagerService) {
|
||||
mAgent = agent;
|
||||
mToken = token;
|
||||
mBackupManagerService = backupManagerService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
mAgent.doRestoreFinished(mToken, backupManagerService.getBackupManagerBinder());
|
||||
mAgent.doRestoreFinished(mToken, mBackupManagerService.getBackupManagerBinder());
|
||||
} catch (RemoteException e) {
|
||||
// never happens; this is used only for local binder calls
|
||||
}
|
||||
@@ -107,23 +127,23 @@ public class PerformAdbRestoreTask implements Runnable {
|
||||
}
|
||||
|
||||
// possible handling states for a given package in the restore dataset
|
||||
final HashMap<String, RestorePolicy> mPackagePolicies
|
||||
private final HashMap<String, RestorePolicy> mPackagePolicies
|
||||
= new HashMap<>();
|
||||
|
||||
// installer package names for each encountered app, derived from the manifests
|
||||
final HashMap<String, String> mPackageInstallers = new HashMap<>();
|
||||
private final HashMap<String, String> mPackageInstallers = new HashMap<>();
|
||||
|
||||
// Signatures for a given package found in its manifest file
|
||||
final HashMap<String, Signature[]> mManifestSignatures
|
||||
private final HashMap<String, Signature[]> mManifestSignatures
|
||||
= new HashMap<>();
|
||||
|
||||
// Packages we've already wiped data on when restoring their first file
|
||||
final HashSet<String> mClearedPackages = new HashSet<>();
|
||||
private final HashSet<String> mClearedPackages = new HashSet<>();
|
||||
|
||||
public PerformAdbRestoreTask(RefactoredBackupManagerService backupManagerService,
|
||||
ParcelFileDescriptor fd, String curPassword, String decryptPassword,
|
||||
IFullBackupRestoreObserver observer, AtomicBoolean latch) {
|
||||
this.backupManagerService = backupManagerService;
|
||||
this.mBackupManagerService = backupManagerService;
|
||||
mInputFile = fd;
|
||||
mCurrentPassword = curPassword;
|
||||
mDecryptPassword = decryptPassword;
|
||||
@@ -139,115 +159,66 @@ public class PerformAdbRestoreTask implements Runnable {
|
||||
// Which packages we've already wiped data on. We prepopulate this
|
||||
// with a whitelist of packages known to be unclearable.
|
||||
mClearedPackages.add("android");
|
||||
mClearedPackages.add(RefactoredBackupManagerService.SETTINGS_PACKAGE);
|
||||
mClearedPackages.add(SETTINGS_PACKAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Slog.i(RefactoredBackupManagerService.TAG, "--- Performing full-dataset restore ---");
|
||||
Slog.i(TAG, "--- Performing full-dataset restore ---");
|
||||
mObbConnection.establish();
|
||||
mObserver = FullBackupRestoreObserverUtils.sendStartRestore(mObserver);
|
||||
|
||||
// Are we able to restore shared-storage data?
|
||||
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
||||
mPackagePolicies.put(RefactoredBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE,
|
||||
mPackagePolicies.put(SHARED_BACKUP_AGENT_PACKAGE,
|
||||
RestorePolicy.ACCEPT);
|
||||
}
|
||||
|
||||
FileInputStream rawInStream = null;
|
||||
DataInputStream rawDataIn = null;
|
||||
try {
|
||||
if (!backupManagerService.backupPasswordMatches(mCurrentPassword)) {
|
||||
if (RefactoredBackupManagerService.DEBUG) {
|
||||
Slog.w(RefactoredBackupManagerService.TAG,
|
||||
"Backup password mismatch; aborting");
|
||||
if (!mBackupManagerService.backupPasswordMatches(mCurrentPassword)) {
|
||||
if (DEBUG) {
|
||||
Slog.w(TAG, "Backup password mismatch; aborting");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
mBytes = 0;
|
||||
byte[] buffer = new byte[32 * 1024];
|
||||
|
||||
rawInStream = new FileInputStream(mInputFile.getFileDescriptor());
|
||||
rawDataIn = new DataInputStream(rawInStream);
|
||||
|
||||
// First, parse out the unencrypted/uncompressed header
|
||||
boolean compressed = false;
|
||||
InputStream preCompressStream = rawInStream;
|
||||
final InputStream in;
|
||||
|
||||
boolean okay = false;
|
||||
final int headerLen = RefactoredBackupManagerService.BACKUP_FILE_HEADER_MAGIC.length();
|
||||
byte[] streamHeader = new byte[headerLen];
|
||||
rawDataIn.readFully(streamHeader);
|
||||
byte[] magicBytes = RefactoredBackupManagerService.BACKUP_FILE_HEADER_MAGIC.getBytes(
|
||||
"UTF-8");
|
||||
if (Arrays.equals(magicBytes, streamHeader)) {
|
||||
// okay, header looks good. now parse out the rest of the fields.
|
||||
String s = readHeaderLine(rawInStream);
|
||||
final int archiveVersion = Integer.parseInt(s);
|
||||
if (archiveVersion <= RefactoredBackupManagerService.BACKUP_FILE_VERSION) {
|
||||
// okay, it's a version we recognize. if it's version 1, we may need
|
||||
// to try two different PBKDF2 regimes to compare checksums.
|
||||
final boolean pbkdf2Fallback = (archiveVersion == 1);
|
||||
|
||||
s = readHeaderLine(rawInStream);
|
||||
compressed = (Integer.parseInt(s) != 0);
|
||||
s = readHeaderLine(rawInStream);
|
||||
if (s.equals("none")) {
|
||||
// no more header to parse; we're good to go
|
||||
okay = true;
|
||||
} else if (mDecryptPassword != null && mDecryptPassword.length() > 0) {
|
||||
preCompressStream = decodeAesHeaderAndInitialize(s, pbkdf2Fallback,
|
||||
rawInStream);
|
||||
if (preCompressStream != null) {
|
||||
okay = true;
|
||||
}
|
||||
} else {
|
||||
Slog.w(RefactoredBackupManagerService.TAG,
|
||||
"Archive is encrypted but no password given");
|
||||
}
|
||||
} else {
|
||||
Slog.w(RefactoredBackupManagerService.TAG, "Wrong header version: " + s);
|
||||
}
|
||||
} else {
|
||||
Slog.w(RefactoredBackupManagerService.TAG, "Didn't read the right header magic");
|
||||
}
|
||||
|
||||
if (!okay) {
|
||||
Slog.w(RefactoredBackupManagerService.TAG, "Invalid restore data; aborting.");
|
||||
InputStream tarInputStream = parseBackupFileHeaderAndReturnTarStream(rawInStream,
|
||||
mDecryptPassword);
|
||||
if (tarInputStream == null) {
|
||||
// There was an error reading the backup file, which is already handled and logged.
|
||||
// Just abort.
|
||||
return;
|
||||
}
|
||||
|
||||
// okay, use the right stream layer based on compression
|
||||
in = (compressed) ? new InflaterInputStream(preCompressStream) : preCompressStream;
|
||||
|
||||
byte[] buffer = new byte[32 * 1024];
|
||||
boolean didRestore;
|
||||
do {
|
||||
didRestore = restoreOneFile(in, false /* mustKillAgent */, buffer,
|
||||
didRestore = restoreOneFile(tarInputStream, false /* mustKillAgent */, buffer,
|
||||
null /* onlyPackage */, true /* allowApks */,
|
||||
backupManagerService.generateRandomIntegerToken(), null /* monitor */);
|
||||
mBackupManagerService.generateRandomIntegerToken(), null /* monitor */);
|
||||
} while (didRestore);
|
||||
|
||||
if (RefactoredBackupManagerService.MORE_DEBUG) {
|
||||
Slog.v(RefactoredBackupManagerService.TAG,
|
||||
"Done consuming input tarfile, total bytes=" + mBytes);
|
||||
if (MORE_DEBUG) {
|
||||
Slog.v(TAG, "Done consuming input tarfile, total bytes=" + mBytes);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Slog.e(RefactoredBackupManagerService.TAG, "Unable to read restore input");
|
||||
Slog.e(TAG, "Unable to read restore input");
|
||||
} finally {
|
||||
tearDownPipes();
|
||||
tearDownAgent(mTargetApp, true);
|
||||
|
||||
try {
|
||||
if (rawDataIn != null) {
|
||||
rawDataIn.close();
|
||||
}
|
||||
if (rawInStream != null) {
|
||||
rawInStream.close();
|
||||
}
|
||||
mInputFile.close();
|
||||
} catch (IOException e) {
|
||||
Slog.w(RefactoredBackupManagerService.TAG, "Close of restore data pipe threw", e);
|
||||
Slog.w(TAG, "Close of restore data pipe threw", e);
|
||||
/* nothing we can do about this */
|
||||
}
|
||||
synchronized (mLatchObject) {
|
||||
@@ -256,12 +227,79 @@ public class PerformAdbRestoreTask implements Runnable {
|
||||
}
|
||||
mObbConnection.tearDown();
|
||||
mObserver = FullBackupRestoreObserverUtils.sendEndRestore(mObserver);
|
||||
Slog.d(RefactoredBackupManagerService.TAG, "Full restore pass complete.");
|
||||
backupManagerService.getWakelock().release();
|
||||
Slog.d(TAG, "Full restore pass complete.");
|
||||
mBackupManagerService.getWakelock().release();
|
||||
}
|
||||
}
|
||||
|
||||
String readHeaderLine(InputStream in) throws IOException {
|
||||
private static void readFullyOrThrow(InputStream in, byte[] buffer) throws IOException {
|
||||
int offset = 0;
|
||||
while (offset < buffer.length) {
|
||||
int bytesRead = in.read(buffer, offset, buffer.length - offset);
|
||||
if (bytesRead <= 0) {
|
||||
throw new IOException("Couldn't fully read data");
|
||||
}
|
||||
offset += bytesRead;
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public static InputStream parseBackupFileHeaderAndReturnTarStream(
|
||||
InputStream rawInputStream,
|
||||
String decryptPassword)
|
||||
throws IOException {
|
||||
// First, parse out the unencrypted/uncompressed header
|
||||
boolean compressed = false;
|
||||
InputStream preCompressStream = rawInputStream;
|
||||
|
||||
boolean okay = false;
|
||||
final int headerLen = BACKUP_FILE_HEADER_MAGIC.length();
|
||||
byte[] streamHeader = new byte[headerLen];
|
||||
readFullyOrThrow(rawInputStream, streamHeader);
|
||||
byte[] magicBytes = BACKUP_FILE_HEADER_MAGIC.getBytes(
|
||||
"UTF-8");
|
||||
if (Arrays.equals(magicBytes, streamHeader)) {
|
||||
// okay, header looks good. now parse out the rest of the fields.
|
||||
String s = readHeaderLine(rawInputStream);
|
||||
final int archiveVersion = Integer.parseInt(s);
|
||||
if (archiveVersion <= BACKUP_FILE_VERSION) {
|
||||
// okay, it's a version we recognize. if it's version 1, we may need
|
||||
// to try two different PBKDF2 regimes to compare checksums.
|
||||
final boolean pbkdf2Fallback = (archiveVersion == 1);
|
||||
|
||||
s = readHeaderLine(rawInputStream);
|
||||
compressed = (Integer.parseInt(s) != 0);
|
||||
s = readHeaderLine(rawInputStream);
|
||||
if (s.equals("none")) {
|
||||
// no more header to parse; we're good to go
|
||||
okay = true;
|
||||
} else if (decryptPassword != null && decryptPassword.length() > 0) {
|
||||
preCompressStream = decodeAesHeaderAndInitialize(
|
||||
decryptPassword, s, pbkdf2Fallback,
|
||||
rawInputStream);
|
||||
if (preCompressStream != null) {
|
||||
okay = true;
|
||||
}
|
||||
} else {
|
||||
Slog.w(TAG, "Archive is encrypted but no password given");
|
||||
}
|
||||
} else {
|
||||
Slog.w(TAG, "Wrong header version: " + s);
|
||||
}
|
||||
} else {
|
||||
Slog.w(TAG, "Didn't read the right header magic");
|
||||
}
|
||||
|
||||
if (!okay) {
|
||||
Slog.w(TAG, "Invalid restore data; aborting.");
|
||||
return null;
|
||||
}
|
||||
|
||||
// okay, use the right stream layer based on compression
|
||||
return compressed ? new InflaterInputStream(preCompressStream) : preCompressStream;
|
||||
}
|
||||
|
||||
private static String readHeaderLine(InputStream in) throws IOException {
|
||||
int c;
|
||||
StringBuilder buffer = new StringBuilder(80);
|
||||
while ((c = in.read()) >= 0) {
|
||||
@@ -273,7 +311,8 @@ public class PerformAdbRestoreTask implements Runnable {
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
InputStream attemptMasterKeyDecryption(String algorithm, byte[] userSalt, byte[] ckSalt,
|
||||
private static InputStream attemptMasterKeyDecryption(String decryptPassword, String algorithm,
|
||||
byte[] userSalt, byte[] ckSalt,
|
||||
int rounds, String userIvHex, String masterKeyBlobHex, InputStream rawInStream,
|
||||
boolean doLog) {
|
||||
InputStream result = null;
|
||||
@@ -281,7 +320,7 @@ public class PerformAdbRestoreTask implements Runnable {
|
||||
try {
|
||||
Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||
SecretKey userKey = PasswordUtils
|
||||
.buildPasswordKey(algorithm, mDecryptPassword, userSalt,
|
||||
.buildPasswordKey(algorithm, decryptPassword, userSalt,
|
||||
rounds);
|
||||
byte[] IV = PasswordUtils.hexToByteArray(userIvHex);
|
||||
IvParameterSpec ivSpec = new IvParameterSpec(IV);
|
||||
@@ -317,11 +356,11 @@ public class PerformAdbRestoreTask implements Runnable {
|
||||
// Only if all of the above worked properly will 'result' be assigned
|
||||
result = new CipherInputStream(rawInStream, c);
|
||||
} else if (doLog) {
|
||||
Slog.w(RefactoredBackupManagerService.TAG, "Incorrect password");
|
||||
Slog.w(TAG, "Incorrect password");
|
||||
}
|
||||
} catch (InvalidAlgorithmParameterException e) {
|
||||
if (doLog) {
|
||||
Slog.e(RefactoredBackupManagerService.TAG, "Needed parameter spec unavailable!", e);
|
||||
Slog.e(TAG, "Needed parameter spec unavailable!", e);
|
||||
}
|
||||
} catch (BadPaddingException e) {
|
||||
// This case frequently occurs when the wrong password is used to decrypt
|
||||
@@ -329,31 +368,32 @@ public class PerformAdbRestoreTask implements Runnable {
|
||||
// used in the checksum failure log in order to avoid providing additional
|
||||
// information to an attacker.
|
||||
if (doLog) {
|
||||
Slog.w(RefactoredBackupManagerService.TAG, "Incorrect password");
|
||||
Slog.w(TAG, "Incorrect password");
|
||||
}
|
||||
} catch (IllegalBlockSizeException e) {
|
||||
if (doLog) {
|
||||
Slog.w(RefactoredBackupManagerService.TAG, "Invalid block size in master key");
|
||||
Slog.w(TAG, "Invalid block size in master key");
|
||||
}
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
if (doLog) {
|
||||
Slog.e(RefactoredBackupManagerService.TAG,
|
||||
"Needed decryption algorithm unavailable!");
|
||||
Slog.e(TAG, "Needed decryption algorithm unavailable!");
|
||||
}
|
||||
} catch (NoSuchPaddingException e) {
|
||||
if (doLog) {
|
||||
Slog.e(RefactoredBackupManagerService.TAG, "Needed padding mechanism unavailable!");
|
||||
Slog.e(TAG, "Needed padding mechanism unavailable!");
|
||||
}
|
||||
} catch (InvalidKeyException e) {
|
||||
if (doLog) {
|
||||
Slog.w(RefactoredBackupManagerService.TAG, "Illegal password; aborting");
|
||||
Slog.w(TAG, "Illegal password; aborting");
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
InputStream decodeAesHeaderAndInitialize(String encryptionName, boolean pbkdf2Fallback,
|
||||
private static InputStream decodeAesHeaderAndInitialize(String decryptPassword,
|
||||
String encryptionName,
|
||||
boolean pbkdf2Fallback,
|
||||
InputStream rawInStream) {
|
||||
InputStream result = null;
|
||||
try {
|
||||
@@ -371,22 +411,21 @@ public class PerformAdbRestoreTask implements Runnable {
|
||||
String masterKeyBlobHex = readHeaderLine(rawInStream); // 9
|
||||
|
||||
// decrypt the master key blob
|
||||
result = attemptMasterKeyDecryption(RefactoredBackupManagerService.PBKDF_CURRENT,
|
||||
result = attemptMasterKeyDecryption(decryptPassword, PBKDF_CURRENT,
|
||||
userSalt, ckSalt,
|
||||
rounds, userIvHex, masterKeyBlobHex, rawInStream, false);
|
||||
if (result == null && pbkdf2Fallback) {
|
||||
result = attemptMasterKeyDecryption(
|
||||
RefactoredBackupManagerService.PBKDF_FALLBACK, userSalt, ckSalt,
|
||||
decryptPassword, PBKDF_FALLBACK, userSalt, ckSalt,
|
||||
rounds, userIvHex, masterKeyBlobHex, rawInStream, true);
|
||||
}
|
||||
} else {
|
||||
Slog.w(RefactoredBackupManagerService.TAG,
|
||||
"Unsupported encryption method: " + encryptionName);
|
||||
Slog.w(TAG, "Unsupported encryption method: " + encryptionName);
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
Slog.w(RefactoredBackupManagerService.TAG, "Can't parse restore data header");
|
||||
Slog.w(TAG, "Can't parse restore data header");
|
||||
} catch (IOException e) {
|
||||
Slog.w(RefactoredBackupManagerService.TAG, "Can't read input header");
|
||||
Slog.w(TAG, "Can't read input header");
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -406,7 +445,7 @@ public class PerformAdbRestoreTask implements Runnable {
|
||||
try {
|
||||
info = tarBackupReader.readTarHeaders();
|
||||
if (info != null) {
|
||||
if (RefactoredBackupManagerService.MORE_DEBUG) {
|
||||
if (MORE_DEBUG) {
|
||||
info.dump();
|
||||
}
|
||||
|
||||
@@ -421,9 +460,8 @@ public class PerformAdbRestoreTask implements Runnable {
|
||||
// Clean up the previous agent relationship if necessary,
|
||||
// and let the observer know we're considering a new app.
|
||||
if (mAgent != null) {
|
||||
if (RefactoredBackupManagerService.DEBUG) {
|
||||
Slog.d(RefactoredBackupManagerService.TAG,
|
||||
"Saw new package; finalizing old one");
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Saw new package; finalizing old one");
|
||||
}
|
||||
// Now we're really done
|
||||
tearDownPipes();
|
||||
@@ -433,19 +471,21 @@ public class PerformAdbRestoreTask implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
if (info.path.equals(RefactoredBackupManagerService.BACKUP_MANIFEST_FILENAME)) {
|
||||
RestorePolicy appManifest = tarBackupReader.readAppManifest(
|
||||
backupManagerService.getPackageManager(), allowApks,
|
||||
mManifestSignatures, info);
|
||||
mPackagePolicies.put(pkg, appManifest);
|
||||
if (info.path.equals(BACKUP_MANIFEST_FILENAME)) {
|
||||
Signature[] signatures = tarBackupReader.readAppManifestAndReturnSignatures(
|
||||
info);
|
||||
RestorePolicy restorePolicy = tarBackupReader.chooseRestorePolicy(
|
||||
mBackupManagerService.getPackageManager(), allowApks,
|
||||
info, signatures);
|
||||
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(
|
||||
RefactoredBackupManagerService.BACKUP_METADATA_FILENAME)) {
|
||||
} else if (info.path.equals(BACKUP_METADATA_FILENAME)) {
|
||||
// Metadata blobs!
|
||||
tarBackupReader.readMetadata(info);
|
||||
|
||||
@@ -472,18 +512,17 @@ public class PerformAdbRestoreTask implements Runnable {
|
||||
// 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 (RefactoredBackupManagerService.DEBUG) {
|
||||
Slog.d(RefactoredBackupManagerService.TAG,
|
||||
"APK file; installing");
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "APK file; installing");
|
||||
}
|
||||
// Try to install the app.
|
||||
String installerName = mPackageInstallers.get(pkg);
|
||||
boolean isSuccessfullyInstalled = RestoreUtils.installApk(
|
||||
instream, backupManagerService.getPackageManager(),
|
||||
instream, mBackupManagerService.getPackageManager(),
|
||||
mInstallObserver, mDeleteObserver, mManifestSignatures,
|
||||
mPackagePolicies, info, installerName,
|
||||
bytesReadListener, backupManagerService.getDataDir()
|
||||
);
|
||||
bytesReadListener, mBackupManagerService.getDataDir()
|
||||
);
|
||||
// good to go; promote to ACCEPT
|
||||
mPackagePolicies.put(pkg, isSuccessfullyInstalled
|
||||
? RestorePolicy.ACCEPT
|
||||
@@ -503,9 +542,8 @@ public class PerformAdbRestoreTask implements Runnable {
|
||||
|
||||
case ACCEPT:
|
||||
if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
|
||||
if (RefactoredBackupManagerService.DEBUG) {
|
||||
Slog.d(RefactoredBackupManagerService.TAG,
|
||||
"apk present but ACCEPT");
|
||||
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
|
||||
@@ -519,8 +557,7 @@ public class PerformAdbRestoreTask implements Runnable {
|
||||
// Something has gone dreadfully wrong when determining
|
||||
// the restore policy from the manifest. Ignore the
|
||||
// rest of this package's data.
|
||||
Slog.e(RefactoredBackupManagerService.TAG,
|
||||
"Invalid policy from manifest");
|
||||
Slog.e(TAG, "Invalid policy from manifest");
|
||||
okay = false;
|
||||
mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
|
||||
break;
|
||||
@@ -533,19 +570,17 @@ public class PerformAdbRestoreTask implements Runnable {
|
||||
|
||||
// If the policy is satisfied, go ahead and set up to pipe the
|
||||
// data to the agent.
|
||||
if (RefactoredBackupManagerService.DEBUG && okay && mAgent != null) {
|
||||
Slog.i(RefactoredBackupManagerService.TAG,
|
||||
"Reusing existing agent instance");
|
||||
if (DEBUG && okay && mAgent != null) {
|
||||
Slog.i(TAG, "Reusing existing agent instance");
|
||||
}
|
||||
if (okay && mAgent == null) {
|
||||
if (RefactoredBackupManagerService.DEBUG) {
|
||||
Slog.d(RefactoredBackupManagerService.TAG,
|
||||
"Need to launch agent for " + pkg);
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Need to launch agent for " + pkg);
|
||||
}
|
||||
|
||||
try {
|
||||
mTargetApp =
|
||||
backupManagerService.getPackageManager().getApplicationInfo(
|
||||
mBackupManagerService.getPackageManager().getApplicationInfo(
|
||||
pkg, 0);
|
||||
|
||||
// If we haven't sent any data to this app yet, we probably
|
||||
@@ -555,29 +590,28 @@ public class PerformAdbRestoreTask implements Runnable {
|
||||
// responsible for coherently managing a full
|
||||
// restore.
|
||||
if (mTargetApp.backupAgentName == null) {
|
||||
if (RefactoredBackupManagerService.DEBUG) {
|
||||
Slog.d(RefactoredBackupManagerService.TAG,
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG,
|
||||
"Clearing app data preparatory to full restore");
|
||||
}
|
||||
backupManagerService.clearApplicationDataSynchronous(pkg);
|
||||
mBackupManagerService.clearApplicationDataSynchronous(pkg);
|
||||
} else {
|
||||
if (RefactoredBackupManagerService.DEBUG) {
|
||||
Slog.d(RefactoredBackupManagerService.TAG, "backup agent ("
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "backup agent ("
|
||||
+ mTargetApp.backupAgentName + ") => no clear");
|
||||
}
|
||||
}
|
||||
mClearedPackages.add(pkg);
|
||||
} else {
|
||||
if (RefactoredBackupManagerService.DEBUG) {
|
||||
Slog.d(RefactoredBackupManagerService.TAG,
|
||||
"We've initialized this app already; no clear "
|
||||
+ "required");
|
||||
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 = backupManagerService.bindToAgentSynchronous(mTargetApp,
|
||||
mAgent = mBackupManagerService.bindToAgentSynchronous(mTargetApp,
|
||||
ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL);
|
||||
mAgentPackage = pkg;
|
||||
} catch (IOException e) {
|
||||
@@ -587,9 +621,7 @@ public class PerformAdbRestoreTask implements Runnable {
|
||||
}
|
||||
|
||||
if (mAgent == null) {
|
||||
Slog.e(
|
||||
RefactoredBackupManagerService.TAG,
|
||||
"Unable to create agent for " + pkg);
|
||||
Slog.e(TAG, "Unable to create agent for " + pkg);
|
||||
okay = false;
|
||||
tearDownPipes();
|
||||
mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
|
||||
@@ -599,7 +631,7 @@ public class PerformAdbRestoreTask implements Runnable {
|
||||
// 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(RefactoredBackupManagerService.TAG, "Restoring data for " + pkg
|
||||
Slog.e(TAG, "Restoring data for " + pkg
|
||||
+ " but agent is for " + mAgentPackage);
|
||||
okay = false;
|
||||
}
|
||||
@@ -612,67 +644,60 @@ public class PerformAdbRestoreTask implements Runnable {
|
||||
boolean agentSuccess = true;
|
||||
long toCopy = info.size;
|
||||
try {
|
||||
backupManagerService
|
||||
mBackupManagerService
|
||||
.prepareOperationTimeout(token,
|
||||
RefactoredBackupManagerService.TIMEOUT_RESTORE_INTERVAL,
|
||||
TIMEOUT_RESTORE_INTERVAL,
|
||||
null,
|
||||
RefactoredBackupManagerService.OP_TYPE_RESTORE_WAIT);
|
||||
OP_TYPE_RESTORE_WAIT);
|
||||
|
||||
if (FullBackup.OBB_TREE_TOKEN.equals(info.domain)) {
|
||||
if (RefactoredBackupManagerService.DEBUG) {
|
||||
Slog.d(RefactoredBackupManagerService.TAG,
|
||||
"Restoring OBB file for " + pkg
|
||||
+ " : " + info.path);
|
||||
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,
|
||||
backupManagerService.getBackupManagerBinder());
|
||||
mBackupManagerService.getBackupManagerBinder());
|
||||
} else if (FullBackup.KEY_VALUE_DATA_TOKEN.equals(info.domain)) {
|
||||
if (RefactoredBackupManagerService.DEBUG) {
|
||||
Slog.d(RefactoredBackupManagerService.TAG,
|
||||
"Restoring key-value file for " + pkg
|
||||
+ " : " + info.path);
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Restoring key-value file for " + pkg
|
||||
+ " : " + info.path);
|
||||
}
|
||||
KeyValueAdbRestoreEngine restoreEngine =
|
||||
new KeyValueAdbRestoreEngine(
|
||||
backupManagerService,
|
||||
backupManagerService.getDataDir(), info, mPipes[0],
|
||||
mBackupManagerService,
|
||||
mBackupManagerService.getDataDir(), info, mPipes[0],
|
||||
mAgent, token);
|
||||
new Thread(restoreEngine, "restore-key-value-runner").start();
|
||||
} else {
|
||||
if (RefactoredBackupManagerService.DEBUG) {
|
||||
Slog.d(RefactoredBackupManagerService.TAG,
|
||||
"Invoking agent to restore file "
|
||||
+ info.path);
|
||||
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(RefactoredBackupManagerService.TAG,
|
||||
"system process agent - spinning a thread");
|
||||
Slog.d(TAG, "system process agent - spinning a thread");
|
||||
RestoreFileRunnable runner = new RestoreFileRunnable(
|
||||
backupManagerService, mAgent, info, mPipes[0], token);
|
||||
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, backupManagerService.getBackupManagerBinder());
|
||||
token, mBackupManagerService.getBackupManagerBinder());
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// couldn't dup the socket for a process-local restore
|
||||
Slog.d(RefactoredBackupManagerService.TAG,
|
||||
"Couldn't establish 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(RefactoredBackupManagerService.TAG,
|
||||
"Agent crashed during full restore");
|
||||
Slog.e(TAG, "Agent crashed during full restore");
|
||||
agentSuccess = false;
|
||||
okay = false;
|
||||
}
|
||||
@@ -700,8 +725,7 @@ public class PerformAdbRestoreTask implements Runnable {
|
||||
try {
|
||||
pipe.write(buffer, 0, nRead);
|
||||
} catch (IOException e) {
|
||||
Slog.e(RefactoredBackupManagerService.TAG,
|
||||
"Failed to write to restore pipe", e);
|
||||
Slog.e(TAG, "Failed to write to restore pipe", e);
|
||||
pipeOkay = false;
|
||||
}
|
||||
}
|
||||
@@ -713,17 +737,16 @@ public class PerformAdbRestoreTask implements Runnable {
|
||||
|
||||
// and now that we've sent it all, wait for the remote
|
||||
// side to acknowledge receipt
|
||||
agentSuccess = backupManagerService.waitUntilOperationComplete(token);
|
||||
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 (RefactoredBackupManagerService.DEBUG) {
|
||||
Slog.d(RefactoredBackupManagerService.TAG,
|
||||
"Agent failure restoring " + pkg + "; now ignoring");
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Agent failure restoring " + pkg + "; now ignoring");
|
||||
}
|
||||
backupManagerService.getBackupHandler().removeMessages(
|
||||
mBackupManagerService.getBackupHandler().removeMessages(
|
||||
MSG_RESTORE_OPERATION_TIMEOUT);
|
||||
tearDownPipes();
|
||||
tearDownAgent(mTargetApp, false);
|
||||
@@ -735,8 +758,8 @@ public class PerformAdbRestoreTask implements Runnable {
|
||||
// ignored package: skip to the next tar stream entry by
|
||||
// reading and discarding this file.
|
||||
if (!okay) {
|
||||
if (RefactoredBackupManagerService.DEBUG) {
|
||||
Slog.d(RefactoredBackupManagerService.TAG, "[discarding file content]");
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "[discarding file content]");
|
||||
}
|
||||
long bytesToConsume = (info.size + 511) & ~511;
|
||||
while (bytesToConsume > 0) {
|
||||
@@ -755,9 +778,8 @@ public class PerformAdbRestoreTask implements Runnable {
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
if (RefactoredBackupManagerService.DEBUG) {
|
||||
Slog.w(RefactoredBackupManagerService.TAG, "io exception on restore socket read",
|
||||
e);
|
||||
if (DEBUG) {
|
||||
Slog.w(TAG, "io exception on restore socket read", e);
|
||||
}
|
||||
// treat as EOF
|
||||
info = null;
|
||||
@@ -768,8 +790,8 @@ public class PerformAdbRestoreTask implements Runnable {
|
||||
|
||||
private static boolean isCanonicalFilePath(String path) {
|
||||
if (path.contains("..") || path.contains("//")) {
|
||||
if (RefactoredBackupManagerService.MORE_DEBUG) {
|
||||
Slog.w(RefactoredBackupManagerService.TAG, "Dropping invalid path " + path);
|
||||
if (MORE_DEBUG) {
|
||||
Slog.w(TAG, "Dropping invalid path " + path);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -777,11 +799,11 @@ public class PerformAdbRestoreTask implements Runnable {
|
||||
return true;
|
||||
}
|
||||
|
||||
void setUpPipes() throws IOException {
|
||||
private void setUpPipes() throws IOException {
|
||||
mPipes = ParcelFileDescriptor.createPipe();
|
||||
}
|
||||
|
||||
void tearDownPipes() {
|
||||
private void tearDownPipes() {
|
||||
if (mPipes != null) {
|
||||
try {
|
||||
mPipes[0].close();
|
||||
@@ -789,49 +811,46 @@ public class PerformAdbRestoreTask implements Runnable {
|
||||
mPipes[1].close();
|
||||
mPipes[1] = null;
|
||||
} catch (IOException e) {
|
||||
Slog.w(RefactoredBackupManagerService.TAG, "Couldn't close agent pipes", e);
|
||||
Slog.w(TAG, "Couldn't close agent pipes", e);
|
||||
}
|
||||
mPipes = null;
|
||||
}
|
||||
}
|
||||
|
||||
void tearDownAgent(ApplicationInfo app, boolean doRestoreFinished) {
|
||||
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 = backupManagerService.generateRandomIntegerToken();
|
||||
final int token = mBackupManagerService.generateRandomIntegerToken();
|
||||
final AdbRestoreFinishedLatch latch = new AdbRestoreFinishedLatch(
|
||||
backupManagerService, token);
|
||||
backupManagerService
|
||||
mBackupManagerService, token);
|
||||
mBackupManagerService
|
||||
.prepareOperationTimeout(token,
|
||||
RefactoredBackupManagerService.TIMEOUT_FULL_BACKUP_INTERVAL,
|
||||
TIMEOUT_FULL_BACKUP_INTERVAL,
|
||||
latch,
|
||||
RefactoredBackupManagerService.OP_TYPE_RESTORE_WAIT);
|
||||
OP_TYPE_RESTORE_WAIT);
|
||||
if (mTargetApp.processName.equals("system")) {
|
||||
if (RefactoredBackupManagerService.MORE_DEBUG) {
|
||||
Slog.d(RefactoredBackupManagerService.TAG,
|
||||
"system agent - restoreFinished on thread");
|
||||
if (MORE_DEBUG) {
|
||||
Slog.d(TAG, "system agent - restoreFinished on thread");
|
||||
}
|
||||
Runnable runner = new RestoreFinishedRunnable(mAgent, token);
|
||||
Runnable runner = new RestoreFinishedRunnable(mAgent, token,
|
||||
mBackupManagerService);
|
||||
new Thread(runner, "restore-sys-finished-runner").start();
|
||||
} else {
|
||||
mAgent.doRestoreFinished(token,
|
||||
backupManagerService.getBackupManagerBinder());
|
||||
mBackupManagerService.getBackupManagerBinder());
|
||||
}
|
||||
|
||||
latch.await();
|
||||
}
|
||||
|
||||
backupManagerService.tearDownAgentAndKill(app);
|
||||
mBackupManagerService.tearDownAgentAndKill(app);
|
||||
} catch (RemoteException e) {
|
||||
Slog.d(RefactoredBackupManagerService.TAG, "Lost app trying to shut down");
|
||||
Slog.d(TAG, "Lost app trying to shut down");
|
||||
}
|
||||
mAgent = null;
|
||||
}
|
||||
}
|
||||
|
||||
final RestoreInstallObserver mInstallObserver = new RestoreInstallObserver();
|
||||
final RestoreDeleteObserver mDeleteObserver = new RestoreDeleteObserver();
|
||||
|
||||
}
|
||||
|
||||
@@ -34,6 +34,10 @@ import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_SYSTEM_APP_NO
|
||||
import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_VERSIONS_MATCH;
|
||||
import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_VERSION_OF_BACKUP_OLDER;
|
||||
|
||||
import static com.android.server.backup.RefactoredBackupManagerService.DEBUG;
|
||||
import static com.android.server.backup.RefactoredBackupManagerService.MORE_DEBUG;
|
||||
import static com.android.server.backup.RefactoredBackupManagerService.TAG;
|
||||
|
||||
import android.app.backup.BackupAgent;
|
||||
import android.app.backup.BackupManagerMonitor;
|
||||
import android.app.backup.FullBackup;
|
||||
@@ -54,7 +58,6 @@ import java.io.ByteArrayInputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Utility methods to read backup tar file.
|
||||
@@ -150,23 +153,20 @@ public class TarBackupReader {
|
||||
case '5': {
|
||||
info.type = BackupAgent.TYPE_DIRECTORY;
|
||||
if (info.size != 0) {
|
||||
Slog.w(RefactoredBackupManagerService.TAG,
|
||||
"Directory entry with nonzero size in header");
|
||||
Slog.w(TAG, "Directory entry with nonzero size in header");
|
||||
info.size = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0: {
|
||||
// presume EOF
|
||||
if (RefactoredBackupManagerService.MORE_DEBUG) {
|
||||
Slog.w(RefactoredBackupManagerService.TAG,
|
||||
"Saw type=0 in tar header block, info=" + info);
|
||||
if (MORE_DEBUG) {
|
||||
Slog.w(TAG, "Saw type=0 in tar header block, info=" + info);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
default: {
|
||||
Slog.e(RefactoredBackupManagerService.TAG,
|
||||
"Unknown tar entity type: " + typeChar);
|
||||
Slog.e(TAG, "Unknown tar entity type: " + typeChar);
|
||||
throw new IOException("Unknown entity type " + typeChar);
|
||||
}
|
||||
}
|
||||
@@ -180,9 +180,8 @@ public class TarBackupReader {
|
||||
info.path = info.path.substring(FullBackup.SHARED_PREFIX.length());
|
||||
info.packageName = RefactoredBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
|
||||
info.domain = FullBackup.SHARED_STORAGE_TOKEN;
|
||||
if (RefactoredBackupManagerService.DEBUG) {
|
||||
Slog.i(RefactoredBackupManagerService.TAG,
|
||||
"File in shared storage: " + info.path);
|
||||
if (DEBUG) {
|
||||
Slog.i(TAG, "File in shared storage: " + info.path);
|
||||
}
|
||||
} else if (FullBackup.APPS_PREFIX.regionMatches(0,
|
||||
info.path, 0, FullBackup.APPS_PREFIX.length())) {
|
||||
@@ -214,10 +213,9 @@ public class TarBackupReader {
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
if (RefactoredBackupManagerService.DEBUG) {
|
||||
Slog.e(RefactoredBackupManagerService.TAG,
|
||||
"Parse error in header: " + e.getMessage());
|
||||
if (RefactoredBackupManagerService.MORE_DEBUG) {
|
||||
if (DEBUG) {
|
||||
Slog.e(TAG, "Parse error in header: " + e.getMessage());
|
||||
if (MORE_DEBUG) {
|
||||
hexLog(block);
|
||||
}
|
||||
}
|
||||
@@ -242,41 +240,35 @@ public class TarBackupReader {
|
||||
if (size <= 0) {
|
||||
throw new IllegalArgumentException("size must be > 0");
|
||||
}
|
||||
if (RefactoredBackupManagerService.MORE_DEBUG) {
|
||||
Slog.i(RefactoredBackupManagerService.TAG, " ... readExactly(" + size + ") called");
|
||||
if (MORE_DEBUG) {
|
||||
Slog.i(TAG, " ... readExactly(" + size + ") called");
|
||||
}
|
||||
int soFar = 0;
|
||||
while (soFar < size) {
|
||||
int nRead = in.read(buffer, offset + soFar, size - soFar);
|
||||
if (nRead <= 0) {
|
||||
if (RefactoredBackupManagerService.MORE_DEBUG) {
|
||||
Slog.w(RefactoredBackupManagerService.TAG,
|
||||
"- wanted exactly " + size + " but got only " + soFar);
|
||||
if (MORE_DEBUG) {
|
||||
Slog.w(TAG, "- wanted exactly " + size + " but got only " + soFar);
|
||||
}
|
||||
break;
|
||||
}
|
||||
soFar += nRead;
|
||||
if (RefactoredBackupManagerService.MORE_DEBUG) {
|
||||
Slog.v(RefactoredBackupManagerService.TAG,
|
||||
" + got " + nRead + "; now wanting " + (size - soFar));
|
||||
if (MORE_DEBUG) {
|
||||
Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soFar));
|
||||
}
|
||||
}
|
||||
return soFar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads app manifest and returns a policy constant.
|
||||
* Reads app manifest, filling version and hasApk fields in the metadata, and returns array of
|
||||
* signatures.
|
||||
*
|
||||
* @param packageManager - PackageManager instance.
|
||||
* @param allowApks - allow restore set to include apks.
|
||||
* @param manifestSignatures - parsed signatures will be put here.
|
||||
* @param info - file metadata.
|
||||
* @return a policy constant.
|
||||
* @return array of signatures or null, in case of an error.
|
||||
* @throws IOException in case of an error.
|
||||
*/
|
||||
public RestorePolicy readAppManifest(PackageManager packageManager,
|
||||
boolean allowApks, HashMap<String, Signature[]> manifestSignatures,
|
||||
FileMetadata info)
|
||||
public Signature[] readAppManifestAndReturnSignatures(FileMetadata info)
|
||||
throws IOException {
|
||||
// Fail on suspiciously large manifest files
|
||||
if (info.size > 64 * 1024) {
|
||||
@@ -284,9 +276,9 @@ public class TarBackupReader {
|
||||
}
|
||||
|
||||
byte[] buffer = new byte[(int) info.size];
|
||||
if (RefactoredBackupManagerService.MORE_DEBUG) {
|
||||
Slog.i(RefactoredBackupManagerService.TAG,
|
||||
" readAppManifest() looking for " + info.size + " bytes");
|
||||
if (MORE_DEBUG) {
|
||||
Slog.i(TAG,
|
||||
" readAppManifestAndReturnSignatures() looking for " + info.size + " bytes");
|
||||
}
|
||||
if (readExactly(mInputStream, buffer, 0, (int) info.size) == info.size) {
|
||||
mBytesReadListener.onBytesRead(info.size);
|
||||
@@ -294,7 +286,6 @@ public class TarBackupReader {
|
||||
throw new IOException("Unexpected EOF in manifest");
|
||||
}
|
||||
|
||||
RestorePolicy policy = RestorePolicy.IGNORE;
|
||||
String[] str = new String[1];
|
||||
int offset = 0;
|
||||
|
||||
@@ -307,7 +298,7 @@ public class TarBackupReader {
|
||||
// TODO: handle <original-package>
|
||||
if (manifestPackage.equals(info.packageName)) {
|
||||
offset = extractLine(buffer, offset, str);
|
||||
version = Integer.parseInt(str[0]); // app version
|
||||
info.version = Integer.parseInt(str[0]); // app version
|
||||
offset = extractLine(buffer, offset, str);
|
||||
// This is the platform version, which we don't use, but we parse it
|
||||
// as a safety against corruption in the manifest.
|
||||
@@ -315,7 +306,7 @@ public class TarBackupReader {
|
||||
offset = extractLine(buffer, offset, str);
|
||||
info.installerPackageName = (str[0].length() > 0) ? str[0] : null;
|
||||
offset = extractLine(buffer, offset, str);
|
||||
boolean hasApk = str[0].equals("1");
|
||||
info.hasApk = str[0].equals("1");
|
||||
offset = extractLine(buffer, offset, str);
|
||||
int numSigs = Integer.parseInt(str[0]);
|
||||
if (numSigs > 0) {
|
||||
@@ -324,162 +315,9 @@ public class TarBackupReader {
|
||||
offset = extractLine(buffer, offset, str);
|
||||
sigs[i] = new Signature(str[0]);
|
||||
}
|
||||
manifestSignatures.put(info.packageName, sigs);
|
||||
|
||||
// Okay, got the manifest info we need...
|
||||
try {
|
||||
PackageInfo pkgInfo = packageManager.getPackageInfo(
|
||||
info.packageName, PackageManager.GET_SIGNATURES);
|
||||
// Fall through to IGNORE if the app explicitly disallows backup
|
||||
final int flags = pkgInfo.applicationInfo.flags;
|
||||
if ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) {
|
||||
// Restore system-uid-space packages only if they have
|
||||
// defined a custom backup agent
|
||||
if ((pkgInfo.applicationInfo.uid
|
||||
>= Process.FIRST_APPLICATION_UID)
|
||||
|| (pkgInfo.applicationInfo.backupAgentName != null)) {
|
||||
// Verify signatures against any installed version; if they
|
||||
// don't match, then we fall though and ignore the data. The
|
||||
// signatureMatch() method explicitly ignores the signature
|
||||
// check for packages installed on the system partition, because
|
||||
// such packages are signed with the platform cert instead of
|
||||
// the app developer's cert, so they're different on every
|
||||
// device.
|
||||
if (AppBackupUtils.signaturesMatch(sigs,
|
||||
pkgInfo)) {
|
||||
if ((pkgInfo.applicationInfo.flags
|
||||
& ApplicationInfo.FLAG_RESTORE_ANY_VERSION) != 0) {
|
||||
Slog.i(RefactoredBackupManagerService.TAG,
|
||||
"Package has restoreAnyVersion; taking data");
|
||||
mMonitor = BackupManagerMonitorUtils.monitorEvent(
|
||||
mMonitor,
|
||||
LOG_EVENT_ID_RESTORE_ANY_VERSION,
|
||||
pkgInfo,
|
||||
LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
|
||||
null);
|
||||
policy = RestorePolicy.ACCEPT;
|
||||
} else if (pkgInfo.versionCode >= version) {
|
||||
Slog.i(RefactoredBackupManagerService.TAG,
|
||||
"Sig + version match; taking data");
|
||||
policy = RestorePolicy.ACCEPT;
|
||||
mMonitor = BackupManagerMonitorUtils.monitorEvent(
|
||||
mMonitor,
|
||||
LOG_EVENT_ID_VERSIONS_MATCH,
|
||||
pkgInfo,
|
||||
LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
|
||||
null);
|
||||
} else {
|
||||
// The data is from a newer version of the app than
|
||||
// is presently installed. That means we can only
|
||||
// use it if the matching apk is also supplied.
|
||||
if (allowApks) {
|
||||
Slog.i(RefactoredBackupManagerService.TAG,
|
||||
"Data version " + version
|
||||
+ " is newer than installed "
|
||||
+ "version "
|
||||
+ pkgInfo.versionCode
|
||||
+ " - requiring apk");
|
||||
policy = RestorePolicy.ACCEPT_IF_APK;
|
||||
} else {
|
||||
Slog.i(RefactoredBackupManagerService.TAG,
|
||||
"Data requires newer version "
|
||||
+ version + "; ignoring");
|
||||
mMonitor = BackupManagerMonitorUtils
|
||||
.monitorEvent(mMonitor,
|
||||
LOG_EVENT_ID_VERSION_OF_BACKUP_OLDER,
|
||||
pkgInfo,
|
||||
LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
|
||||
BackupManagerMonitorUtils
|
||||
.putMonitoringExtra(
|
||||
null,
|
||||
EXTRA_LOG_OLD_VERSION,
|
||||
version));
|
||||
|
||||
policy = RestorePolicy.IGNORE;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Slog.w(RefactoredBackupManagerService.TAG,
|
||||
"Restore manifest signatures do not match "
|
||||
+ "installed application for "
|
||||
+ info.packageName);
|
||||
mMonitor = BackupManagerMonitorUtils.monitorEvent(
|
||||
mMonitor,
|
||||
LOG_EVENT_ID_FULL_RESTORE_SIGNATURE_MISMATCH,
|
||||
pkgInfo,
|
||||
LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
|
||||
null);
|
||||
}
|
||||
} else {
|
||||
Slog.w(RefactoredBackupManagerService.TAG,
|
||||
"Package " + info.packageName
|
||||
+ " is system level with no agent");
|
||||
mMonitor = BackupManagerMonitorUtils.monitorEvent(
|
||||
|
||||
|
||||
mMonitor,
|
||||
LOG_EVENT_ID_SYSTEM_APP_NO_AGENT,
|
||||
pkgInfo,
|
||||
LOG_EVENT_CATEGORY_AGENT,
|
||||
null);
|
||||
}
|
||||
} else {
|
||||
if (RefactoredBackupManagerService.DEBUG) {
|
||||
Slog.i(RefactoredBackupManagerService.TAG,
|
||||
"Restore manifest from "
|
||||
+ info.packageName + " but allowBackup=false");
|
||||
}
|
||||
mMonitor = BackupManagerMonitorUtils.monitorEvent(
|
||||
mMonitor,
|
||||
LOG_EVENT_ID_FULL_RESTORE_ALLOW_BACKUP_FALSE,
|
||||
pkgInfo,
|
||||
LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
|
||||
null);
|
||||
}
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
// Okay, the target app isn't installed. We can process
|
||||
// the restore properly only if the dataset provides the
|
||||
// apk file and we can successfully install it.
|
||||
if (allowApks) {
|
||||
if (RefactoredBackupManagerService.DEBUG) {
|
||||
Slog.i(RefactoredBackupManagerService.TAG,
|
||||
"Package " + info.packageName
|
||||
+ " not installed; requiring apk in dataset");
|
||||
}
|
||||
policy = RestorePolicy.ACCEPT_IF_APK;
|
||||
} else {
|
||||
policy = RestorePolicy.IGNORE;
|
||||
}
|
||||
Bundle monitoringExtras = BackupManagerMonitorUtils.putMonitoringExtra(
|
||||
null,
|
||||
EXTRA_LOG_EVENT_PACKAGE_NAME, info.packageName);
|
||||
monitoringExtras = BackupManagerMonitorUtils.putMonitoringExtra(
|
||||
monitoringExtras,
|
||||
EXTRA_LOG_POLICY_ALLOW_APKS, allowApks);
|
||||
mMonitor = BackupManagerMonitorUtils.monitorEvent(
|
||||
mMonitor,
|
||||
LOG_EVENT_ID_APK_NOT_INSTALLED,
|
||||
null,
|
||||
LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
|
||||
monitoringExtras);
|
||||
}
|
||||
|
||||
if (policy == RestorePolicy.ACCEPT_IF_APK && !hasApk) {
|
||||
Slog.i(RefactoredBackupManagerService.TAG,
|
||||
"Cannot restore package " + info.packageName
|
||||
+ " without the matching .apk");
|
||||
mMonitor = BackupManagerMonitorUtils.monitorEvent(
|
||||
mMonitor,
|
||||
LOG_EVENT_ID_CANNOT_RESTORE_WITHOUT_APK,
|
||||
null,
|
||||
LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
|
||||
BackupManagerMonitorUtils.putMonitoringExtra(null,
|
||||
EXTRA_LOG_EVENT_PACKAGE_NAME, info.packageName));
|
||||
}
|
||||
return sigs;
|
||||
} else {
|
||||
Slog.i(RefactoredBackupManagerService.TAG,
|
||||
"Missing signature on backed-up package "
|
||||
+ info.packageName);
|
||||
Slog.i(TAG, "Missing signature on backed-up package " + info.packageName);
|
||||
mMonitor = BackupManagerMonitorUtils.monitorEvent(
|
||||
mMonitor,
|
||||
LOG_EVENT_ID_MISSING_SIGNATURE,
|
||||
@@ -489,9 +327,8 @@ public class TarBackupReader {
|
||||
EXTRA_LOG_EVENT_PACKAGE_NAME, info.packageName));
|
||||
}
|
||||
} else {
|
||||
Slog.i(RefactoredBackupManagerService.TAG,
|
||||
"Expected package " + info.packageName
|
||||
+ " but restore manifest claims " + manifestPackage);
|
||||
Slog.i(TAG, "Expected package " + info.packageName
|
||||
+ " but restore manifest claims " + manifestPackage);
|
||||
Bundle monitoringExtras = BackupManagerMonitorUtils.putMonitoringExtra(null,
|
||||
EXTRA_LOG_EVENT_PACKAGE_NAME, info.packageName);
|
||||
monitoringExtras = BackupManagerMonitorUtils.putMonitoringExtra(
|
||||
@@ -505,9 +342,8 @@ public class TarBackupReader {
|
||||
monitoringExtras);
|
||||
}
|
||||
} else {
|
||||
Slog.i(RefactoredBackupManagerService.TAG,
|
||||
"Unknown restore manifest version " + version
|
||||
+ " for package " + info.packageName);
|
||||
Slog.i(TAG, "Unknown restore manifest version " + version
|
||||
+ " for package " + info.packageName);
|
||||
Bundle monitoringExtras = BackupManagerMonitorUtils.putMonitoringExtra(null,
|
||||
EXTRA_LOG_EVENT_PACKAGE_NAME, info.packageName);
|
||||
monitoringExtras = BackupManagerMonitorUtils.putMonitoringExtra(monitoringExtras,
|
||||
@@ -521,8 +357,7 @@ public class TarBackupReader {
|
||||
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
Slog.w(RefactoredBackupManagerService.TAG,
|
||||
"Corrupt restore manifest for package " + info.packageName);
|
||||
Slog.w(TAG, "Corrupt restore manifest for package " + info.packageName);
|
||||
mMonitor = BackupManagerMonitorUtils.monitorEvent(
|
||||
mMonitor,
|
||||
BackupManagerMonitor.LOG_EVENT_ID_CORRUPT_MANIFEST,
|
||||
@@ -531,7 +366,166 @@ public class TarBackupReader {
|
||||
BackupManagerMonitorUtils.putMonitoringExtra(null, EXTRA_LOG_EVENT_PACKAGE_NAME,
|
||||
info.packageName));
|
||||
} catch (IllegalArgumentException e) {
|
||||
Slog.w(RefactoredBackupManagerService.TAG, e.getMessage());
|
||||
Slog.w(TAG, e.getMessage());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Chooses restore policy.
|
||||
*
|
||||
* @param packageManager - PackageManager instance.
|
||||
* @param allowApks - allow restore set to include apks.
|
||||
* @param info - file metadata.
|
||||
* @param signatures - array of signatures parsed from backup file.
|
||||
* @return a restore policy constant.
|
||||
*/
|
||||
public RestorePolicy chooseRestorePolicy(PackageManager packageManager,
|
||||
boolean allowApks, FileMetadata info, Signature[] signatures) {
|
||||
if (signatures == null) {
|
||||
return RestorePolicy.IGNORE;
|
||||
}
|
||||
|
||||
RestorePolicy policy = RestorePolicy.IGNORE;
|
||||
|
||||
// Okay, got the manifest info we need...
|
||||
try {
|
||||
PackageInfo pkgInfo = packageManager.getPackageInfo(
|
||||
info.packageName, PackageManager.GET_SIGNATURES);
|
||||
// Fall through to IGNORE if the app explicitly disallows backup
|
||||
final int flags = pkgInfo.applicationInfo.flags;
|
||||
if ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) {
|
||||
// Restore system-uid-space packages only if they have
|
||||
// defined a custom backup agent
|
||||
if ((pkgInfo.applicationInfo.uid
|
||||
>= Process.FIRST_APPLICATION_UID)
|
||||
|| (pkgInfo.applicationInfo.backupAgentName != null)) {
|
||||
// Verify signatures against any installed version; if they
|
||||
// don't match, then we fall though and ignore the data. The
|
||||
// signatureMatch() method explicitly ignores the signature
|
||||
// check for packages installed on the system partition, because
|
||||
// such packages are signed with the platform cert instead of
|
||||
// the app developer's cert, so they're different on every
|
||||
// device.
|
||||
if (AppBackupUtils.signaturesMatch(signatures, pkgInfo)) {
|
||||
if ((pkgInfo.applicationInfo.flags
|
||||
& ApplicationInfo.FLAG_RESTORE_ANY_VERSION) != 0) {
|
||||
Slog.i(TAG, "Package has restoreAnyVersion; taking data");
|
||||
mMonitor = BackupManagerMonitorUtils.monitorEvent(
|
||||
mMonitor,
|
||||
LOG_EVENT_ID_RESTORE_ANY_VERSION,
|
||||
pkgInfo,
|
||||
LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
|
||||
null);
|
||||
policy = RestorePolicy.ACCEPT;
|
||||
} else if (pkgInfo.versionCode >= info.version) {
|
||||
Slog.i(TAG, "Sig + version match; taking data");
|
||||
policy = RestorePolicy.ACCEPT;
|
||||
mMonitor = BackupManagerMonitorUtils.monitorEvent(
|
||||
mMonitor,
|
||||
LOG_EVENT_ID_VERSIONS_MATCH,
|
||||
pkgInfo,
|
||||
LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
|
||||
null);
|
||||
} else {
|
||||
// The data is from a newer version of the app than
|
||||
// is presently installed. That means we can only
|
||||
// use it if the matching apk is also supplied.
|
||||
if (allowApks) {
|
||||
Slog.i(TAG, "Data version " + info.version
|
||||
+ " is newer than installed "
|
||||
+ "version "
|
||||
+ pkgInfo.versionCode
|
||||
+ " - requiring apk");
|
||||
policy = RestorePolicy.ACCEPT_IF_APK;
|
||||
} else {
|
||||
Slog.i(TAG, "Data requires newer version "
|
||||
+ info.version + "; ignoring");
|
||||
mMonitor = BackupManagerMonitorUtils
|
||||
.monitorEvent(mMonitor,
|
||||
LOG_EVENT_ID_VERSION_OF_BACKUP_OLDER,
|
||||
pkgInfo,
|
||||
LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
|
||||
BackupManagerMonitorUtils
|
||||
.putMonitoringExtra(
|
||||
null,
|
||||
EXTRA_LOG_OLD_VERSION,
|
||||
info.version));
|
||||
|
||||
policy = RestorePolicy.IGNORE;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Slog.w(TAG, "Restore manifest signatures do not match "
|
||||
+ "installed application for "
|
||||
+ info.packageName);
|
||||
mMonitor = BackupManagerMonitorUtils.monitorEvent(
|
||||
mMonitor,
|
||||
LOG_EVENT_ID_FULL_RESTORE_SIGNATURE_MISMATCH,
|
||||
pkgInfo,
|
||||
LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
|
||||
null);
|
||||
}
|
||||
} else {
|
||||
Slog.w(TAG, "Package " + info.packageName
|
||||
+ " is system level with no agent");
|
||||
mMonitor = BackupManagerMonitorUtils.monitorEvent(
|
||||
mMonitor,
|
||||
LOG_EVENT_ID_SYSTEM_APP_NO_AGENT,
|
||||
pkgInfo,
|
||||
LOG_EVENT_CATEGORY_AGENT,
|
||||
null);
|
||||
}
|
||||
} else {
|
||||
if (DEBUG) {
|
||||
Slog.i(TAG,
|
||||
"Restore manifest from " + info.packageName + " but allowBackup=false");
|
||||
}
|
||||
mMonitor = BackupManagerMonitorUtils.monitorEvent(
|
||||
mMonitor,
|
||||
LOG_EVENT_ID_FULL_RESTORE_ALLOW_BACKUP_FALSE,
|
||||
pkgInfo,
|
||||
LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
|
||||
null);
|
||||
}
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
// Okay, the target app isn't installed. We can process
|
||||
// the restore properly only if the dataset provides the
|
||||
// apk file and we can successfully install it.
|
||||
if (allowApks) {
|
||||
if (DEBUG) {
|
||||
Slog.i(TAG, "Package " + info.packageName
|
||||
+ " not installed; requiring apk in dataset");
|
||||
}
|
||||
policy = RestorePolicy.ACCEPT_IF_APK;
|
||||
} else {
|
||||
policy = RestorePolicy.IGNORE;
|
||||
}
|
||||
Bundle monitoringExtras = BackupManagerMonitorUtils.putMonitoringExtra(
|
||||
null,
|
||||
EXTRA_LOG_EVENT_PACKAGE_NAME, info.packageName);
|
||||
monitoringExtras = BackupManagerMonitorUtils.putMonitoringExtra(
|
||||
monitoringExtras,
|
||||
EXTRA_LOG_POLICY_ALLOW_APKS, allowApks);
|
||||
mMonitor = BackupManagerMonitorUtils.monitorEvent(
|
||||
mMonitor,
|
||||
LOG_EVENT_ID_APK_NOT_INSTALLED,
|
||||
null,
|
||||
LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
|
||||
monitoringExtras);
|
||||
}
|
||||
|
||||
if (policy == RestorePolicy.ACCEPT_IF_APK && !info.hasApk) {
|
||||
Slog.i(TAG, "Cannot restore package " + info.packageName
|
||||
+ " without the matching .apk");
|
||||
mMonitor = BackupManagerMonitorUtils.monitorEvent(
|
||||
mMonitor,
|
||||
LOG_EVENT_ID_CANNOT_RESTORE_WITHOUT_APK,
|
||||
null,
|
||||
LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
|
||||
BackupManagerMonitorUtils.putMonitoringExtra(null,
|
||||
EXTRA_LOG_EVENT_PACKAGE_NAME, info.packageName));
|
||||
}
|
||||
|
||||
return policy;
|
||||
@@ -543,9 +537,8 @@ public class TarBackupReader {
|
||||
long partial = (size + 512) % 512;
|
||||
if (partial > 0) {
|
||||
final int needed = 512 - (int) partial;
|
||||
if (RefactoredBackupManagerService.MORE_DEBUG) {
|
||||
Slog.i(RefactoredBackupManagerService.TAG,
|
||||
"Skipping tar padding: " + needed + " bytes");
|
||||
if (MORE_DEBUG) {
|
||||
Slog.i(TAG, "Skipping tar padding: " + needed + " bytes");
|
||||
}
|
||||
byte[] buffer = new byte[needed];
|
||||
if (readExactly(mInputStream, buffer, 0, needed) == needed) {
|
||||
@@ -588,24 +581,21 @@ public class TarBackupReader {
|
||||
int token = in.readInt();
|
||||
int size = in.readInt();
|
||||
if (size > 64 * 1024) {
|
||||
throw new IOException("Datum "
|
||||
+ Integer.toHexString(token)
|
||||
throw new IOException("Datum " + Integer.toHexString(token)
|
||||
+ " too big; corrupt? size=" + info.size);
|
||||
}
|
||||
switch (token) {
|
||||
case RefactoredBackupManagerService.BACKUP_WIDGET_METADATA_TOKEN: {
|
||||
if (RefactoredBackupManagerService.MORE_DEBUG) {
|
||||
Slog.i(RefactoredBackupManagerService.TAG,
|
||||
"Got widget metadata for " + info.packageName);
|
||||
if (MORE_DEBUG) {
|
||||
Slog.i(TAG, "Got widget metadata for " + info.packageName);
|
||||
}
|
||||
mWidgetData = new byte[size];
|
||||
in.read(mWidgetData);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
if (RefactoredBackupManagerService.DEBUG) {
|
||||
Slog.i(RefactoredBackupManagerService.TAG, "Ignoring metadata blob "
|
||||
+ Integer.toHexString(token)
|
||||
if (DEBUG) {
|
||||
Slog.i(TAG, "Ignoring metadata blob " + Integer.toHexString(token)
|
||||
+ " for " + info.packageName);
|
||||
}
|
||||
in.skipBytes(size);
|
||||
@@ -614,9 +604,9 @@ public class TarBackupReader {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Slog.w(RefactoredBackupManagerService.TAG,
|
||||
"Metadata mismatch: package " + info.packageName
|
||||
+ " but widget data for " + pkg);
|
||||
Slog.w(TAG,
|
||||
"Metadata mismatch: package " + info.packageName + " but widget data for "
|
||||
+ pkg);
|
||||
|
||||
Bundle monitoringExtras = BackupManagerMonitorUtils.putMonitoringExtra(null,
|
||||
EXTRA_LOG_EVENT_PACKAGE_NAME, info.packageName);
|
||||
@@ -630,7 +620,7 @@ public class TarBackupReader {
|
||||
monitoringExtras);
|
||||
}
|
||||
} else {
|
||||
Slog.w(RefactoredBackupManagerService.TAG, "Unsupported metadata version " + version);
|
||||
Slog.w(TAG, "Unsupported metadata version " + version);
|
||||
|
||||
Bundle monitoringExtras = BackupManagerMonitorUtils
|
||||
.putMonitoringExtra(null, EXTRA_LOG_EVENT_PACKAGE_NAME,
|
||||
@@ -692,7 +682,7 @@ public class TarBackupReader {
|
||||
throws IOException {
|
||||
// We should never see a pax extended header larger than this
|
||||
if (info.size > 32 * 1024) {
|
||||
Slog.w(RefactoredBackupManagerService.TAG,
|
||||
Slog.w(TAG,
|
||||
"Suspiciously large pax header size " + info.size
|
||||
+ " - aborting");
|
||||
throw new IOException("Sanity failure: pax header size " + info.size);
|
||||
@@ -740,8 +730,8 @@ public class TarBackupReader {
|
||||
} else if ("size".equals(keyStr)) {
|
||||
info.size = Long.parseLong(valStr);
|
||||
} else {
|
||||
if (RefactoredBackupManagerService.DEBUG) {
|
||||
Slog.i(RefactoredBackupManagerService.TAG, "Unhandled pax key: " + key);
|
||||
if (DEBUG) {
|
||||
Slog.i(TAG, "Unhandled pax key: " + key);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License
|
||||
*/
|
||||
|
||||
package com.android.server.backup.restore;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.content.Context;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.filters.SmallTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.frameworks.servicestests.R;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
@SmallTest
|
||||
@Presubmit
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class PerformAdbRestoreTaskTest {
|
||||
private Context mContext;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
mContext = InstrumentationRegistry.getContext();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseBackupFileAndReturnTarStream_backupNotEncrypted_returnsNonNull()
|
||||
throws Exception {
|
||||
InputStream inputStream = mContext.getResources().openRawResource(
|
||||
R.raw.backup_telephony_no_password);
|
||||
InputStream tarInputStream = PerformAdbRestoreTask.parseBackupFileHeaderAndReturnTarStream(
|
||||
inputStream, null);
|
||||
|
||||
assertThat(tarInputStream).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
parseBackupFileAndReturnTarStream_backupEncryptedAndPasswordProvided_returnsNonNull()
|
||||
throws Exception {
|
||||
InputStream inputStream = mContext.getResources().openRawResource(
|
||||
R.raw.backup_telephony_with_password);
|
||||
InputStream tarInputStream = PerformAdbRestoreTask.parseBackupFileHeaderAndReturnTarStream(
|
||||
inputStream, "123");
|
||||
|
||||
assertThat(tarInputStream).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
parseBackupFileAndReturnTarStream_backupEncryptedAndPasswordNotProvided_returnsNull()
|
||||
throws Exception {
|
||||
InputStream inputStream = mContext.getResources().openRawResource(
|
||||
R.raw.backup_telephony_with_password);
|
||||
InputStream tarInputStream = PerformAdbRestoreTask.parseBackupFileHeaderAndReturnTarStream(
|
||||
inputStream, null);
|
||||
|
||||
assertThat(tarInputStream).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
parseBackupFileAndReturnTarStream_backupEncryptedAndIncorrectPassword_returnsNull()
|
||||
throws Exception {
|
||||
InputStream inputStream = mContext.getResources().openRawResource(
|
||||
R.raw.backup_telephony_with_password);
|
||||
InputStream tarInputStream = PerformAdbRestoreTask.parseBackupFileHeaderAndReturnTarStream(
|
||||
inputStream, "1234");
|
||||
|
||||
assertThat(tarInputStream).isNull();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License
|
||||
*/
|
||||
|
||||
package com.android.server.backup.utils;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.app.backup.IBackupManagerMonitor;
|
||||
import android.content.Context;
|
||||
import android.content.pm.Signature;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.filters.SmallTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.frameworks.servicestests.R;
|
||||
import com.android.server.backup.FileMetadata;
|
||||
import com.android.server.backup.restore.PerformAdbRestoreTask;
|
||||
|
||||
import com.google.common.hash.Hashing;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
@SmallTest
|
||||
@Presubmit
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class TarBackupReaderTest {
|
||||
private static final String TELEPHONY_PACKAGE_NAME = "com.android.providers.telephony";
|
||||
private static final String TELEPHONY_PACKAGE_SIGNATURE_SHA256 =
|
||||
"301aa3cb081134501c45f1422abc66c24224fd5ded5fdc8f17e697176fd866aa";
|
||||
private static final int TELEPHONY_PACKAGE_VERSION = 25;
|
||||
|
||||
@Mock
|
||||
private BytesReadListener mBytesReadListenerMock;
|
||||
@Mock
|
||||
private IBackupManagerMonitor mBackupManagerMonitorMock;
|
||||
private Context mContext;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
mContext = InstrumentationRegistry.getContext();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readTarHeaders_backupEncrypted_correctlyParsesFileMetadata() throws Exception {
|
||||
InputStream inputStream = mContext.getResources().openRawResource(
|
||||
R.raw.backup_telephony_with_password);
|
||||
InputStream tarInputStream = PerformAdbRestoreTask.parseBackupFileHeaderAndReturnTarStream(
|
||||
inputStream, "123");
|
||||
TarBackupReader tarBackupReader = new TarBackupReader(tarInputStream,
|
||||
mBytesReadListenerMock, mBackupManagerMonitorMock);
|
||||
FileMetadata fileMetadata = tarBackupReader.readTarHeaders();
|
||||
|
||||
assertThat(fileMetadata.packageName).isEqualTo(TELEPHONY_PACKAGE_NAME);
|
||||
assertThat(fileMetadata.mode).isEqualTo(0600);
|
||||
assertThat(fileMetadata.size).isEqualTo(2438);
|
||||
assertThat(fileMetadata.domain).isEqualTo(null);
|
||||
assertThat(fileMetadata.path).isEqualTo("_manifest");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readTarHeaders_backupNotEncrypted_correctlyParsesFileMetadata() throws Exception {
|
||||
InputStream inputStream = mContext.getResources().openRawResource(
|
||||
R.raw.backup_telephony_no_password);
|
||||
InputStream tarInputStream = PerformAdbRestoreTask.parseBackupFileHeaderAndReturnTarStream(
|
||||
inputStream, null);
|
||||
TarBackupReader tarBackupReader = new TarBackupReader(tarInputStream,
|
||||
mBytesReadListenerMock, mBackupManagerMonitorMock);
|
||||
FileMetadata fileMetadata = tarBackupReader.readTarHeaders();
|
||||
|
||||
assertThat(fileMetadata.packageName).isEqualTo(TELEPHONY_PACKAGE_NAME);
|
||||
assertThat(fileMetadata.mode).isEqualTo(0600);
|
||||
assertThat(fileMetadata.size).isEqualTo(2438);
|
||||
assertThat(fileMetadata.domain).isEqualTo(null);
|
||||
assertThat(fileMetadata.path).isEqualTo("_manifest");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readAppManifest_backupEncrypted_correctlyParsesAppManifest() throws Exception {
|
||||
InputStream inputStream = mContext.getResources().openRawResource(
|
||||
R.raw.backup_telephony_with_password);
|
||||
InputStream tarInputStream = PerformAdbRestoreTask.parseBackupFileHeaderAndReturnTarStream(
|
||||
inputStream, "123");
|
||||
TarBackupReader tarBackupReader = new TarBackupReader(tarInputStream,
|
||||
mBytesReadListenerMock, mBackupManagerMonitorMock);
|
||||
FileMetadata fileMetadata = tarBackupReader.readTarHeaders();
|
||||
|
||||
Signature[] signatures = tarBackupReader.readAppManifestAndReturnSignatures(fileMetadata);
|
||||
|
||||
assertThat(fileMetadata.version).isEqualTo(TELEPHONY_PACKAGE_VERSION);
|
||||
assertThat(fileMetadata.hasApk).isFalse();
|
||||
assertThat(signatures).isNotNull();
|
||||
assertThat(signatures).hasLength(1);
|
||||
|
||||
String signatureSha256 = Hashing.sha256().hashBytes(signatures[0].toByteArray()).toString();
|
||||
assertThat(signatureSha256).isEqualTo(TELEPHONY_PACKAGE_SIGNATURE_SHA256);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readAppManifest_backupNotEncrypted_correctlyParsesAppManifest() throws Exception {
|
||||
InputStream inputStream = mContext.getResources().openRawResource(
|
||||
R.raw.backup_telephony_no_password);
|
||||
InputStream tarInputStream = PerformAdbRestoreTask.parseBackupFileHeaderAndReturnTarStream(
|
||||
inputStream, null);
|
||||
TarBackupReader tarBackupReader = new TarBackupReader(tarInputStream,
|
||||
mBytesReadListenerMock, mBackupManagerMonitorMock);
|
||||
FileMetadata fileMetadata = tarBackupReader.readTarHeaders();
|
||||
|
||||
Signature[] signatures = tarBackupReader.readAppManifestAndReturnSignatures(fileMetadata);
|
||||
|
||||
assertThat(fileMetadata.version).isEqualTo(TELEPHONY_PACKAGE_VERSION);
|
||||
assertThat(fileMetadata.hasApk).isFalse();
|
||||
assertThat(signatures).isNotNull();
|
||||
assertThat(signatures).hasLength(1);
|
||||
|
||||
String signatureSha256 = Hashing.sha256().hashBytes(signatures[0].toByteArray()).toString();
|
||||
assertThat(signatureSha256).isEqualTo(TELEPHONY_PACKAGE_SIGNATURE_SHA256);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user