Merge "Update path to reverse_path.py"
diff --git a/core/tasks/cts.mk b/core/tasks/cts.mk
index d923c26..d0b077a 100644
--- a/core/tasks/cts.mk
+++ b/core/tasks/cts.mk
@@ -65,7 +65,9 @@
 
 CTS_TEST_JAR_LIST := \
 	cts-junit \
-	CtsJdwp
+	CtsJdwp \
+	cts-testng \
+	CtsLibcoreOj
 
 # Depend on the full package paths rather than the phony targets to avoid
 # rebuilding the packages every time.
diff --git a/tools/signapk/src/com/android/signapk/SignApk.java b/tools/signapk/src/com/android/signapk/SignApk.java
index d763be5..69f17e2 100644
--- a/tools/signapk/src/com/android/signapk/SignApk.java
+++ b/tools/signapk/src/com/android/signapk/SignApk.java
@@ -132,10 +132,15 @@
     private static final int MIN_API_LEVEL_FOR_SHA256_JAR_SIGNATURES = 18;
 
     /**
-     * Return one of USE_SHA1 or USE_SHA256 according to the signature
-     * algorithm specified in the cert.
+     * Returns the digest algorithm ID (one of {@code USE_SHA1} or {@code USE_SHA256}) to be used
+     * for v1 signing (using JAR Signature Scheme) an APK using the private key corresponding to the
+     * provided certificate.
+     *
+     * @param minSdkVersion minimum Android platform API Level supported by the APK (see
+     *        minSdkVersion attribute in AndroidManifest.xml). The higher the minSdkVersion, the
+     *        stronger hash may be used for signing the APK.
      */
