support TARGET_EXTRA_RECOVERY_KEYS

Specifying one or more key files (without .x509.pem extension) as
TARGET_EXTRA_RECOVERY_KEYS causes them to be included as acceptable
keys for recovery packages.  They are *not* included in otacerts.zip,
so actual downloaded over-the-air packages can't use them, but they
can be used to sign sideload-only packages.

Bug: 3413359
Change-Id: I6f248ffa35f0c6b125dd8a7517493017e236c776
diff --git a/core/Makefile b/core/Makefile
index e267fe0..c5a0dee 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -656,11 +656,12 @@
 	$(call intermediates-dir-for,PACKAGING,ota_keys)/keys
 DUMPKEY_JAR := $(HOST_OUT_JAVA_LIBRARIES)/dumpkey.jar
 $(RECOVERY_INSTALL_OTA_KEYS): PRIVATE_OTA_PUBLIC_KEYS := $(OTA_PUBLIC_KEYS)
-$(RECOVERY_INSTALL_OTA_KEYS): $(OTA_PUBLIC_KEYS) $(DUMPKEY_JAR)
-	@echo "DumpPublicKey: $@ <= $(PRIVATE_OTA_PUBLIC_KEYS)"
+$(RECOVERY_INSTALL_OTA_KEYS): extra_keys := $(patsubst %,%.x509.pem,$(TARGET_EXTRA_RECOVERY_KEYS))
+$(RECOVERY_INSTALL_OTA_KEYS): $(OTA_PUBLIC_KEYS) $(DUMPKEY_JAR) $(extra_keys)
+	@echo "DumpPublicKey: $@ <= $(PRIVATE_OTA_PUBLIC_KEYS) $(extra_keys)"
 	@rm -rf $@
 	@mkdir -p $(dir $@)
-	java -jar $(DUMPKEY_JAR) $(PRIVATE_OTA_PUBLIC_KEYS) > $@
+	java -jar $(DUMPKEY_JAR) $(PRIVATE_OTA_PUBLIC_KEYS) $(extra_keys) > $@
 
 $(INSTALLED_RECOVERYIMAGE_TARGET): $(MKBOOTFS) $(MKBOOTIMG) $(MINIGZIP) \
 		$(INSTALLED_RAMDISK_TARGET) \
@@ -1069,6 +1070,9 @@
 ifdef INTERNAL_USERIMAGES_SPARSE_EXT_FLAG
 	$(hide) echo "extfs_sparse_flag=$(INTERNAL_USERIMAGES_SPARSE_EXT_FLAG)" >> $(zip_root)/META/misc_info.txt
 endif
+ifdef TARGET_EXTRA_RECOVERY_KEYS
+	$(hide) echo "extra_recovery_keys=$(TARGET_EXTRA_RECOVERY_KEYS)" >> $(zip_root)/META/misc_info.txt
+endif
 	@# Zip everything up, preserving symlinks
 	$(hide) (cd $(zip_root) && zip -qry ../$(notdir $@) .)
 	@# Run fs_config on all the system files in the zip, and save the output
diff --git a/tools/releasetools/sign_target_files_apks b/tools/releasetools/sign_target_files_apks
index 5fca691..5353063 100755
--- a/tools/releasetools/sign_target_files_apks
+++ b/tools/releasetools/sign_target_files_apks
@@ -204,6 +204,17 @@
   except KeyError:
     raise ExternalError("can't read META/otakeys.txt from input")
 
+  misc_info = common.LoadInfoDict(input_tf_zip)
+
+  extra_recovery_keys = misc_info.get("extra_recovery_keys", None)
+  if extra_recovery_keys:
+    extra_recovery_keys = [OPTIONS.key_map.get(k, k) + ".x509.pem"
+                           for k in extra_recovery_keys.split()]
+    if extra_recovery_keys:
+      print "extra recovery-only key(s): " + ", ".join(extra_recovery_keys)
+  else:
+    extra_recovery_keys = []
+
   mapped_keys = []
   for k in keylist:
     m = re.match(r"^(.*)\.x509\.pem$", k)
@@ -217,15 +228,18 @@
     print "for OTA package verification"
   else:
     mapped_keys.append(
-        OPTIONS.key_map["build/target/product/security/testkey"] + ".x509.pem")
+        OPTIONS.key_map.get("build/target/product/security/testkey",
+                            "build/target/product/security/testkey")
+        + ".x509.pem")
     print "META/otakeys.txt has no keys; using", mapped_keys[0]
 
   # recovery uses a version of the key that has been slightly
   # predigested (by DumpPublicKey.java) and put in res/keys.
+  # extra_recovery_keys are used only in recovery.
 
   p = common.Run(["java", "-jar",
                   os.path.join(OPTIONS.search_path, "framework", "dumpkey.jar")]
-                 + mapped_keys,
+                 + mapped_keys + extra_recovery_keys,
                  stdout=subprocess.PIPE)
   data, _ = p.communicate()
   if p.returncode != 0:
@@ -234,6 +248,7 @@
 
   # SystemUpdateActivity uses the x509.pem version of the keys, but
   # put into a zipfile system/etc/security/otacerts.zip.
+  # We DO NOT include the extra_recovery_keys (if any) here.
 
   tempfile = cStringIO.StringIO()
   certs_zip = zipfile.ZipFile(tempfile, "w")