Merge "Fix path escaping for aidl generated java"
diff --git a/tools/releasetools/ota_from_target_files.py b/tools/releasetools/ota_from_target_files.py
index 777ddbe..6966964 100755
--- a/tools/releasetools/ota_from_target_files.py
+++ b/tools/releasetools/ota_from_target_files.py
@@ -108,6 +108,17 @@
       Generate a log file that shows the differences in the source and target
       builds for an incremental package. This option is only meaningful when
       -i is specified.
+
+  --payload_signer <signer>
+      Specify the signer when signing the payload and metadata for A/B OTAs.
+      By default (i.e. without this flag), it calls 'openssl pkeyutl' to sign
+      with the package private key. If the private key cannot be accessed
+      directly, a payload signer that knows how to do that should be specified.
+      The signer will be supplied with "-inkey <path_to_key>",
+      "-in <input_file>" and "-out <output_file>" parameters.
+
+  --payload_signer_args <args>
+      Specify the arguments needed for payload signer.
 """
 
 import sys
@@ -119,6 +130,7 @@
 import multiprocessing
 import os
 import subprocess
+import shlex
 import tempfile
 import zipfile
 
@@ -154,6 +166,8 @@
 OPTIONS.stash_threshold = 0.8
 OPTIONS.gen_verify = False
 OPTIONS.log_diff = None
+OPTIONS.payload_signer = None
+OPTIONS.payload_signer_args = []
 
 def MostPopularKey(d, default):
   """Given a dict, return the key corresponding to the largest
@@ -1155,17 +1169,19 @@
         "default_system_dev_certificate",
         "build/target/product/security/testkey")
 
-  # A/B updater expects key in RSA format.
-  cmd = ["openssl", "pkcs8",
-         "-in", OPTIONS.package_key + OPTIONS.private_key_suffix,
-         "-inform", "DER", "-nocrypt"]
-  rsa_key = common.MakeTempFile(prefix="key-", suffix=".key")
-  cmd.extend(["-out", rsa_key])
-  p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
-  p1.communicate()
-  assert p1.returncode == 0, "openssl pkcs8 failed"
+  # A/B updater expects a signing key in RSA format. Gets the key ready for
+  # later use in step 3, unless a payload_signer has been specified.
+  if OPTIONS.payload_signer is None:
+    cmd = ["openssl", "pkcs8",
+           "-in", OPTIONS.package_key + OPTIONS.private_key_suffix,
+           "-inform", "DER", "-nocrypt"]
+    rsa_key = common.MakeTempFile(prefix="key-", suffix=".key")
+    cmd.extend(["-out", rsa_key])
+    p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
+    p1.communicate()
+    assert p1.returncode == 0, "openssl pkcs8 failed"
 
-  # Stage the output zip package for signing.
+  # Stage the output zip package for package signing.
   temp_zip_file = tempfile.NamedTemporaryFile()
   output_zip = zipfile.ZipFile(temp_zip_file, "w",
                                compression=zipfile.ZIP_DEFLATED)
@@ -1222,21 +1238,29 @@
   signed_metadata_sig_file = common.MakeTempFile(prefix="signed-sig-",
                                                  suffix=".bin")
   # 3a. Sign the payload hash.
-  cmd = ["openssl", "pkeyutl", "-sign",
-         "-inkey", rsa_key,
-         "-pkeyopt", "digest:sha256",
-         "-in", payload_sig_file,
-         "-out", signed_payload_sig_file]
+  if OPTIONS.payload_signer is not None:
+    cmd = [OPTIONS.payload_signer]
+    cmd.extend(OPTIONS.payload_signer_args)
+  else:
+    cmd = ["openssl", "pkeyutl", "-sign",
+           "-inkey", rsa_key,
+           "-pkeyopt", "digest:sha256"]
+  cmd.extend(["-in", payload_sig_file,
+              "-out", signed_payload_sig_file])
   p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
   p1.communicate()
   assert p1.returncode == 0, "openssl sign payload failed"
 
   # 3b. Sign the metadata hash.
