Use signatures on restore

On restore now, the backup manager gets the signature blocks corresponding to
the restore set from the transport.  It then validates those signatures against
the on-device app signatures, and refuses to restore data to an app whose
on-device sig block does not match the backup image's.

Also actually implement 'bmgr transport N' so that we can select the local
transport easily during runtime.
diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
index 3af80fa..841e3df 100644
--- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
+++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
@@ -80,6 +80,14 @@
             doRestore();
             return;
         }
+
+        if ("transport".equals(op)) {
+            doTransport();
+            return;
+        }
+
+        System.err.println("Unknown command");
+        showUsage();
     }
 
     private void doRun() {
@@ -113,6 +121,19 @@
         }
     }
 
+    private void doTransport() {
+        try {
+            int which = Integer.parseInt(nextArg());
+            int old = mBmgr.selectBackupTransport(which);
+            System.out.println("Selected transport " + which + " (formerly " + old + ")");
+        } catch (NumberFormatException e) {
+            showUsage();
+        } catch (RemoteException e) {
+            System.err.println(e.toString());
+            System.err.println(BMGR_NOT_RUNNING_ERR);
+        }
+    }
+
     private void doList() {
         String arg = nextArg();     // sets, transports, packages set#
         if ("transports".equals(arg)) {
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index f9a6f5b..6ee8260 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -31,6 +31,7 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.PackageManager;
+import android.content.pm.Signature;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
@@ -153,7 +154,9 @@
         mJournalDir.mkdirs();
         makeJournalLocked();    // okay because no other threads are running yet
 
-        // Build our mapping of uid to backup client services
+        // Build our mapping of uid to backup client services.  This implicitly
+        // schedules a backup pass on the Package Manager metadata the first
+        // time anything needs to be backed up.
         synchronized (mBackupParticipants) {
             addPackageParticipantsLocked(null);
         }
@@ -656,8 +659,15 @@
                 // Look up the package info & signatures.  This is first so that if it
                 // throws an exception, there's no file setup yet that would need to
                 // be unraveled.
-                PackageInfo packInfo = mPackageManager.getPackageInfo(packageName,
+                PackageInfo packInfo;
+                if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) {
+                    // The metadata 'package' is synthetic
+                    packInfo = new PackageInfo();
+                    packInfo.packageName = packageName;
+                } else {
+                    packInfo = mPackageManager.getPackageInfo(packageName,
                         PackageManager.GET_SIGNATURES);
+                }
 
                 // !!! TODO: get the state file dir from the transport
                 File savedStateName = new File(mStateDir, packageName);
@@ -745,6 +755,28 @@
         return null;
     }
 
+    private boolean signaturesMatch(Signature[] storedSigs, Signature[] deviceSigs) {
+        // !!! TODO: this demands that every stored signature match one
+        // that is present on device, and does not demand the converse.
+        // Is this this right policy?
+        int nStored = storedSigs.length;
+        int nDevice = deviceSigs.length;
+
+        for (int i=0; i < nStored; i++) {
+            boolean match = false;
+            for (int j=0; j < nDevice; j++) {
+                if (storedSigs[i].equals(deviceSigs[j])) {
+                    match = true;
+                    break;
+                }
+            }
+            if (!match) {
+                return false;
+            }
+        }
+        return true;
+    }
+
     class PerformRestoreThread extends Thread {
         private IBackupTransport mTransport;
         private int mToken;
@@ -791,6 +823,13 @@
                         // !!! TODO: pick out the set for this token
                         mImage = images[0];
 
+                        // Pull the Package Manager metadata from the restore set first
+                        PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
+                                mPackageManager, allAgentApps());
+                        PackageInfo pmApp = new PackageInfo();
+                        pmApp.packageName = PACKAGE_MANAGER_SENTINEL;
+                        processOneRestore(pmApp, IBackupAgent.Stub.asInterface(pmAgent.onBind()));
+
                         // build the set of apps we will attempt to restore
                         PackageInfo[] packages = mTransport.getAppSet(mImage.token);
                         HashSet<PackageInfo> appsToRestore = new HashSet<PackageInfo>();
@@ -798,7 +837,20 @@
                             // get the real PackageManager idea of the package
                             PackageInfo app = isRestorable(pkg);
                             if (app != null) {
-                                appsToRestore.add(app);
+                                // Validate against the backed-up signature block, too
+                                Signature[] storedSigs
+                                        = pmAgent.getRestoredSignatures(app.packageName);
+                                if (storedSigs != null) {
+                                    // !!! TODO: check app version here as well
+                                    if (signaturesMatch(storedSigs, app.signatures)) {
+                                        appsToRestore.add(app);
+                                    } else {
+                                        Log.w(TAG, "Sig mismatch on restore of " + app.packageName);
+                                    }
+                                } else {
+                                    Log.i(TAG, "No stored sigs for " + app.packageName
+                                            + " so not restoring");
+                                }
                             }
                         }
 
@@ -1002,6 +1054,7 @@
     // Report the currently active transport
     public int getCurrentTransport() {
         mContext.enforceCallingPermission("android.permission.BACKUP", "selectBackupTransport");
+        Log.v(TAG, "getCurrentTransport() returning " + mTransportId);
         return mTransportId;
     }
 
@@ -1011,6 +1064,7 @@
 
         int prevTransport = mTransportId;
         mTransportId = transportId;
+        Log.v(TAG, "selectBackupTransport() set " + mTransportId + " returning " + prevTransport);
         return prevTransport;
     }