-    private static int getDigestAlgorithm(X509Certificate cert, int minSdkVersion) {
+    private static int getV1DigestAlgorithmForApk(X509Certificate cert, int minSdkVersion) {
         String sigAlg = cert.getSigAlgName().toUpperCase(Locale.US);
         if ("SHA1WITHRSA".equals(sigAlg) || "MD5WITHRSA".equals(sigAlg)) {
             // see "HISTORICAL NOTE" above.
@@ -152,20 +157,50 @@
         }
     }
 
-    /** Returns the expected signature algorithm for this key type. */
-    private static String getSignatureAlgorithm(X509Certificate cert, int minSdkVersion) {
-        String keyType = cert.getPublicKey().getAlgorithm().toUpperCase(Locale.US);
-        if ("RSA".equalsIgnoreCase(keyType)) {
-            if ((minSdkVersion >= MIN_API_LEVEL_FOR_SHA256_JAR_SIGNATURES)
-                    || (getDigestAlgorithm(cert, minSdkVersion) == USE_SHA256)) {
-                return "SHA256withRSA";
-            } else {
-                return "SHA1withRSA";
-            }
-        } else if ("EC".equalsIgnoreCase(keyType)) {
-            return "SHA256withECDSA";
+    /**
+     * Returns the digest algorithm ID (one of {@code USE_SHA1} or {@code USE_SHA256}) to be used
+     * for signing an OTA update package using the private key corresponding to the provided
+     * certificate.
+     */
+    private static int getDigestAlgorithmForOta(X509Certificate cert) {
+        String sigAlg = cert.getSigAlgName().toUpperCase(Locale.US);
+        if ("SHA1WITHRSA".equals(sigAlg) || "MD5WITHRSA".equals(sigAlg)) {
+            // see "HISTORICAL NOTE" above.
+            return USE_SHA1;
+        } else if (sigAlg.startsWith("SHA256WITH")) {
+            return USE_SHA256;
         } else {
-            throw new IllegalArgumentException("unsupported key type: " + keyType);
+            throw new IllegalArgumentException("unsupported signature algorithm \"" + sigAlg +
+                                               "\" in cert [" + cert.getSubjectDN());
+        }
+    }
+
+    /**
+     * Returns the JCA {@link java.security.Signature} algorithm to be used for signing and OTA
+     * or v1 signing an APK using the private key corresponding to the provided certificate and the
+     * provided digest algorithm (see {@code USE_SHA1} and {@code USE_SHA256} constants).
+     */
+    private static String getJcaSignatureAlgorithmForV1SigningOrOta(
+            X509Certificate cert, int hash) {
+        String sigAlgDigestPrefix;
+        switch (hash) {
+            case USE_SHA1:
+                sigAlgDigestPrefix = "SHA1";
+                break;
+            case USE_SHA256:
+                sigAlgDigestPrefix = "SHA256";
+                break;
+            default:
+                throw new IllegalArgumentException("Unknown hash ID: " + hash);
+        }
+
+        String keyAlgorithm = cert.getPublicKey().getAlgorithm();
+        if ("RSA".equalsIgnoreCase(keyAlgorithm)) {
+            return sigAlgDigestPrefix + "withRSA";
+        } else if ("EC".equalsIgnoreCase(keyAlgorithm)) {
+            return sigAlgDigestPrefix + "withECDSA";
+        } else {
+            throw new IllegalArgumentException("Unsupported key algorithm: " + keyAlgorithm);
         }
     }
 
@@ -483,7 +518,7 @@
 
     /** Sign data and write the digital signature to 'out'. */
     private static void writeSignatureBlock(
-        CMSTypedData data, X509Certificate publicKey, PrivateKey privateKey, int minSdkVersion,
+        CMSTypedData data, X509Certificate publicKey, PrivateKey privateKey, int hash,
         OutputStream out)
         throws IOException,
                CertificateEncodingException,
@@ -495,7 +530,8 @@
 
         CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
         ContentSigner signer =
-                new JcaContentSignerBuilder(getSignatureAlgorithm(publicKey, minSdkVersion))
+                new JcaContentSignerBuilder(
+                        getJcaSignatureAlgorithmForV1SigningOrOta(publicKey, hash))
                         .build(privateKey);
         gen.addSignerInfoGenerator(
             new JcaSignerInfoGeneratorBuilder(
@@ -686,21 +722,21 @@
         private final File publicKeyFile;
         private final X509Certificate publicKey;
         private final PrivateKey privateKey;
+        private final int hash;
         private final long timestamp;
-        private final int minSdkVersion;
         private final OutputStream outputStream;
         private final ASN1ObjectIdentifier type;
         private WholeFileSignerOutputStream signer;
 
         public CMSSigner(JarFile inputJar, File publicKeyFile,
-                         X509Certificate publicKey, PrivateKey privateKey, long timestamp,
-                         int minSdkVersion, OutputStream outputStream) {
+                         X509Certificate publicKey, PrivateKey privateKey, int hash,
+                         long timestamp, OutputStream outputStream) {
             this.inputJar = inputJar;
             this.publicKeyFile = publicKeyFile;
             this.publicKey = publicKey;
             this.privateKey = privateKey;
+            this.hash = hash;
             this.timestamp = timestamp;
-            this.minSdkVersion = minSdkVersion;
             this.outputStream = outputStream;
             this.type = new ASN1ObjectIdentifier(CMSObjectIdentifiers.data.getId());
         }
@@ -725,8 +761,6 @@
                 signer = new WholeFileSignerOutputStream(out, outputStream);
                 JarOutputStream outputJar = new JarOutputStream(signer);
 
-                int hash = getDigestAlgorithm(publicKey, minSdkVersion);
-
                 Manifest manifest = addDigestsToManifest(inputJar, hash);
                 copyFiles(manifest, inputJar, outputJar, timestamp, 0);
                 addOtacert(outputJar, publicKeyFile, timestamp, manifest, hash);
@@ -734,8 +768,8 @@
                 signFile(manifest,
                          new X509Certificate[]{ publicKey },
                          new PrivateKey[]{ privateKey },
+                         new int[] { hash },
                          timestamp,
-                         minSdkVersion,
                          false, // Don't sign using APK Signature Scheme v2
                          outputJar);
 
@@ -753,7 +787,7 @@
                    CertificateEncodingException,
                    OperatorCreationException,
                    CMSException {
-            SignApk.writeSignatureBlock(this, publicKey, privateKey, minSdkVersion, temp);
+            SignApk.writeSignatureBlock(this, publicKey, privateKey, hash, temp);
         }
 
         public WholeFileSignerOutputStream getSigner() {
@@ -763,10 +797,10 @@
 
     private static void signWholeFile(JarFile inputJar, File publicKeyFile,
                                       X509Certificate publicKey, PrivateKey privateKey,
-                                      long timestamp, int minSdkVersion,
+                                      int hash, long timestamp,
                                       OutputStream outputStream) throws Exception {
         CMSSigner cmsOut = new CMSSigner(inputJar, publicKeyFile,
-                publicKey, privateKey, timestamp, minSdkVersion, outputStream);
+                publicKey, privateKey, hash, timestamp, outputStream);
 
         ByteArrayOutputStream temp = new ByteArrayOutputStream();
 
@@ -831,9 +865,8 @@
     }
 
     private static void signFile(Manifest manifest,
-                                 X509Certificate[] publicKey, PrivateKey[] privateKey,
+                                 X509Certificate[] publicKey, PrivateKey[] privateKey, int[] hash,
                                  long timestamp,
-                                 int minSdkVersion,
                                  boolean additionallySignedUsingAnApkSignatureScheme,
                                  JarOutputStream outputJar)
         throws Exception {
@@ -855,7 +888,7 @@
             writeSignatureFile(
                     manifest,
                     baos,
-                    getDigestAlgorithm(publicKey[k], minSdkVersion),
+                    hash[k],
                     additionallySignedUsingAnApkSignatureScheme);
             byte[] signedData = baos.toByteArray();
             outputJar.write(signedData);
@@ -868,7 +901,7 @@
             je.setTime(timestamp);
             outputJar.putNextEntry(je);
             writeSignatureBlock(new CMSProcessableByteArray(signedData),
-                                publicKey[k], privateKey[k], minSdkVersion, outputJar);
+                                publicKey[k], privateKey[k], hash[k], outputJar);
         }
     }
 
@@ -1075,7 +1108,6 @@
 
         JarFile inputJar = null;
         FileOutputStream outputFile = null;
-        int hashes = 0;
 
         try {
             File firstPublicKeyFile = new File(args[argstart+0]);
@@ -1085,7 +1117,6 @@
                 for (int i = 0; i < numKeys; ++i) {
                     int argNum = argstart + i*2;
                     publicKey[i] = readPublicKey(new File(args[argNum]));
-                    hashes |= getDigestAlgorithm(publicKey[i], minSdkVersion);
                 }
             } catch (IllegalArgumentException e) {
                 System.err.println(e);
@@ -1111,10 +1142,11 @@
             // NOTE: Signing currently recompresses any compressed entries using Deflate (default
             // compression level for OTA update files and maximum compession level for APKs).
             if (signWholeFile) {
-                SignApk.signWholeFile(inputJar, firstPublicKeyFile,
-                                      publicKey[0], privateKey[0],
-                                      timestamp, minSdkVersion,
-                                      outputFile);
+                int digestAlgorithm = getDigestAlgorithmForOta(publicKey[0]);
+                signWholeFile(inputJar, firstPublicKeyFile,
+                        publicKey[0], privateKey[0], digestAlgorithm,
+                        timestamp,
+                        outputFile);
             } else {
                 // Generate, in memory, an APK signed using standard JAR Signature Scheme.
                 ByteArrayOutputStream v1SignedApkBuf = new ByteArrayOutputStream();
@@ -1122,12 +1154,18 @@
                 // Use maximum compression for compressed entries because the APK lives forever on
                 // the system partition.
                 outputJar.setLevel(9);
-                Manifest manifest = addDigestsToManifest(inputJar, hashes);
+                int v1DigestAlgorithmBitSet = 0;
+                int[] v1DigestAlgorithm = new int[numKeys];
+                for (int i = 0; i < numKeys; ++i) {
+                    v1DigestAlgorithm[i] = getV1DigestAlgorithmForApk(publicKey[i], minSdkVersion);
+                    v1DigestAlgorithmBitSet |= v1DigestAlgorithm[i];
+                }
+                Manifest manifest = addDigestsToManifest(inputJar, v1DigestAlgorithmBitSet);
                 copyFiles(manifest, inputJar, outputJar, timestamp, alignment);
                 signFile(
                         manifest,
-                        publicKey, privateKey,
-                        timestamp, minSdkVersion, signUsingApkSignatureSchemeV2,
+                        publicKey, privateKey, v1DigestAlgorithm,
+                        timestamp, signUsingApkSignatureSchemeV2,
                         outputJar);
                 outputJar.close();
                 ByteBuffer v1SignedApk = ByteBuffer.wrap(v1SignedApkBuf.toByteArray());