-  cmd = ["openssl", "pkeyutl", "-sign",
-         "-inkey", rsa_key,
-         "-pkeyopt", "digest:sha256",
-         "-in", metadata_sig_file,
-         "-out", signed_metadata_sig_file]
+  if OPTIONS.payload_signer is not None:
+    cmd = [OPTIONS.payload_signer]
+    cmd.extend(OPTIONS.payload_signer_args)
+  else:
+    cmd = ["openssl", "pkeyutl", "-sign",
+           "-inkey", rsa_key,
+           "-pkeyopt", "digest:sha256"]
+  cmd.extend(["-in", metadata_sig_file,
+              "-out", signed_metadata_sig_file])
   p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
   p1.communicate()
   assert p1.returncode == 0, "openssl sign metadata failed"
@@ -1896,6 +1920,10 @@
       OPTIONS.gen_verify = True
     elif o == "--log_diff":
       OPTIONS.log_diff = a
+    elif o == "--payload_signer":
+      OPTIONS.payload_signer = a
+    elif o == "--payload_signer_args":
+      OPTIONS.payload_signer_args = shlex.split(a)
     else:
       return False
     return True
@@ -1924,6 +1952,8 @@
                                  "stash_threshold=",
                                  "gen_verify",
                                  "log_diff=",
+                                 "payload_signer=",
+                                 "payload_signer_args=",
                              ], extra_option_handler=option_handler)
 
   if len(args) != 2:
diff --git a/tools/releasetools/sign_target_files_apks.py b/tools/releasetools/sign_target_files_apks.py
index f758ae0..3341f9f 100755
--- a/tools/releasetools/sign_target_files_apks.py
+++ b/tools/releasetools/sign_target_files_apks.py
@@ -51,10 +51,12 @@
       in which they appear on the command line.
 
   -o  (--replace_ota_keys)
