Add 'restoreAnyVersion' attr for apps that want to restore "future" data

If a backup-participating app sets android:restoreAnyVersion="true" in its
manifest <application> tag, then its agent will be invoked for restore
even if the available dataset was created by a later version of the app
than is currently installed on the device.  This will not typically be
a problem for third party applications, since for them the installation
and initial data restore are tightly coupled, but it can cause serious
problems for applications which are both preinstalled on the system
partition and overridden by later updates.  The primary difficulty
that this new attribute addresses is this:

1. User buys a Nexus One, Market self-updates, and the user installs some apps.
At this point the backup data on the server may indicate that the version of
Market which originated its bookkeeping is newer than the stock N1 Market app.
2. User loses their phone, and buys a replacement N1.  At setup time, Market
has not yet had a chance to self-update, so when the restore comes in, it's
tagged as being from "the future" and so the restore is refused.  No apps get
reinstalled.

Bug: 2442127
Change-Id: I076a9553dc613e5c3189350e778315718ed1ed2b
This commit is contained in:
Christopher Tate
2010-02-24 16:06:18 -08:00
parent 27eb24148a
commit 3dda518a4f
6 changed files with 70 additions and 13 deletions

View File

@@ -6785,6 +6785,17 @@
visibility="public"
>
</field>
<field name="restoreAnyVersion"
type="int"
transient="false"
volatile="false"
value="16843451"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
<field name="restoreNeedsApplication"
type="int"
transient="false"
@@ -14630,7 +14641,7 @@
</parameter>
<parameter name="features" type="java.lang.String[]">
</parameter>
<parameter name="activityForPrompting" type="android.app.Activity">
<parameter name="activity" type="android.app.Activity">
</parameter>
<parameter name="addAccountOptions" type="android.os.Bundle">
</parameter>

View File

@@ -247,6 +247,19 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
*/
public static final int FLAG_NATIVE_DEBUGGABLE = 1<<21;
/**
* Value for {@link #flags}: Set to true if the application's backup
* agent claims to be able to handle restore data even "from the future,"
* i.e. from versions of the application with a versionCode greater than
* the one currently installed on the device.
*
* <p>If android:allowBackup is set to false or no android:backupAgent
* is specified, this flag will be ignored.
*
* {@hide}
*/
public static final int FLAG_RESTORE_ANY_VERSION = 1<<22;
/**
* Flags associated with the application. Any combination of
* {@link #FLAG_SYSTEM}, {@link #FLAG_DEBUGGABLE}, {@link #FLAG_HAS_CODE},

View File

@@ -1369,8 +1369,8 @@ public class PackageParser {
if (allowBackup) {
ai.flags |= ApplicationInfo.FLAG_ALLOW_BACKUP;
// backupAgent, killAfterRestore, and restoreNeedsApplication are only relevant
// if backup is possible for the given application.
// backupAgent, killAfterRestore, restoreNeedsApplication, and restoreAnyVersion
// are only relevant if backup is possible for the given application.
String backupAgent = sa.getNonResourceString(
com.android.internal.R.styleable.AndroidManifestApplication_backupAgent);
if (backupAgent != null) {
@@ -1390,6 +1390,11 @@ public class PackageParser {
false)) {
ai.flags |= ApplicationInfo.FLAG_RESTORE_NEEDS_APPLICATION;
}
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_restoreAnyVersion,
false)) {
ai.flags |= ApplicationInfo.FLAG_RESTORE_ANY_VERSION;
}
}
}

View File

@@ -593,12 +593,15 @@
<attr name="backupAgent" format="string" />
<!-- Whether to allow the application to participate in backup
infrastructure.
STOPSHIP: more explanation -->
infrastructure. If this attribute is set to <code>false</code>, no backup
of the application will ever be performed, even by a full-system backup that
would otherwise cause all application data to be saved via adb. The
default value of this attribute is <code>true</code>. -->
<attr name="allowBackup" format="boolean" />
<!-- Whether the application in question should be terminated after its
settings have been restored. The default is to do so. -->
settings have been restored. The default is <code>true</code>,
which means to do so. -->
<attr name="killAfterRestore" format="boolean" />
<!-- Whether the application needs to have its own Application subclass
@@ -606,6 +609,16 @@
Application class to avoid interference with application logic. -->
<attr name="restoreNeedsApplication" format="boolean" />
<!-- Indicate that the application is prepared to attempt a restore of any
backed-up dataset, even if the backup is apparently from a newer version
of the application than is currently installed on the device. Setting
this attribute to <code>true</code> will permit the Backup Manager to
attempt restore even when a version mismatch suggests that the data are
incompatible. <em>Use with caution!</em>
<p>The default value of this attribute is <code>false</code>. -->
<attr name="restoreAnyVersion" format="boolean" />
<!-- The default install location defined by an application. -->
<attr name="installLocation">
<!-- Let the system decide ideal install location -->
@@ -700,6 +713,7 @@
<attr name="allowBackup" />
<attr name="killAfterRestore" />
<attr name="restoreNeedsApplication" />
<attr name="restoreAnyVersion" />
<attr name="neverEncrypt" />
</declare-styleable>

View File

@@ -1231,6 +1231,7 @@
<public type="attr" name="safeMode" id="0x010102b8" />
<public type="attr" name="webTextViewStyle" id="0x010102b9" />
<public type="attr" name="overscrollMode" id="0x010102ba" />
<public type="attr" name="restoreAnyVersion" id="0x010102bb" />
<public type="anim" name="cycle_interpolator" id="0x010a000c" />

View File

@@ -1647,11 +1647,22 @@ class BackupManagerService extends IBackupManager.Stub {
}
if (metaInfo.versionCode > packageInfo.versionCode) {
String message = "Version " + metaInfo.versionCode
+ " > installed version " + packageInfo.versionCode;
Log.w(TAG, "Package " + packageName + ": " + message);
EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, message);
continue;
// Data is from a "newer" version of the app than we have currently
// installed. If the app has not declared that it is prepared to
// handle this case, we do not attempt the restore.
if ((packageInfo.applicationInfo.flags
& ApplicationInfo.FLAG_RESTORE_ANY_VERSION) == 0) {
String message = "Version " + metaInfo.versionCode
+ " > installed version " + packageInfo.versionCode;
Log.w(TAG, "Package " + packageName + ": " + message);
EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
packageName, message);
continue;
} else {
if (DEBUG) Log.v(TAG, "Version " + metaInfo.versionCode
+ " > installed " + packageInfo.versionCode
+ " but restoreAnyVersion");
}
}
if (!signaturesMatch(metaInfo.signatures, packageInfo)) {
@@ -1695,8 +1706,10 @@ class BackupManagerService extends IBackupManager.Stub {
// The agent was probably running with a stub Application object,
// which isn't a valid run mode for the main app logic. Shut
// down the app so that next time it's launched, it gets the
// usual full initialization.
if ((packageInfo.applicationInfo.flags
// usual full initialization. Note that this is only done for
// full-system restores: when a single app has requested a restore,
// it is explicitly not killed following that operation.
if (mTargetPackage == null && (packageInfo.applicationInfo.flags
& ApplicationInfo.FLAG_KILL_AFTER_RESTORE) != 0) {
if (DEBUG) Log.d(TAG, "Restore complete, killing host process of "
+ packageInfo.applicationInfo.processName);