summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristopher Tate <ctate@google.com>2016-06-17 13:24:02 -0700
committerChristopher Tate <ctate@google.com>2016-06-22 13:53:41 -0700
commit5cb5e89d770f474031e4ab30fb7f24c66333590a (patch)
tree187e55259a6e10550a0e0faf29048c56acd730bc
parent0bf31c3fa6263f595022d8f3addfd4d28f70db99 (diff)
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
-rw-r--r--packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java5
-rw-r--r--services/backup/java/com/android/server/backup/BackupManagerService.java148
2 files changed, 141 insertions, 12 deletions
diff --git a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
index a3ea592ebe5d..47d1493e1574 100644
--- a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
+++ b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
@@ -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());
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index eea771d26a0e..e6f99c15ab66 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -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;
}