-      Replace the certificate (public key) used by OTA package
-      verification with the one specified in the input target_files
-      zip (in the META/otakeys.txt file).  Key remapping (-k and -d)
-      is performed on this key.
+      Replace the certificate (public key) used by OTA package verification
+      with the ones specified in the input target_files zip (in the
+      META/otakeys.txt file). Key remapping (-k and -d) is performed on the
+      keys. For A/B devices, the payload verification key will be replaced
+      as well. If there're multiple OTA keys, only the first one will be used
+      for payload verification.
 
   -t  (--tag_changes)  <+tag>,<-tag>,...
       Comma-separated list of changes to make to the set of tags (in
@@ -63,6 +65,19 @@
       removed.  Changes are processed in the order they appear.
       Default value is "-test-keys,-dev-keys,+release-keys".
 
+  --replace_verity_private_key <key>
+      Replace the private key used for verity signing. It expects a filename
+      WITHOUT the extension (e.g. verity_key).
+
+  --replace_verity_public_key <key>
+      Replace the certificate (public key) used for verity verification. The
+      key file replaces the one at BOOT/RAMDISK/verity_key (or ROOT/verity_key
+      for devices using system_root_image). It expects the key filename WITH
+      the extension (e.g. verity_key.pub).
+
+  --replace_verity_keyid <path_to_X509_PEM_cert_file>
+      Replace the veritykeyid in BOOT/cmdline of input_target_file_zip
+      with keyid of the cert pointed by <path_to_X509_PEM_cert_file>.
 """
 
 import sys
@@ -92,6 +107,7 @@
 OPTIONS.replace_ota_keys = False
 OPTIONS.replace_verity_public_key = False
 OPTIONS.replace_verity_private_key = False
+OPTIONS.replace_verity_keyid = False
 OPTIONS.tag_changes = ("-test-keys", "-dev-keys", "+release-keys")
 
 def GetApkCerts(tf_zip):
@@ -171,7 +187,9 @@
                  for i in input_tf_zip.infolist()
                  if i.filename.endswith('.apk')])
   rebuild_recovery = False
+  system_root_image = misc_info.get("system_root_image") == "true"
 
+  # tmpdir will only be used to regenerate the recovery-from-boot patch.
   tmpdir = tempfile.mkdtemp()
   def write_to_temp(fn, attr, data):
     fn = os.path.join(tmpdir, fn)
@@ -196,25 +214,6 @@
     data = input_tf_zip.read(info.filename)
     out_info = copy.copy(info)
 
-    # Replace keys if requested.
-    if (info.filename == "META/misc_info.txt" and
-        OPTIONS.replace_verity_private_key):
-      ReplaceVerityPrivateKey(input_tf_zip, output_tf_zip, misc_info,
-                              OPTIONS.replace_verity_private_key[1])
-    elif (info.filename in ("BOOT/RAMDISK/verity_key",
-                            "BOOT/verity_key") and
-          OPTIONS.replace_verity_public_key):
-      new_data = ReplaceVerityPublicKey(output_tf_zip, info.filename,
-                                        OPTIONS.replace_verity_public_key[1])
-      write_to_temp(info.filename, info.external_attr, new_data)
-    # Copy BOOT/, RECOVERY/, META/, ROOT/ to rebuild recovery patch.
-    elif (info.filename.startswith("BOOT/") or
-          info.filename.startswith("RECOVERY/") or
-          info.filename.startswith("META/") or
-          info.filename.startswith("ROOT/") or
-          info.filename == "SYSTEM/etc/recovery-resource.dat"):
-      write_to_temp(info.filename, info.external_attr, data)
-
     # Sign APKs.
     if info.filename.endswith(".apk"):
       name = os.path.basename(info.filename)
@@ -228,6 +227,8 @@
         # an APK we're not supposed to sign.
         print "NOT signing: %s" % (name,)
         common.ZipWriteStr(output_tf_zip, out_info, data)
+
+    # System properties.
     elif info.filename in ("SYSTEM/build.prop",
                            "VENDOR/build.prop",
                            "BOOT/RAMDISK/default.prop",
@@ -238,34 +239,94 @@
       if info.filename in ("BOOT/RAMDISK/default.prop",
                            "RECOVERY/RAMDISK/default.prop"):
         write_to_temp(info.filename, info.external_attr, new_data)
+
     elif info.filename.endswith("mac_permissions.xml"):
       print "rewriting %s with new keys." % (info.filename,)
       new_data = ReplaceCerts(data)
       common.ZipWriteStr(output_tf_zip, out_info, new_data)
+
+    # Trigger a rebuild of the recovery patch if needed.
     elif info.filename in ("SYSTEM/recovery-from-boot.p",
                            "SYSTEM/etc/recovery.img",
                            "SYSTEM/bin/install-recovery.sh"):
       rebuild_recovery = True
+
+    # Don't copy OTA keys if we're replacing them.
     elif (OPTIONS.replace_ota_keys and
-          info.filename in ("RECOVERY/RAMDISK/res/keys",
-                            "SYSTEM/etc/security/otacerts.zip")):
-      # don't copy these files if we're regenerating them below
+          info.filename in (
+              "BOOT/RAMDISK/res/keys",
+              "RECOVERY/RAMDISK/res/keys",
+              "SYSTEM/etc/security/otacerts.zip",
+              "SYSTEM/etc/update_engine/update-payload-key.pub.pem")):
       pass
+
+    # Skip META/misc_info.txt if we will replace the verity private key later.
     elif (OPTIONS.replace_verity_private_key and
           info.filename == "META/misc_info.txt"):
       pass
+
+    # Skip verity public key if we will replace it.
     elif (OPTIONS.replace_verity_public_key and
           info.filename in ("BOOT/RAMDISK/verity_key",
-                            "BOOT/verity_key")):
+                            "ROOT/verity_key")):
       pass
+
+    # Skip verity keyid (for system_root_image use) if we will replace it.
+    elif (OPTIONS.replace_verity_keyid and
+          info.filename == "BOOT/cmdline"):
+      pass
+
+    # Copy BOOT/, RECOVERY/, META/, ROOT/ to rebuild recovery patch. This case
+    # must come AFTER other matching rules.
+    elif (info.filename.startswith("BOOT/") or
+          info.filename.startswith("RECOVERY/") or
+          info.filename.startswith("META/") or
+          info.filename.startswith("ROOT/") or
+          info.filename == "SYSTEM/etc/recovery-resource.dat"):
+      write_to_temp(info.filename, info.external_attr, data)
+      common.ZipWriteStr(output_tf_zip, out_info, data)
+
+    # A non-APK file; copy it verbatim.
     else:
-      # a non-APK file; copy it verbatim
       common.ZipWriteStr(output_tf_zip, out_info, data)
 
   if OPTIONS.replace_ota_keys:
     new_recovery_keys = ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info)
     if new_recovery_keys:
-      write_to_temp("RECOVERY/RAMDISK/res/keys", 0o755 << 16, new_recovery_keys)
+      if system_root_image:
+        recovery_keys_location = "BOOT/RAMDISK/res/keys"
+      else:
+        recovery_keys_location = "RECOVERY/RAMDISK/res/keys"
+      # The "new_recovery_keys" has been already written into the output_tf_zip
+      # while calling ReplaceOtaKeys(). We're just putting the same copy to
+      # tmpdir in case we need to regenerate the recovery-from-boot patch.
+      write_to_temp(recovery_keys_location, 0o755 << 16, new_recovery_keys)
+
+  # Replace the keyid string in META/misc_info.txt.
+  if OPTIONS.replace_verity_private_key:
+    ReplaceVerityPrivateKey(input_tf_zip, output_tf_zip, misc_info,
+                            OPTIONS.replace_verity_private_key[1])
+
+  if OPTIONS.replace_verity_public_key:
+    if system_root_image:
+      dest = "ROOT/verity_key"
+    else:
+      dest = "BOOT/RAMDISK/verity_key"
+    # We are replacing the one in boot image only, since the one under
+    # recovery won't ever be needed.
+    new_data = ReplaceVerityPublicKey(
+        output_tf_zip, dest, OPTIONS.replace_verity_public_key[1])
+    write_to_temp(dest, 0o755 << 16, new_data)
+
+  # Replace the keyid string in BOOT/cmdline.
+  if OPTIONS.replace_verity_keyid:
+    new_cmdline = ReplaceVerityKeyId(input_tf_zip, output_tf_zip,
+      OPTIONS.replace_verity_keyid[1])
+    # Writing the new cmdline to tmpdir is redundant as the bootimage
+    # gets build in the add_image_to_target_files and rebuild_recovery
+    # is not exercised while building the boot image for the A/B
+    # path
+    write_to_temp("BOOT/cmdline", 0o755 << 16, new_cmdline)
 
   if rebuild_recovery:
     recovery_img = common.GetBootableImage(
@@ -398,7 +459,8 @@
                            "build/target/product/security/testkey")
     mapped_keys.append(
         OPTIONS.key_map.get(devkey, devkey) + ".x509.pem")
-    print "META/otakeys.txt has no keys; using", mapped_keys[0]
+    print("META/otakeys.txt has no keys; using %s for OTA package"
+          " verification." % (mapped_keys[0],))
 
   # recovery uses a version of the key that has been slightly
   # predigested (by DumpPublicKey.java) and put in res/keys.
@@ -411,8 +473,13 @@
   new_recovery_keys, _ = p.communicate()
   if p.returncode != 0:
     raise common.ExternalError("failed to run dumpkeys")
-  common.ZipWriteStr(output_tf_zip, "RECOVERY/RAMDISK/res/keys",
-                     new_recovery_keys)
+
+  # system_root_image puts the recovery keys at BOOT/RAMDISK.
+  if misc_info.get("system_root_image") == "true":
+    recovery_keys_location = "BOOT/RAMDISK/res/keys"
+  else:
+    recovery_keys_location = "RECOVERY/RAMDISK/res/keys"
+  common.ZipWriteStr(output_tf_zip, recovery_keys_location, new_recovery_keys)
 
   # SystemUpdateActivity uses the x509.pem version of the keys, but
   # put into a zipfile system/etc/security/otacerts.zip.
@@ -426,8 +493,23 @@
   common.ZipWriteStr(output_tf_zip, "SYSTEM/etc/security/otacerts.zip",
                      temp_file.getvalue())
 
+  # For A/B devices, update the payload verification key.
+  if misc_info.get("ab_update") == "true":
+    # Unlike otacerts.zip that may contain multiple keys, we can only specify
+    # ONE payload verification key.
+    if len(mapped_keys) > 1:
+      print("\n  WARNING: Found more than one OTA keys; Using the first one"
+            " as payload verification key.\n\n")
+
+    print "Using %s for payload verification." % (mapped_keys[0],)
+    common.ZipWrite(
+        output_tf_zip,
+        mapped_keys[0],
+        arcname="SYSTEM/etc/update_engine/update-payload-key.pub.pem")
+
   return new_recovery_keys
 
+
 def ReplaceVerityPublicKey(targetfile_zip, filename, key_path):
   print "Replacing verity public key with %s" % key_path
   with open(key_path) as f:
@@ -435,6 +517,7 @@
   common.ZipWriteStr(targetfile_zip, filename, data)
   return data
 
+
 def ReplaceVerityPrivateKey(targetfile_input_zip, targetfile_output_zip,
                             misc_info, key_path):
   print "Replacing verity private key with %s" % key_path
@@ -444,6 +527,32 @@
   common.ZipWriteStr(targetfile_output_zip, "META/misc_info.txt", new_misc_info)
   misc_info["verity_key"] = key_path
 
+
+def ReplaceVerityKeyId(targetfile_input_zip, targetfile_output_zip, keypath):
+  in_cmdline = targetfile_input_zip.read("BOOT/cmdline")
+  # copy in_cmdline to output_zip if veritykeyid is not present in in_cmdline
+  if "veritykeyid" not in in_cmdline:
+    common.ZipWriteStr(targetfile_output_zip, "BOOT/cmdline", in_cmdline)
+    return in_cmdline
+  out_cmdline = []
+  for param in in_cmdline.split():
+    if "veritykeyid" in param:
+      # extract keyid using openssl command
+      p = common.Run(["openssl", "x509", "-in", keypath, "-text"], stdout=subprocess.PIPE)
+      keyid, stderr = p.communicate()
+      keyid = re.search(r'keyid:([0-9a-fA-F:]*)', keyid).group(1).replace(':', '').lower()
+      print "Replacing verity keyid with %s error=%s" % (keyid, stderr)
+      out_cmdline.append("veritykeyid=id:%s" % (keyid,))
+    else:
+      out_cmdline.append(param)
+
+  out_cmdline = ' '.join(out_cmdline)
+  out_cmdline = out_cmdline.strip()
+  print "out_cmdline %s" % (out_cmdline)
+  common.ZipWriteStr(targetfile_output_zip, "BOOT/cmdline", out_cmdline)
+  return out_cmdline
+
+
 def BuildKeyMap(misc_info, key_mapping_options):
   for s, d in key_mapping_options:
     if s is None:   # -d option
@@ -541,6 +650,8 @@
       OPTIONS.replace_verity_public_key = (True, a)
     elif o == "--replace_verity_private_key":
       OPTIONS.replace_verity_private_key = (True, a)
+    elif o == "--replace_verity_keyid":
+      OPTIONS.replace_verity_keyid = (True, a)
     else:
       return False
     return True
@@ -553,7 +664,8 @@
                                               "replace_ota_keys",
                                               "tag_changes=",
                                               "replace_verity_public_key=",
-                                              "replace_verity_private_key="],
+                                              "replace_verity_private_key=",
+                                              "replace_verity_keyid="],
                              extra_option_handler=option_handler)
 
   if len(args) != 2: