Merge "Improve concurrency for JAR entry inspection requests."
diff --git a/tools/apksigner/core/src/com/android/apksigner/core/DefaultApkSignerEngine.java b/tools/apksigner/core/src/com/android/apksigner/core/DefaultApkSignerEngine.java
index 30d4011..dae3c5e 100644
--- a/tools/apksigner/core/src/com/android/apksigner/core/DefaultApkSignerEngine.java
+++ b/tools/apksigner/core/src/com/android/apksigner/core/DefaultApkSignerEngine.java
@@ -28,6 +28,7 @@
 import java.io.IOException;
 import java.security.InvalidKeyException;
 import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
 import java.security.PrivateKey;
 import java.security.PublicKey;
 import java.security.SignatureException;
@@ -227,7 +228,7 @@
             GetJarEntryDataDigestRequest dataDigestRequest =
                     new GetJarEntryDataDigestRequest(
                             entryName,
-                            V1SchemeSigner.getMessageDigestInstance(mV1ContentDigestAlgorithm));
+                            V1SchemeSigner.getJcaMessageDigestAlgorithm(mV1ContentDigestAlgorithm));
             mOutputJarEntryDigestRequests.put(entryName, dataDigestRequest);
             mOutputJarEntryDigests.remove(entryName);
             return dataDigestRequest;
@@ -590,9 +591,9 @@
     private static class GetJarEntryDataRequest implements InspectJarEntryRequest {
         private final String mEntryName;
         private final Object mLock = new Object();
-        private final ByteArrayOutputStreamSink mBuf = new ByteArrayOutputStreamSink();
 
         private boolean mDone;
+        private ByteArrayOutputStreamSink mBuf;
 
         private GetJarEntryDataRequest(String entryName) {
             mEntryName = entryName;
@@ -607,6 +608,9 @@
         public DataSink getDataSink() {
             synchronized (mLock) {
                 checkNotDone();
+                if (mBuf == null) {
+                    mBuf = new ByteArrayOutputStreamSink();
+                }
                 return mBuf;
             }
         }
@@ -640,7 +644,7 @@
                 if (!mDone) {
                     throw new IllegalStateException("Not yet done");
                 }
-                return mBuf.getData();
+                return (mBuf != null) ? mBuf.getData() : new byte[0];
             }
         }
     }
@@ -650,17 +654,17 @@
      */
     private static class GetJarEntryDataDigestRequest implements InspectJarEntryRequest {
         private final String mEntryName;
-        private final MessageDigest mMessageDigest;
-        private final DataSink mDataSink;
+        private final String mJcaDigestAlgorithm;
         private final Object mLock = new Object();
 
         private boolean mDone;
+        private DataSink mDataSink;
+        private MessageDigest mMessageDigest;
         private byte[] mDigest;
 
-        private GetJarEntryDataDigestRequest(String entryName, MessageDigest digest) {
+        private GetJarEntryDataDigestRequest(String entryName, String jcaDigestAlgorithm) {
             mEntryName = entryName;
-            mMessageDigest = digest;
-            mDataSink = new MessageDigestSink(new MessageDigest[] {mMessageDigest});
+            mJcaDigestAlgorithm = jcaDigestAlgorithm;
         }
 
         @Override
@@ -672,10 +676,27 @@
         public DataSink getDataSink() {
             synchronized (mLock) {
                 checkNotDone();
+                if (mDataSink == null) {
+                    mDataSink = new MessageDigestSink(new MessageDigest[] {getMessageDigest()});
+                }
                 return mDataSink;
             }
         }
 
+        private MessageDigest getMessageDigest() {
+            synchronized (mLock) {
+                if (mMessageDigest == null) {
+                    try {
+                        mMessageDigest = MessageDigest.getInstance(mJcaDigestAlgorithm);
+                    } catch (NoSuchAlgorithmException e) {
+                        throw new RuntimeException(
+                                mJcaDigestAlgorithm + " MessageDigest not available", e);
+                    }
+                }
+                return mMessageDigest;
+            }
+        }
+
         @Override
         public void done() {
             synchronized (mLock) {
@@ -683,7 +704,9 @@
                     return;
                 }
                 mDone = true;
-                mDigest = mMessageDigest.digest();
+                mDigest = getMessageDigest().digest();
+                mMessageDigest = null;
+                mDataSink = null;
             }
         }
 
diff --git a/tools/apksigner/core/src/com/android/apksigner/core/internal/apk/v1/V1SchemeSigner.java b/tools/apksigner/core/src/com/android/apksigner/core/internal/apk/v1/V1SchemeSigner.java
index b99cdec..8b59b8e 100644
--- a/tools/apksigner/core/src/com/android/apksigner/core/internal/apk/v1/V1SchemeSigner.java
+++ b/tools/apksigner/core/src/com/android/apksigner/core/internal/apk/v1/V1SchemeSigner.java
@@ -169,6 +169,14 @@
     }
 
     /**
+     * Returns the JCA {@link MessageDigest} algorithm corresponding to the provided digest
+     * algorithm.
+     */
+    public static String getJcaMessageDigestAlgorithm(DigestAlgorithm digestAlgorithm) {
+        return digestAlgorithm.getJcaMessageDigestAlgorithm();
+    }
+
+    /**
      * Returns {@code true} if the provided JAR entry must be mentioned in signed JAR archive's
      * manifest.
      */