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
diff --git a/api/current.xml b/api/current.xml
index 3af1426..031506b 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -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>
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 2e405c1..8773f59 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -248,6 +248,19 @@
     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},
      * {@link #FLAG_PERSISTENT}, {@link #FLAG_FACTORY_TEST}, and
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 5823560..98aacaa 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1369,8 +1369,8 @@
         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 @@
                         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;
+                }
             }
         }
         
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index b3adf49..d66f513 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -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>
     
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index b334337..5da8e85 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -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" />
     
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index f79a02a..d4b28e2 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -1647,11 +1647,22 @@
                     }
 
                     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 @@
                         // 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);