summaryrefslogtreecommitdiffstats
path: root/core/java/android
diff options
context:
space:
mode:
authorChristopher Tate <ctate@google.com>2013-04-18 16:57:43 -0700
committerChristopher Tate <ctate@google.com>2013-04-19 14:30:11 -0700
commitf85f5b2125461ea664cf67a16d4608a5a9bf2f98 (patch)
treef72c3c5328469b79ff9fb18d91ba0ca9a259928f /core/java/android
parent7f6fc12997d67ae80a044bc0b4cc17797d887911 (diff)
downloadframeworks_base-f85f5b2125461ea664cf67a16d4608a5a9bf2f98.zip
frameworks_base-f85f5b2125461ea664cf67a16d4608a5a9bf2f98.tar.gz
frameworks_base-f85f5b2125461ea664cf67a16d4608a5a9bf2f98.tar.bz2
Provide SharedPreferences coherence guarantees for BackupAgent
SharedPreferences uses deferred writes internally, and the public API doesn't allow apps to explicitly synchronize with this, so the backup/restore implementation needs to take a little care to make sure that the app process isn't killed before the deferred writes land on disk. This parallels the coherence guarantees around SharedPreference that the Activity and Service lifecycles provide. Bug 8659368 Change-Id: I853e54f9fb0d2d260dbe6e40d640959f998092df
Diffstat (limited to 'core/java/android')
-rw-r--r--core/java/android/app/backup/BackupAgent.java48
1 files changed, 48 insertions, 0 deletions
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index 37fddcb..67c772b 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -17,12 +17,15 @@
package android.app.backup;
import android.app.IBackupAgent;
+import android.app.QueuedWork;
import android.app.backup.IBackupManager;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.pm.ApplicationInfo;
import android.os.Binder;
+import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
@@ -33,6 +36,7 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashSet;
import java.util.LinkedList;
+import java.util.concurrent.CountDownLatch;
import libcore.io.ErrnoException;
import libcore.io.Libcore;
@@ -122,6 +126,32 @@ public abstract class BackupAgent extends ContextWrapper {
/** @hide */
public static final int TYPE_SYMLINK = 3;
+ Handler mHandler = null;
+
+ class SharedPrefsSynchronizer implements Runnable {
+ public final CountDownLatch mLatch = new CountDownLatch(1);
+
+ @Override
+ public void run() {
+ QueuedWork.waitToFinish();
+ mLatch.countDown();
+ }
+ };
+
+ // Syncing shared preferences deferred writes needs to happen on the main looper thread
+ private void waitForSharedPrefs() {
+ if (mHandler == null) {
+ mHandler = new Handler(Looper.getMainLooper());
+ }
+
+ final SharedPrefsSynchronizer s = new SharedPrefsSynchronizer();
+ mHandler.postAtFrontOfQueue(s);
+ try {
+ s.mLatch.await();
+ } catch (InterruptedException e) { /* ignored */ }
+ }
+
+
public BackupAgent() {
super(null);
}
@@ -542,6 +572,11 @@ public abstract class BackupAgent extends ContextWrapper {
Log.d(TAG, "onBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex);
throw ex;
} finally {
+ // Ensure that any SharedPreferences writes have landed after the backup,
+ // in case the app code has side effects (since apps cannot provide this
+ // guarantee themselves).
+ waitForSharedPrefs();
+
Binder.restoreCallingIdentity(ident);
try {
callbackBinder.opComplete(token);
@@ -569,6 +604,9 @@ public abstract class BackupAgent extends ContextWrapper {
Log.d(TAG, "onRestore (" + BackupAgent.this.getClass().getName() + ") threw", ex);
throw ex;
} finally {
+ // Ensure that any side-effect SharedPreferences writes have landed
+ waitForSharedPrefs();
+
Binder.restoreCallingIdentity(ident);
try {
callbackBinder.opComplete(token);
@@ -586,6 +624,10 @@ public abstract class BackupAgent extends ContextWrapper {
if (DEBUG) Log.v(TAG, "doFullBackup() invoked");
+ // Ensure that any SharedPreferences writes have landed *before*
+ // we potentially try to back up the underlying files directly.
+ waitForSharedPrefs();
+
try {
BackupAgent.this.onFullBackup(new FullBackupDataOutput(data));
} catch (IOException ex) {
@@ -595,6 +637,9 @@ public abstract class BackupAgent extends ContextWrapper {
Log.d(TAG, "onBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex);
throw ex;
} finally {
+ // ... and then again after, as in the doBackup() case
+ waitForSharedPrefs();
+
// Send the EOD marker indicating that there is no more data
// forthcoming from this agent.
try {
@@ -624,6 +669,9 @@ public abstract class BackupAgent extends ContextWrapper {
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
+ // Ensure that any side-effect SharedPreferences writes have landed
+ waitForSharedPrefs();
+
Binder.restoreCallingIdentity(ident);
try {
callbackBinder.opComplete(token);