Add support for using custom signapk.jar.

Details:
* New --signapk_path, --extra_signapk_args, --java_path.
* New --public_key_suffix, --private_key_suffix so you can change the filenames.
* Fixes raising exceptions on error.

Change-Id: I0b7014b6d779d52ae896f95dfecb1bcccf536cf4
(cherry picked from commit a28acc6972ce35e9dfab061f175e229859d3e4db)
diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py
index 263ae11..58582ba 100644
--- a/tools/releasetools/common.py
+++ b/tools/releasetools/common.py
@@ -20,6 +20,7 @@
 import os
 import platform
 import re
+import shlex
 import shutil
 import subprocess
 import sys
@@ -40,6 +41,11 @@
 class Options(object): pass
 OPTIONS = Options()
 OPTIONS.search_path = "out/host/linux-x86"
+OPTIONS.signapk_path = "framework/signapk.jar"  # Relative to search_path
+OPTIONS.extra_signapk_args = []
+OPTIONS.java_path = "java"  # Use the one on the path by default.
+OPTIONS.public_key_suffix = ".x509.pem"
+OPTIONS.private_key_suffix = ".pk8"
 OPTIONS.verbose = False
 OPTIONS.tempfiles = []
 OPTIONS.device_specific = None
@@ -379,6 +385,7 @@
 
   no_passwords = []
   need_passwords = []
+  key_passwords = {}
   devnull = open("/dev/null", "w+b")
   for k in sorted(keylist):
     # We don't need a password for things that aren't really keys.
@@ -386,19 +393,36 @@
       no_passwords.append(k)
       continue
 
