Merge change 4831 into donut

* changes:
  Add app version to the backup metadata
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index fa88111..75e0e64 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -54,6 +54,7 @@
 import com.android.internal.backup.IBackupTransport;
 
 import com.android.server.PackageManagerBackupAgent;
+import com.android.server.PackageManagerBackupAgent.Metadata;
 
 import java.io.EOFException;
 import java.io.File;
@@ -111,9 +112,8 @@
     // Do we need to back up the package manager metadata on the next pass?
     private boolean mDoPackageManager;
     private static final String PACKAGE_MANAGER_SENTINEL = "@pm@";
-    // Backups that we have started.  These are separate to prevent starvation
-    // if an app keeps re-enqueuing itself.
-    private ArrayList<BackupRequest> mBackupQueue;
+
+    // locking around the pending-backup management
     private final Object mQueueLock = new Object();
 
     // The thread performing the sequence of queued backups binds to each app's agent
@@ -296,6 +296,7 @@
                 }
 
                 // snapshot the pending-backup set and work on that
+                ArrayList<BackupRequest> queue = new ArrayList<BackupRequest>();
                 File oldJournal = mJournal;
                 synchronized (mQueueLock) {
                     if (mPendingBackups.size() == 0) {
@@ -303,13 +304,11 @@
                         break;
                     }
 
-                    if (mBackupQueue == null) {
-                        mBackupQueue = new ArrayList<BackupRequest>();
-                        for (BackupRequest b: mPendingBackups.values()) {
-                            mBackupQueue.add(b);
-                        }
-                        mPendingBackups = new HashMap<ApplicationInfo,BackupRequest>();
+                    for (BackupRequest b: mPendingBackups.values()) {
+                        queue.add(b);
                     }
+                    Log.v(TAG, "clearing pending backups");
+                    mPendingBackups.clear();
 
                     // Start a new backup-queue journal file too
                     if (mJournalStream != null) {
@@ -328,7 +327,7 @@
                     // at next boot and the journaled requests fulfilled.
                 }
 
-                (new PerformBackupThread(transport, mBackupQueue, oldJournal)).start();
+                (new PerformBackupThread(transport, queue, oldJournal)).start();
                 break;
             }
 
@@ -759,6 +758,8 @@
     private boolean signaturesMatch(Signature[] storedSigs, Signature[] deviceSigs) {
         // Allow unsigned apps, but not signed on one device and unsigned on the other
         // !!! TODO: is this the right policy?
+        if (DEBUG) Log.v(TAG, "signaturesMatch(): stored=" + storedSigs
+                + " device=" + deviceSigs);
         if ((storedSigs == null || storedSigs.length == 0)
                 && (deviceSigs == null || deviceSigs.length == 0)) {
             return true;
@@ -800,6 +801,7 @@
 
         @Override
         public void run() {
+            if (DEBUG) Log.v(TAG, "Beginning restore process");
             /**
              * Restore sequence:
              *
@@ -847,13 +849,19 @@
                             PackageInfo app = isRestorable(pkg);
                             if (app != null) {
                                 // Validate against the backed-up signature block, too
-                                Signature[] storedSigs
-                                        = pmAgent.getRestoredSignatures(app.packageName);
-                                // !!! TODO: check app version here as well
-                                if (signaturesMatch(storedSigs, app.signatures)) {
-                                    appsToRestore.add(app);
+                                Metadata info = pmAgent.getRestoredMetadata(app.packageName);
+                                if (app.versionCode >= info.versionCode) {
+                                    if (DEBUG) Log.v(TAG, "Restore version " + info.versionCode
+                                            + " compatible with app version " + app.versionCode);
+                                    if (signaturesMatch(info.signatures, app.signatures)) {
+                                        appsToRestore.add(app);
+                                    } else {
+                                        Log.w(TAG, "Sig mismatch restoring " + app.packageName);
+                                    }
                                 } else {
-                                    Log.w(TAG, "Sig mismatch on restore of " + app.packageName);
+                                    Log.i(TAG, "Restore set for " + app.packageName
+                                            + " is too new [" + info.versionCode
+                                            + "] for installed app version " + app.versionCode);
                                 }
                             }
                         }
@@ -1202,11 +1210,10 @@
                     pw.println(app.toString());
                 }
             }
-            pw.println("Pending:");
-            Iterator<BackupRequest> br = mPendingBackups.values().iterator();
-            while (br.hasNext()) {
-                pw.print("    ");
-                pw.println(br);
+            pw.println("Pending: " + mPendingBackups.size());
+            for (BackupRequest req : mPendingBackups.values()) {
+                pw.print("   ");
+                pw.println(req);
             }
         }
     }
diff --git a/services/java/com/android/server/PackageManagerBackupAgent.java b/services/java/com/android/server/PackageManagerBackupAgent.java
index 6bd32a0..cc84430 100644
--- a/services/java/com/android/server/PackageManagerBackupAgent.java
+++ b/services/java/com/android/server/PackageManagerBackupAgent.java
@@ -40,9 +40,6 @@
 import java.util.HashSet;
 import java.util.List;
 
-// !!!TODO: take this out
-import java.util.zip.CRC32;
-
 /**
  * We back up the signatures of each package so that during a system restore,
  * we can verify that the app whose data we think we have matches the app
@@ -57,7 +54,17 @@
 
     private List<ApplicationInfo> mAllApps;
     private PackageManager mPackageManager;
-    private HashMap<String, Signature[]> mRestoredSignatures;
+    private HashMap<String, Metadata> mRestoredSignatures;
+
+    public class Metadata {
+        public int versionCode;
+        public Signature[] signatures;
+
+        Metadata(int version, Signature[] sigs) {
+            versionCode = version;
+            signatures = sigs;
+        }
+    }
 
     // We're constructed with the set of applications that are participating
     // in backup.  This set changes as apps are installed & removed.
@@ -67,7 +74,7 @@
         mRestoredSignatures = null;
     }
 
-    public Signature[] getRestoredSignatures(String packageName) {
+    public Metadata getRestoredMetadata(String packageName) {
         if (mRestoredSignatures == null) {
             return null;
         }
@@ -83,6 +90,9 @@
 
         // For each app we have on device, see if we've backed it up yet.  If not,
         // write its signature block to the output, keyed on the package name.
+        if (DEBUG) Log.v(TAG, "onBackup()");
+        ByteArrayOutputStream bufStream = new ByteArrayOutputStream();  // we'll reuse these
+        DataOutputStream outWriter = new DataOutputStream(bufStream);
         for (ApplicationInfo app : mAllApps) {
             String packName = app.packageName;
             if (!existing.contains(packName)) {
@@ -90,16 +100,27 @@
                 try {
                     PackageInfo info = mPackageManager.getPackageInfo(packName,
                             PackageManager.GET_SIGNATURES);
-                    // build a byte array out of the signature list
+                    /*
+                     * Metadata for each package:
+                     *
+                     * int version       -- [4] the package's versionCode
+                     * byte[] signatures -- [len] flattened Signature[] of the package
+                     */
+
+                    // marshall the version code in a canonical form
+                    bufStream.reset();
+                    outWriter.writeInt(info.versionCode);
+                    byte[] versionBuf = bufStream.toByteArray();
+
                     byte[] sigs = flattenSignatureArray(info.signatures);
-//                  !!! TODO: take out this debugging
+
+                    // !!! TODO: take out this debugging
                     if (DEBUG) {
-                        CRC32 crc = new CRC32();
-                        crc.update(sigs);
-                        Log.i(TAG, "+ flat sig array for " + packName + " : "
-                                + crc.getValue());
+                        Log.v(TAG, "+ metadata for " + packName + " version=" + info.versionCode);
                     }
-                    data.writeEntityHeader(packName, sigs.length);
+                    // Now we can write the backup entity for this package
+                    data.writeEntityHeader(packName, versionBuf.length + sigs.length);
+                    data.writeEntityData(versionBuf, versionBuf.length);
                     data.writeEntityData(sigs, sigs.length);
                 } catch (NameNotFoundException e) {
                     // Weird; we just found it, and now are told it doesn't exist.
@@ -113,6 +134,8 @@
             } else {
                 // We've already backed up this app.  Remove it from the set so
                 // we can tell at the end what has disappeared from the device.
+                // !!! TODO: take out the debugging message
+                if (DEBUG) Log.v(TAG, "= already backed up metadata for " + packName);
                 if (!existing.remove(packName)) {
                     Log.d(TAG, "*** failed to remove " + packName + " from package set!");
                 }
@@ -123,6 +146,8 @@
         // mentioned in the saved state file, but appear to no longer be present
         // on the device.  Write a deletion entity for them.
         for (String app : existing) {
+            // !!! TODO: take out this msg
+            if (DEBUG) Log.v(TAG, "- removing metadata for deleted pkg " + app);
             try {
                 data.writeEntityHeader(app, -1);
             } catch (IOException e) {
@@ -141,27 +166,29 @@
     public void onRestore(BackupDataInput data, ParcelFileDescriptor newState)
             throws IOException {
         List<ApplicationInfo> restoredApps = new ArrayList<ApplicationInfo>();
-        HashMap<String, Signature[]> sigMap = new HashMap<String, Signature[]>();
+        HashMap<String, Metadata> sigMap = new HashMap<String, Metadata>();
 
         while (data.readNextHeader()) {
             int dataSize = data.getDataSize();
             byte[] buf = new byte[dataSize];
             data.readEntityData(buf, 0, dataSize);
 
-            Signature[] sigs = unflattenSignatureArray(buf);
+            ByteArrayInputStream bufStream = new ByteArrayInputStream(buf);
+            DataInputStream in = new DataInputStream(bufStream);
+            int versionCode = in.readInt();
+
+            Signature[] sigs = unflattenSignatureArray(in);
             String pkg = data.getKey();
 //          !!! TODO: take out this debugging
             if (DEBUG) {
-                CRC32 crc = new CRC32();
-                crc.update(buf);
-                Log.i(TAG, "- unflat sig array for " + pkg + " : "
-                        + crc.getValue());
+                Log.i(TAG, "+ restored metadata for " + pkg
+                        + " versionCode=" + versionCode + " sigs=" + sigs);
             }
 
             ApplicationInfo app = new ApplicationInfo();
             app.packageName = pkg;
             restoredApps.add(app);
-            sigMap.put(pkg, sigs);
+            sigMap.put(pkg, new Metadata(versionCode, sigs));
         }
 
         mRestoredSignatures = sigMap;
@@ -193,9 +220,7 @@
         return outBuf.toByteArray();
     }
 
-    private Signature[] unflattenSignatureArray(byte[] buffer) {
-        ByteArrayInputStream inBufStream = new ByteArrayInputStream(buffer);
-        DataInputStream in = new DataInputStream(inBufStream);
+    private Signature[] unflattenSignatureArray(/*byte[] buffer*/ DataInputStream in) {
         Signature[] sigs = null;
 
         try {