Existing instances don't know that the file has changed out from
under them, so they continue to return stale values from reads, and
risk overwriting restored data with stale content if writes are
performed. We now tell the backing cache system to induce a
reload after restore (i.e. after we might have written a relevant
file out from under it).
Along the way we shook out an irregularity in the way we were
setting up the context topology of non-lifecycle instances of
the metadata-handling BackupAgent subclass, so that's fixed
now too.
Bug
12061817
Test: cts-tradefed run cts -m CtsBackupHostTestCases
Change-Id: I401fe9297235b55d8a8f041e430d122dc6e24129
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteOrder;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteOrder;
+import java.util.ArrayList;
import java.util.Objects;
class ReceiverRestrictedContext extends ContextWrapper {
import java.util.Objects;
class ReceiverRestrictedContext extends ContextWrapper {
+ @Override
+ public void reloadSharedPreferences() {
+ // Build the list of all per-context impls (i.e. caches) we know about
+ ArrayList<SharedPreferencesImpl> spImpls = new ArrayList<>();
+ synchronized (ContextImpl.class) {
+ final ArrayMap<File, SharedPreferencesImpl> cache = getSharedPreferencesCacheLocked();
+ for (int i = 0; i < cache.size(); i++) {
+ final SharedPreferencesImpl sp = cache.valueAt(i);
+ if (sp != null) {
+ spImpls.add(sp);
+ }
+ }
+ }
+
+ // Issue the reload outside the cache lock
+ for (int i = 0; i < spImpls.size(); i++) {
+ spImpls.get(i).startReloadIfChangedUnexpectedly();
+ }
+ }
+
/**
* Try our best to migrate all files from source to target that match
* requested prefix.
/**
* Try our best to migrate all files from source to target that match
* requested prefix.
long ident = Binder.clearCallingIdentity();
if (DEBUG) Log.v(TAG, "doRestore() invoked");
long ident = Binder.clearCallingIdentity();
if (DEBUG) Log.v(TAG, "doRestore() invoked");
+
+ // Ensure that any side-effect SharedPreferences writes have landed *before*
+ // we may be about to rewrite the file out from underneath
+ waitForSharedPrefs();
+
BackupDataInput input = new BackupDataInput(data.getFileDescriptor());
try {
BackupAgent.this.onRestore(input, appVersionCode, newState);
BackupDataInput input = new BackupDataInput(data.getFileDescriptor());
try {
BackupAgent.this.onRestore(input, appVersionCode, newState);
Log.d(TAG, "onRestore (" + BackupAgent.this.getClass().getName() + ") threw", ex);
throw ex;
} finally {
Log.d(TAG, "onRestore (" + BackupAgent.this.getClass().getName() + ") threw", ex);
throw ex;
} finally {
- // Ensure that any side-effect SharedPreferences writes have landed
- waitForSharedPrefs();
+ // And bring live SharedPreferences instances up to date
+ reloadSharedPreferences();
Binder.restoreCallingIdentity(ident);
try {
Binder.restoreCallingIdentity(ident);
try {
} finally {
// Ensure that any side-effect SharedPreferences writes have landed
waitForSharedPrefs();
} finally {
// Ensure that any side-effect SharedPreferences writes have landed
waitForSharedPrefs();
+ // And bring live SharedPreferences instances up to date
+ reloadSharedPreferences();
Binder.restoreCallingIdentity(ident);
try {
Binder.restoreCallingIdentity(ident);
try {
*/
public abstract boolean deleteSharedPreferences(String name);
*/
public abstract boolean deleteSharedPreferences(String name);
+ /** @hide */
+ public abstract void reloadSharedPreferences();
+
/**
* Open a private file associated with this Context's application package
* for reading.
/**
* Open a private file associated with this Context's application package
* for reading.
import android.annotation.SystemApi;
import android.app.IApplicationThread;
import android.app.IServiceConnection;
import android.annotation.SystemApi;
import android.app.IApplicationThread;
import android.app.IServiceConnection;
-import android.app.Notification;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
return mBase.getSharedPreferences(file, mode);
}
return mBase.getSharedPreferences(file, mode);
}
+ /** @hide */
+ @Override
+ public void reloadSharedPreferences() {
+ mBase.reloadSharedPreferences();
+ }
+
@Override
public boolean moveSharedPreferencesFrom(Context sourceContext, String name) {
return mBase.moveSharedPreferencesFrom(sourceContext, name);
@Override
public boolean moveSharedPreferencesFrom(Context sourceContext, String name) {
return mBase.moveSharedPreferencesFrom(sourceContext, name);
return !appGetsFullBackup(pkg);
}
return !appGetsFullBackup(pkg);
}
+ /*
+ * Construct a backup agent instance for the metadata pseudopackage. This is a
+ * process-local non-lifecycle agent instance, so we manually set up the context
+ * topology for it.
+ */
+ PackageManagerBackupAgent makeMetadataAgent() {
+ PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(mPackageManager);
+ pmAgent.attach(mContext);
+ pmAgent.onCreate();
+ return pmAgent;
+ }
+
+ /*
+ * Same as above but with the explicit package-set configuration.
+ */
+ PackageManagerBackupAgent makeMetadataAgent(List<PackageInfo> packages) {
+ PackageManagerBackupAgent pmAgent =
+ new PackageManagerBackupAgent(mPackageManager, packages);
+ pmAgent.attach(mContext);
+ pmAgent.onCreate();
+ return pmAgent;
+ }
+
// ----- Asynchronous backup/restore handler thread -----
private class BackupHandler extends Handler {
// ----- Asynchronous backup/restore handler thread -----
private class BackupHandler extends Handler {
// because it's cheap and this way we guarantee that we don't get out of
// step even if we're selecting among various transports at run time.
if (mStatus == BackupTransport.TRANSPORT_OK) {
// because it's cheap and this way we guarantee that we don't get out of
// step even if we're selecting among various transports at run time.
if (mStatus == BackupTransport.TRANSPORT_OK) {
- PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
- mPackageManager);
+ PackageManagerBackupAgent pmAgent = makeMetadataAgent();
mStatus = invokeAgentForBackup(PACKAGE_MANAGER_SENTINEL,
IBackupAgent.Stub.asInterface(pmAgent.onBind()), mTransport);
addBackupTrace("PMBA invoke: " + mStatus);
mStatus = invokeAgentForBackup(PACKAGE_MANAGER_SENTINEL,
IBackupAgent.Stub.asInterface(pmAgent.onBind()), mTransport);
addBackupTrace("PMBA invoke: " + mStatus);
mObserver = observer;
mLatchObject = latch;
mAgent = null;
mObserver = observer;
mLatchObject = latch;
mAgent = null;
- mPackageManagerBackupAgent = new PackageManagerBackupAgent(mPackageManager);
+ mPackageManagerBackupAgent = makeMetadataAgent();
mAgentPackage = null;
mTargetApp = null;
mObbConnection = new FullBackupObbConnection();
mAgentPackage = null;
mTargetApp = null;
mObbConnection = new FullBackupObbConnection();
// Pull the Package Manager metadata from the restore set first
mCurrentPackage = new PackageInfo();
mCurrentPackage.packageName = PACKAGE_MANAGER_SENTINEL;
// Pull the Package Manager metadata from the restore set first
mCurrentPackage = new PackageInfo();
mCurrentPackage.packageName = PACKAGE_MANAGER_SENTINEL;
- mPmAgent = new PackageManagerBackupAgent(mPackageManager, null);
+ mPmAgent = makeMetadataAgent(null);
mAgent = IBackupAgent.Stub.asInterface(mPmAgent.onBind());
if (MORE_DEBUG) {
Slog.v(TAG, "initiating restore for PMBA");
mAgent = IBackupAgent.Stub.asInterface(mPmAgent.onBind());
if (MORE_DEBUG) {
Slog.v(TAG, "initiating restore for PMBA");
throw new UnsupportedOperationException();
}
throw new UnsupportedOperationException();
}
+ /** @hide */
+ @Override
+ public void reloadSharedPreferences() {
+ throw new UnsupportedOperationException();
+ }
+
@Override
public boolean moveSharedPreferencesFrom(Context sourceContext, String name) {
throw new UnsupportedOperationException();
@Override
public boolean moveSharedPreferencesFrom(Context sourceContext, String name) {
throw new UnsupportedOperationException();