-    p = Run(["openssl", "pkcs8", "-in", k+".pk8",
+    p = Run(["openssl", "pkcs8", "-in", k+OPTIONS.private_key_suffix,
              "-inform", "DER", "-nocrypt"],
             stdin=devnull.fileno(),
             stdout=devnull.fileno(),
             stderr=subprocess.STDOUT)
     p.communicate()
     if p.returncode == 0:
+      # Definitely an unencrypted key.
       no_passwords.append(k)
     else:
-      need_passwords.append(k)
+      p = Run(["openssl", "pkcs8", "-in", k+OPTIONS.private_key_suffix,
+               "-inform", "DER", "-passin", "pass:"],
+              stdin=devnull.fileno(),
+              stdout=devnull.fileno(),
+              stderr=subprocess.PIPE)
+      stdout, stderr = p.communicate()
+      if p.returncode == 0:
+        # Encrypted key with empty string as password.
+        key_passwords[k] = ''
+      elif stderr.startswith('Error decrypting key'):
+        # Definitely encrypted key.
+        # It would have said "Error reading key" if it didn't parse correctly.
+        need_passwords.append(k)
+      else:
+        # Potentially, a type of key that openssl doesn't understand.
+        # We'll let the routines in signapk.jar handle it.
+        no_passwords.append(k)
   devnull.close()
 
-  key_passwords = PasswordManager().GetPasswords(need_passwords)
+  key_passwords.update(PasswordManager().GetPasswords(need_passwords))
   key_passwords.update(dict.fromkeys(no_passwords, None))
   return key_passwords
 
@@ -426,11 +450,13 @@
   else:
     sign_name = output_name
 
-  cmd = ["java", "-Xmx2048m", "-jar",
-           os.path.join(OPTIONS.search_path, "framework", "signapk.jar")]
+  cmd = [OPTIONS.java_path, "-Xmx2048m", "-jar",
+         os.path.join(OPTIONS.search_path, OPTIONS.signapk_path)]
+  cmd.extend(OPTIONS.extra_signapk_args)
   if whole_file:
     cmd.append("-w")
-  cmd.extend([key + ".x509.pem", key + ".pk8",
+  cmd.extend([key + OPTIONS.public_key_suffix,
+              key + OPTIONS.private_key_suffix,
               input_name, sign_name])
 
   p = Run(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
@@ -494,12 +520,14 @@
                  r'private_key="(.*)"$', line)
     if m:
       name, cert, privkey = m.groups()
+      public_key_suffix_len = len(OPTIONS.public_key_suffix)
+      private_key_suffix_len = len(OPTIONS.private_key_suffix)
       if cert in SPECIAL_CERT_STRINGS and not privkey:
         certmap[name] = cert
-      elif (cert.endswith(".x509.pem") and
-            privkey.endswith(".pk8") and
-            cert[:-9] == privkey[:-4]):
-        certmap[name] = cert[:-9]
+      elif (cert.endswith(OPTIONS.public_key_suffix) and
+            privkey.endswith(OPTIONS.private_key_suffix) and
+            cert[:-public_key_suffix_len] == privkey[:-private_key_suffix_len]):
+        certmap[name] = cert[:-public_key_suffix_len]
       else:
         raise ValueError("failed to parse line from apkcerts.txt:\n" + line)
   return certmap
@@ -543,8 +571,10 @@
   try:
     opts, args = getopt.getopt(
         argv, "hvp:s:x:" + extra_opts,
-        ["help", "verbose", "path=", "device_specific=", "extra="] +
-          list(extra_long_opts))
+        ["help", "verbose", "path=", "signapk_path=", "extra_signapk_args=",
+         "java_path=", "public_key_suffix=", "private_key_suffix=",
+         "device_specific=", "extra="] +
+        list(extra_long_opts))
   except getopt.GetoptError, err:
     Usage(docstring)
     print "**", str(err), "**"
@@ -560,6 +590,16 @@
       OPTIONS.verbose = True
     elif o in ("-p", "--path"):
       OPTIONS.search_path = a
+    elif o in ("--signapk_path",):
+      OPTIONS.signapk_path = a
+    elif o in ("--extra_signapk_args",):
+      OPTIONS.extra_signapk_args = shlex.split(a)
+    elif o in ("--java_path",):
+      OPTIONS.java_path = a
+    elif o in ("--public_key_suffix",):
+      OPTIONS.public_key_suffix = a
+    elif o in ("--private_key_suffix",):
+      OPTIONS.private_key_suffix = a
     elif o in ("-s", "--device_specific"):
       OPTIONS.device_specific = a
     elif o in ("-x", "--extra"):
diff --git a/tools/releasetools/sign_target_files_apks b/tools/releasetools/sign_target_files_apks
index eaad8a4..9fb1008 100755
--- a/tools/releasetools/sign_target_files_apks
+++ b/tools/releasetools/sign_target_files_apks
@@ -208,7 +208,7 @@
   try:
     keylist = input_tf_zip.read("META/otakeys.txt").split()
   except KeyError:
-    raise ExternalError("can't read META/otakeys.txt from input")
+    raise common.ExternalError("can't read META/otakeys.txt from input")
 
   extra_recovery_keys = misc_info.get("extra_recovery_keys", None)
   if extra_recovery_keys:
@@ -223,7 +223,7 @@
   for k in keylist:
     m = re.match(r"^(.*)\.x509\.pem$", k)
     if not m:
-      raise ExternalError("can't parse \"%s\" from META/otakeys.txt" % (k,))
+      raise common.ExternalError("can't parse \"%s\" from META/otakeys.txt" % (k,))
     k = m.group(1)
     mapped_keys.append(OPTIONS.key_map.get(k, k) + ".x509.pem")
 
@@ -247,7 +247,7 @@
                  stdout=subprocess.PIPE)
   data, _ = p.communicate()
   if p.returncode != 0:
-    raise ExternalError("failed to run dumpkeys")
+    raise common.ExternalError("failed to run dumpkeys")
   common.ZipWriteStr(output_tf_zip, "RECOVERY/RAMDISK/res/keys", data)
 
   # SystemUpdateActivity uses the x509.pem version of the keys, but