Fix adb backup/restore

* Exclude key/value-only backup participants until we have a chance to
  augment the archive format with proper handling.

* Don't back up 'stopped' apps, which would un-stop them

* Fix unspecified-user bindService/startActivity invocations

* Teach adb restore about the onRestoreFinished() lifecycle method

* Implement proper app timeout handling in the adb data flows

* Backstop wallpaper backup against rare leftover-state issues

Bug 28056941

Change-Id: Ia59c71a2c74a632a2c2a527b9b7374229c440d46
This commit is contained in:
Christopher Tate
2016-06-17 13:24:02 -07:00
parent 0bf31c3fa6
commit 5cb5e89d77
2 changed files with 141 additions and 12 deletions

View File

@@ -92,6 +92,11 @@ public class WallpaperBackupAgent extends BackupAgent {
if (DEBUG) {
Slog.v(TAG, "Wallpaper is backup-eligible; linking & writing");
}
// In case of prior muddled state
infoStage.delete();
imageStage.delete();
Os.link(mWallpaperInfo.getCanonicalPath(), infoStage.getCanonicalPath());
fullBackupFile(infoStage, data);
Os.link(mWallpaperFile.getCanonicalPath(), imageStage.getCanonicalPath());

View File

@@ -725,6 +725,19 @@ public class BackupManagerService {
return true;
}
/* adb backup: is this app only capable of doing key/value? We say otherwise if
* the app has a backup agent and does not say fullBackupOnly, *unless* it
* is a package that we know _a priori_ explicitly supports both key/value and
* full-data backup.
*/
private static boolean appIsKeyValueOnly(PackageInfo pkg) {
if ("com.android.providers.settings".equals(pkg.packageName)) {
return false;
}
return !appGetsFullBackup(pkg);
}
// ----- Asynchronous backup/restore handler thread -----
private class BackupHandler extends Handler {
@@ -3446,8 +3459,8 @@ public class BackupManagerService {
Intent obbIntent = new Intent().setComponent(new ComponentName(
"com.android.sharedstoragebackup",
"com.android.sharedstoragebackup.ObbBackupService"));
BackupManagerService.this.mContext.bindService(
obbIntent, this, Context.BIND_AUTO_CREATE);
BackupManagerService.this.mContext.bindServiceAsUser(
obbIntent, this, Context.BIND_AUTO_CREATE, UserHandle.SYSTEM);
}
public void tearDown() {
@@ -3965,7 +3978,7 @@ public class BackupManagerService {
}
// Full backup task variant used for adb backup
class PerformAdbBackupTask extends FullBackupTask {
class PerformAdbBackupTask extends FullBackupTask implements BackupRestoreTask {
FullBackupEngine mBackupEngine;
final AtomicBoolean mLatch;
@@ -3979,6 +3992,7 @@ public class BackupManagerService {
boolean mIncludeSystem;
boolean mCompress;
ArrayList<String> mPackages;
PackageInfo mCurrentTarget;
String mCurrentPassword;
String mEncryptPassword;
@@ -4009,6 +4023,9 @@ public class BackupManagerService {
} else {
mEncryptPassword = encryptPassword;
}
if (MORE_DEBUG) {
Slog.w(TAG, "Encrypting backup with passphrase=" + mEncryptPassword);
}
mCompress = doCompress;
}
@@ -4165,7 +4182,9 @@ public class BackupManagerService {
Iterator<Entry<String, PackageInfo>> iter = packagesToBackup.entrySet().iterator();
while (iter.hasNext()) {
PackageInfo pkg = iter.next().getValue();
if (!appIsEligibleForBackup(pkg.applicationInfo)) {
if (!appIsEligibleForBackup(pkg.applicationInfo)
|| appIsStopped(pkg.applicationInfo)
|| appIsKeyValueOnly(pkg)) {
iter.remove();
}
}
@@ -4267,9 +4286,11 @@ public class BackupManagerService {
final boolean isSharedStorage =
pkg.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE);
mBackupEngine = new FullBackupEngine(out, null, pkg, mIncludeApks, null);
mBackupEngine = new FullBackupEngine(out, null, pkg, mIncludeApks, this);
sendOnBackupPackage(isSharedStorage ? "Shared storage" : pkg.packageName);
// Don't need to check preflight result as there is no preflight hook.
mCurrentTarget = pkg;
mBackupEngine.backupOnePackage();
// after the app's agent runs to handle its private filesystem
@@ -4308,6 +4329,28 @@ public class BackupManagerService {
mWakelock.release();
}
}
// BackupRestoreTask methods, used for timeout handling
@Override
public void execute() {
// Unused
}
@Override
public void operationComplete(long result) {
// Unused
}
@Override
public void handleTimeout() {
final PackageInfo target = mCurrentTarget;
if (DEBUG) {
Slog.w(TAG, "adb backup timeout of " + target);
}
if (target != null) {
tearDownAgentAndKill(mCurrentTarget.applicationInfo);
}
}
}
// Full backup task extension used for transport-oriented operation
@@ -5255,7 +5298,7 @@ public class BackupManagerService {
byte[] mWidgetData = null;
// Runner that can be placed in a separate thread to do in-process
// invocations of the full restore API asynchronously
// invocations of the full restore API asynchronously. Used by adb restore.
class RestoreFileRunnable implements Runnable {
IBackupAgent mAgent;
FileMetadata mInfo;
@@ -6404,6 +6447,46 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
// ***** end new engine class ***
// Used for synchronizing doRestoreFinished during adb restore
class AdbRestoreFinishedLatch implements BackupRestoreTask {
static final String TAG = "AdbRestoreFinishedLatch";
final CountDownLatch mLatch;
AdbRestoreFinishedLatch() {
mLatch = new CountDownLatch(1);
}
void await() {
boolean latched = false;
try {
latched = mLatch.await(TIMEOUT_FULL_BACKUP_INTERVAL, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
Slog.w(TAG, "Interrupted!");
}
}
@Override
public void execute() {
// Unused
}
@Override
public void operationComplete(long result) {
if (MORE_DEBUG) {
Slog.w(TAG, "adb onRestoreFinished() complete");
}
mLatch.countDown();
}
@Override
public void handleTimeout() {
if (DEBUG) {
Slog.w(TAG, "adb onRestoreFinished() timed out");
}
mLatch.countDown();
}
}
class PerformAdbRestoreTask implements Runnable {
ParcelFileDescriptor mInputFile;
String mCurrentPassword;
@@ -6419,6 +6502,27 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
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 {
final IBackupAgent mAgent;
final int mToken;
RestoreFinishedRunnable(IBackupAgent agent, int token) {
mAgent = agent;
mToken = token;
}
@Override
public void run() {
try {
mAgent.doRestoreFinished(mToken, mBackupManagerBinder);
} catch (RemoteException e) {
// never happens; this is used only for local binder calls
}
}
}
// possible handling states for a given package in the restore dataset
final HashMap<String, RestorePolicy> mPackagePolicies
= new HashMap<String, RestorePolicy>();
@@ -6560,7 +6664,7 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
Slog.e(TAG, "Unable to read restore input");
} finally {
tearDownPipes();
tearDownAgent(mTargetApp);
tearDownAgent(mTargetApp, true);
try {
if (rawDataIn != null) rawDataIn.close();
@@ -6714,7 +6818,7 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
if (DEBUG) Slog.d(TAG, "Saw new package; finalizing old one");
// Now we're really done
tearDownPipes();
tearDownAgent(mTargetApp);
tearDownAgent(mTargetApp, true);
mTargetApp = null;
mAgentPackage = null;
}
@@ -6936,10 +7040,12 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
// okay, if the remote end failed at any point, deal with
// it by ignoring the rest of the restore on it
if (!agentSuccess) {
if (DEBUG) {
Slog.d(TAG, "Agent failure restoring " + pkg + "; now ignoring");
}
mBackupHandler.removeMessages(MSG_TIMEOUT);
tearDownPipes();
tearDownAgent(mTargetApp);
mAgent = null;
tearDownAgent(mTargetApp, false);
mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
}
}
@@ -6988,9 +7094,27 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
}
}
void tearDownAgent(ApplicationInfo app) {
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 = generateToken();
final AdbRestoreFinishedLatch latch = new AdbRestoreFinishedLatch();
prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, latch);
if (mTargetApp.processName.equals("system")) {
if (MORE_DEBUG) {
Slog.d(TAG, "system agent - restoreFinished on thread");
}
Runnable runner = new RestoreFinishedRunnable(mAgent, token);
new Thread(runner, "restore-sys-finished-runner").start();
} else {
mAgent.doRestoreFinished(token, mBackupManagerBinder);
}
latch.await();
}
// unbind and tidy up even on timeout or failure, just in case
mActivityManager.unbindBackupAgent(app);
@@ -9354,7 +9478,7 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
"com.android.backupconfirm.BackupRestoreConfirmation");
confIntent.putExtra(FullBackup.CONF_TOKEN_INTENT_EXTRA, token);
confIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(confIntent);
mContext.startActivityAsUser(confIntent, UserHandle.SYSTEM);
} catch (ActivityNotFoundException e) {
return false;
}