Remember which apps have available restore info in the ancestral dataset

When we perform a full-system restore, remember the set of applications which
have data available in our ancestral dataset.  This is a key filter for not
having to do a round trip to the [remote] storage backend at app-install time
unless it is likely to be fruitful.

Change-Id: I7c77b490c560c581888d84f02f258b2e2d73bc69
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 24187a5..27055ed 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -76,6 +76,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Random;
+import java.util.Set;
 
 class BackupManagerService extends IBackupManager.Stub {
     private static final String TAG = "BackupManagerService";
@@ -226,7 +227,9 @@
     private File mEverStored;
     HashSet<String> mEverStoredApps = new HashSet<String>();
 
+    static final int CURRENT_ANCESTRAL_RECORD_VERSION = 1;  // increment when the schema changes
     File mTokenFile;
+    Set<String> mAncestralPackages = null;
     long mAncestralToken = 0;
     long mCurrentToken = 0;
 
@@ -500,8 +503,20 @@
         mTokenFile = new File(mBaseStateDir, "ancestral");
         try {
             RandomAccessFile tf = new RandomAccessFile(mTokenFile, "r");
-            mAncestralToken = tf.readLong();
-            mCurrentToken = tf.readLong();
+            int version = tf.readInt();
+            if (version == CURRENT_ANCESTRAL_RECORD_VERSION) {
+                mAncestralToken = tf.readLong();
+                mCurrentToken = tf.readLong();
+
+                int numPackages = tf.readInt();
+                if (numPackages >= 0) {
+                    mAncestralPackages = new HashSet<String>();
+                    for (int i = 0; i < numPackages; i++) {
+                        String pkgName = tf.readUTF();
+                        mAncestralPackages.add(pkgName);
+                    }
+                }
+            }
         } catch (IOException e) {
             Log.w(TAG, "Unable to read token file", e);
         }
@@ -972,12 +987,31 @@
         }
     }
 
-    // Record the current and ancestral backup tokens persistently
+    // Persistently record the current and ancestral backup tokens as well
+    // as the set of packages with data [supposedly] available in the
+    // ancestral dataset.
     void writeRestoreTokens() {
         try {
             RandomAccessFile af = new RandomAccessFile(mTokenFile, "rwd");
+
+            // First, the version number of this record, for futureproofing
+            af.writeInt(CURRENT_ANCESTRAL_RECORD_VERSION);
+
+            // Write the ancestral and current tokens
             af.writeLong(mAncestralToken);
             af.writeLong(mCurrentToken);
+
+            // Now write the set of ancestral packages
+            if (mAncestralPackages == null) {
+                af.writeInt(-1);
+            } else {
+                af.writeInt(mAncestralPackages.size());
+                if (DEBUG) Log.v(TAG, "Ancestral packages:  " + mAncestralPackages.size());
+                for (String pkgName : mAncestralPackages) {
+                    af.writeUTF(pkgName);
+                    if (DEBUG) Log.v(TAG, "   " + pkgName);
+                }
+            }
             af.close();
         } catch (IOException e) {
             Log.w(TAG, "Unable to write token file:", e);
@@ -1502,6 +1536,7 @@
              * the user is waiting, after all.
              */
 
+            PackageManagerBackupAgent pmAgent = null;
             int error = -1; // assume error
 
             // build the set of apps to restore
@@ -1562,7 +1597,7 @@
                 }
 
                 // Pull the Package Manager metadata from the restore set first
-                PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
+                pmAgent = new PackageManagerBackupAgent(
                         mPackageManager, agentPackages);
                 processOneRestore(omPackage, 0, IBackupAgent.Stub.asInterface(pmAgent.onBind()));
 
@@ -1705,8 +1740,10 @@
                 }
 
                 // If this was a restoreAll operation, record that this was our
-                // ancestral dataset
-                if (mTargetPackage == null) {
+                // ancestral dataset, as well as the set of apps that are possibly
+                // restoreable from the dataset
+                if (mTargetPackage == null && pmAgent != null) {
+                    mAncestralPackages = pmAgent.getRestoredPackages();
                     mAncestralToken = mToken;
                     writeRestoreTokens();
                 }
@@ -2436,6 +2473,12 @@
                 }
             }
 
+            pw.println("Ancestral packages: "
+                    + (mAncestralPackages == null ? "none" : mAncestralPackages.size()));
+            for (String pkg : mAncestralPackages) {
+                pw.println("    " + pkg);
+            }
+
             pw.println("Ever backed up: " + mEverStoredApps.size());
             for (String pkg : mEverStoredApps) {
                 pw.println("    " + pkg);
diff --git a/services/java/com/android/server/PackageManagerBackupAgent.java b/services/java/com/android/server/PackageManagerBackupAgent.java
index dbd1826..9551db5 100644
--- a/services/java/com/android/server/PackageManagerBackupAgent.java
+++ b/services/java/com/android/server/PackageManagerBackupAgent.java
@@ -40,6 +40,7 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 /**
  * We back up the signatures of each package so that during a system restore,
@@ -100,6 +101,20 @@
 
         return mRestoredSignatures.get(packageName);
     }
+
+    public Set<String> getRestoredPackages() {
+        if (mRestoredSignatures == null) {
+            Log.w(TAG, "getRestoredPackages() before metadata read!");
+            return null;
+        }
+
+        // This is technically the set of packages on the originating handset
+        // that had backup agents at all, not limited to the set of packages
+        // that had actually contributed a restore dataset, but it's a
+        // close enough approximation for our purposes and does not require any
+        // additional involvement by the transport to obtain.
+        return mRestoredSignatures.keySet();
+    }
     
     // The backed up data is the signature block for each app, keyed by
     // the package name.